feat: 主动建议系统(Proactive Suggestion)— 周期 1 - #8
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: beb47c24ec
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| case "open_memory_board": | ||
| case "open_skill_creator": | ||
| // TODO(Task 11+): UI 导航由 web 端处理;此处仅完成反馈记录(已 recordFeedback)。 | ||
| return; |
There was a problem hiding this comment.
Execute accepted todo and skill actions
When a user accepts a todo or skill suggestion, both the banner and ProactiveHub send only suggestion:act; neither renderer handles navigation separately. These branches therefore mark the record accepted and immediately return, so “把未完成任务记下来” creates no todo and a skill suggestion opens or creates nothing. Implement the promised action or return enough information for the renderer to perform it before removing the suggestion.
Useful? React with 👍 / 👎.
| case "open_automation_create": { | ||
| createAutomationJob({ | ||
| name: action.automationTitle, | ||
| schedule: { type: "manual" }, |
There was a problem hiding this comment.
Create an actual schedule for automation suggestions
When an automation or follow-up suggestion is accepted, this creates a manual job even though the suggestion promises a timed reminder or unattended recurring task. Manual jobs receive nextRunAt: null and are explicitly skipped by scheduleJob in automation-runner-service.ts, so the accepted automation never runs automatically. Open the scheduling flow or derive a valid once/interval/cron schedule instead of silently creating a manual-only job.
Useful? React with 👍 / 👎.
| export function getNeverKeys(): Set<string> { | ||
| const keys = new Set<string>(); | ||
| for (const r of listSuggestions()) { | ||
| if (r.status === "never") keys.add(r.duplicateKey); |
There was a problem hiding this comment.
Persist the kind-level mute promised by never feedback
When the user clicks “不再建议这类”, the never state contributes only this record's duplicateKey; isTypeSilenced separately considers only three consecutive ignored records. A different suggestion with the same kind therefore continues to appear, directly violating the explicit opt-out shown in both suggestion UIs. Persist and consult a muted-kind setting, or relabel the action as suppressing only this exact suggestion.
Useful? React with 👍 / 👎.
| feedback: SuggestionFeedback, | ||
| ): Promise<void> { | ||
| try { | ||
| recordFeedback(id, feedback); |
There was a problem hiding this comment.
Make feedback processing idempotent
If suggestion:act is retried or submitted twice for the same ID, there is no guard requiring the record to still be suggested: every request reapplies the type-weight multiplier and an accepted retry dispatches the action again, potentially creating duplicate automation jobs or memory candidates. Read and validate the prior status before recording feedback and dispatching the action so only the first transition is effective.
Useful? React with 👍 / 👎.
| const seenKeys = new Set<string>(); | ||
| let sessionSuggested = 0; | ||
| if (sessionKey) { | ||
| for (const r of listSuggestions("suggested")) { |
There was a problem hiding this comment.
Keep handled records in the session dedup set
After a suggestion is accepted or ignored, it disappears from listSuggestions("suggested"), so it no longer counts toward the two-per-session budget and its duplicate key is forgotten. Because every evaluation reprocesses the last 30 user messages, the same ignored signal can be proposed again on the very next completed run, while a long-lived thread can receive more than the documented session maximum. Build the session count and seenKeys from all records for that session, not only currently pending ones.
Useful? React with 👍 / 👎.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task 14: SuggestionBanner 组件。订阅 suggestionsVersionAtom,过滤 thread+workspace+24h 未过期记录,渲染 接受/忽略/不再建议 三态按钮,点击后调 actOnSuggestion 并显式重拉(覆盖 Task 12 feedback 不广播的 gap)。仿 AgentView.test.tsx fake-DOM 测试模式。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PUBLIC_RENDERER_SIDECAR_METHODS 漏配 7 个 suggestion RPC (list/act/stats/delete/clear-all/run-analysis/set-enabled), 导致生产桌面模式下 validateRendererSidecarMethod 抛 'unsupported renderer sidecar method',前端 list/act 等全部 失败。这是 Proma P0 教训的典型:链路看似接通但实际 IPC 被白名单拦截。electron-security.test.mjs 已覆盖(修复前失败)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
beb47c2 to
cd47394
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd4739473e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| try { | ||
| const neverKeys = getNeverKeys(); |
There was a problem hiding this comment.
Honor the global switch during manual analysis
When suggestion:set-enabled has set the global switch to false, this entry point still calls the analyst and persists its results because only evaluateSessionSuggestions checks getEnabled(). A subsequent suggestion:run-analysis therefore creates new suggestions while the suggestion system is disabled; short-circuit this path on the same setting.
Useful? React with 👍 / 👎.
| const filtered = candidates.filter( | ||
| (c) => !neverKeys.has(c.duplicateKey) && !suggestedKeys.has(c.duplicateKey), | ||
| ); |
There was a problem hiding this comment.
Recheck duplicate keys after the analyst finishes
The duplicate-key snapshot is taken before the potentially 60-second LLM call. If another analysis request or a session evaluation persists the same key while this request is awaiting runAnalysis, this stale set lets a second identical record through. Re-read the current suggested keys after the await, or enforce duplicate-key uniqueness atomically when persisting.
Useful? React with 👍 / 👎.
| // fail-open:automation 段省略 | ||
| } | ||
|
|
||
| return sections.length > 0 ? sections.join("\n") : "(暂无记忆)"; |
There was a problem hiding this comment.
Skip analysis when there is no supporting context
For a fresh user with no memory entries, correction rules, or automation jobs, this returns the non-empty sentinel (暂无记忆); runAnalysis only short-circuits empty context, so clicking “分析工作模式” still makes a paid model request and may wait up to 60 seconds despite having no evidence to analyze. Return an empty string here, or explicitly recognize the sentinel before invoking the provider.
Useful? React with 👍 / 👎.
合并 remote 6 个 commit 到本地 browser-annotation + abort 线: - 输入队列 followUpQueueMode 三态(shared/sidecar/web:blocked 重试/富引导/三态路由) - PR #8 主动建议 / #9 L3 Persona / #10 主动中心自动化 冲突仅 schemas.agent-attachments.test.ts(两边各加独立 describe 块,保留两块)。 合并后 sidecar typecheck 绿 + abort/schema 66 测试绿。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
主动建议系统(Proactive Suggestion)— 周期 1
为 Lume 增加完整的主动建议子系统,对齐 proma的 ProactiveAgent 能力。让 Agent 从「被动回答」进化到「对的时候提对的建议 + 越用越准」。
核心理念
主动性 = 用户接受率,不是建议次数。所有 LLM Recall 98%+ 但误报率 51-65%,因此误报控制是一等公民(阈值 / 预算 / 频率学习 / 静默)。
实现(1:1 移植 Proma 建议引擎,适配 Lume sidecar 架构)
apps/sidecar/src/services/suggest/:signals(6 类词典正则,零 LLM)/ rules(5 类规则 + skill 后处理)/ engine(误报控制:threshold 0.6 / 预算 1+2 / 拒绝门 / 去重四连)/ feedback(频率学习 ×1.2/×0.8/×0.5 + 连续忽略 3 次静默)/ analyst(LLM 工作模式发现 + 严格 schema)/ service / store(suggestions.json 原子持久化)/ adapter(thread transcript 提取)core-suggestion-hooks监听run.afterComplete(与 memory-hooks 平行,不改 lume-runner),fire-and-forgetsuggestion:changed推送(复用writeNotification)createAutomationJob9-link pipeline 端到端贯通
trigger → hook → service → engine → signals/rules → store → broadcaster → sidecar→web 推送 → UI → feedback。final review (opus) 逐跳验证无遗留 dead link。
⭐ 子代理实测发现并修复 1 个 P0 dead link
apps/desktop/src/renderer-sidecar-methods.ts的 RPC 白名单漏配 7 个 suggestion 通道——sidecar 单测全绿(不跑 desktop preload),但生产桌面模式下所有 suggestion RPC 抛unsupported静默失败。这正是 Proma 1409 的 P0 教训(「功能看似正常但真实链路从不执行」)的精确复现。已修复 +electron-security.test反射 guard 守门。验证
架构落点
全在
apps/sidecar(逻辑)+apps/web(UI),不依赖任何原生 API,无「必须放 desktop」约束。🤖 Generated with Claude Code