dsh-plan-completion 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ - insert:
2
+ - id: plan-completion
3
+ name: 'dsh-plan-completion'
4
+ config:
5
+ evidenceChars: 1500
6
+ maxTokensPatch: 400
7
+ maxTokensRewrite: 1200
8
+ warmBackfill: true
9
+ modelCalls: true # false: heuristic-only; no plugin model requests
10
+ # Debug endpoints (debug/diag/theme/experiment/autojob/steps/reset) stay off in production.
11
+ enableDebugRoutes: false
12
+ # Optional: set a local directory to persist debug logs / experiment data.
13
+ # debugDir: '<local debug directory>'
@@ -0,0 +1,106 @@
1
+ # 交付说明 · 会话计划补全插件(dsh-plan-completion)v0.1.0
2
+
3
+ > 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。当前未发布修复已接入鉴权、收紧模型路由及诊断数据,详见 [security.md](security.md) 和 [CHANGELOG](../CHANGELOG.md);旧记录中的“无鉴权”、原始诊断字段与独立评审不代表当前实现。
4
+
5
+ > 一期交付 · 2026-11-02 · 真机验收通过
6
+ > 设计与实现说明见 `README.md`;设计规格见 `docs/design-v1.2.md`
7
+ > 实验数据归档见 `docs/experiment-archive.md`
8
+
9
+ ## 一、它是什么
10
+
11
+ 在 DSH 输入框上方常驻的「计划补全」助手:
12
+
13
+ - 每次 AI 回合结束,自动在输入框上方生成一句**可直接发送的下一句建议**;按 `Tab` 填入输入框(只填入、不发送)。
14
+ - 左侧常驻 `▤ vX.Y` 入口,点开可看**隐式计划**:步骤列表、每步状态、证据与最近更新原因。
15
+ - 计划与建议都是后台由独立模型调用生成,**不进入主对话、不改变 Agent 的提示词/工具/行为**。
16
+
17
+ ## 二、交付物
18
+
19
+ | 路径 | 说明 |
20
+ |---|---|
21
+ | `dsh-plan-completion/index.js` | Host 半:事件监听、计划/建议生成、HTTP 路由、slash 命令、用户编辑通道 |
22
+ | `dsh-plan-completion/client.js` | Client 半:建议条 + 常驻入口 + 计划 Popover + Tab/快捷键 + 主题适配 |
23
+ | `dsh-plan-completion/host/*.js` | 纯函数模块:plan / prompt / evidence / heuristics / config / store / job / experiment |
24
+ | `dsh-plan-completion/test/` | 111 个单测(plan / prompt / heuristics / evidence / job / store / pipeline / Host 接线 / Client 生命周期 / 安全与隐私) |
25
+ | `dsh-plan-completion/cordis.patch.yml` | 插件装配行 + **生产配置** |
26
+ | `dsh-plan-completion/README.md` | 实现说明、触发链路、数据通道、配置、验证状态、已知限制 |
27
+ | `docs/experiment-archive.md` | 实验与验收数据摘要(仓库内);原始数据仅本地保留(`research/archive/…`) |
28
+
29
+ ## 三、安装 / 卸载
30
+
31
+ 已安装在当前 profile(bundle id `dsh-plan-completion`)。重装 / 卸载:
32
+
33
+ ```powershell
34
+ plugin_manager install_bundle target='<repo>'
35
+ plugin_manager remove_bundle target='dsh-plan-completion'
36
+ ```
37
+
38
+ - **Host 代码变更**:需要一次 profile 重组(`plugin_manager set_plugin` 开关一次,或重装)后生效。
39
+ - **Client 代码变更**:需要刷新页面(Ctrl+R)后生效;链接的本地 bundle 不会自动热更。
40
+
41
+ ## 四、使用
42
+
43
+ | 操作 | 效果 |
44
+ |---|---|
45
+ | `Tab`(输入框聚焦) | 把当前建议填入输入框(不发送) |
46
+ | `Alt+1..3` | 填入第 1~3 条备选建议 |
47
+ | `Esc` | 关闭当前建议(只留 `▤` 入口) |
48
+ | 点击 `▤ vX.Y` | 打开计划页(步骤 / 状态 / 证据 / 更新原因) |
49
+ | `Ctrl+Alt+P` | 快速打开/关闭计划页 |
50
+ | `/roadmap` | 在会话里查看当前计划(只读) |
51
+ | `/replan <方向>` | 带方向强制重写计划(用户编辑过计划后,这是唯一能整体重写的入口) |
52
+
53
+ ## 五、生产配置(已冻结)
54
+
55
+ `cordis.patch.yml`:
56
+
57
+ ```yaml
58
+ - insert:
59
+ - id: plan-completion
60
+ name: 'dsh-plan-completion'
61
+ config:
62
+ evidenceChars: 1500 # patch 路径证据字符预算
63
+ maxTokensPatch: 400 # patch 输出上限(实测只需 65~120)
64
+ maxTokensRewrite: 1200 # rewrite/冷启动输出上限(输出完整步骤表)
65
+ ```
66
+
67
+ 改配置 → 重组插件 → 看 Host 启动日志 `host-ready.settings` 确认生效。
68
+
69
+ ## 六、验证证据链
70
+
71
+ 1. **单测**:78 tests / 78 pass / 0 fail。
72
+ 2. **两轮隔离测试**(固定快照 + 固定计划;温度 0;每组 3 次;字符切片;跨模型盲评):
73
+ - 证据 800/2000/4000 字符 → 盲评 4.67/5 持平;输出 200/300/500 → 4.63/4.67/4.70;27/27 解析成功、可用率 100%。
74
+ - 结论:证据 800~1200 字符、输出 200~300 tokens 足够;增加上下文无收益。
75
+ 3. **6 条真机 auto-job 样本**:输出 75~113 tok(均值 88)、延迟 1.25~1.53s、建议 21~36 字、6/6 可用;计划无实质变化时不 bump。
76
+ 4. **配置生效**:`host-ready.config` 回显 1500/400/1200;force 调用 `maxTokens:1200`、patch 调用 `maxTokens:400`。
77
+ 5. **UI 验收**:浅色 / 暗色主题截图、建议胶囊与会话卡片左缘对齐、无建议时只剩 `▤`、新会话/切会话行为符合预期。
78
+ 6. **用户编辑通道**:标记 s4 done → `gen+1 / editedByUser=true / reason 记录`,之后模型只能 patch;整计划整理(分类)→ 7 步 `[交付]/[验证]/[冻结]/[收尾]/[二期]`。
79
+
80
+ ## 七、已知限制(v0.1.1 现状)
81
+
82
+ 1. **旧会话回填已支持**(v0.1.1):打开插件从未观察过的会话时,Client 会 POST `/plan-completion/warm`,Host 从持久化会话日志重建计划+建议(同一会话 10 分钟内只回填一次;需要一次模型调用,可用 `warmBackfill:false` 关闭)。不触发回填时仍只有 `▤` 入口。
83
+ 2. **计划不持久化**:进程内存保存,DSH 重启后计划丢失,下个回合结束重新生成(二期接 `storageDomain`)。
84
+ 3. **用户编辑锁**:`editedByUser=true` 后模型不能自动 rewrite 覆盖计划,只能 patch;需要整体重构用 `/replan`。
85
+ 4. **调试路由**:`experiment|autojob|steps|theme|diag|reset|debug` 仅在 `enableDebugRoutes:true` 时注册,且与其他路由一样必须通过宿主 `connection.requestRejection`(v0.1.1 起)。
86
+ 5. **无「核对进度」按钮**:计划页当前只展示状态/证据;按需模型核对按钮在二期清单(见下)。
87
+ 6. **模型路由**:v0.1.1 起只使用宿主当前默认 provider/model(不再自动改投其他供应商);`modelCalls:false` 可关闭插件全部模型调用。用户三选一设置页仍在二期。
88
+
89
+ ## 八、二期清单
90
+
91
+ - [ ] 计划持久化(`storageDomain` + 文件存储,重启不丢)
92
+ - [ ] 模型选择设置页(跟随会话默认 / 固定模型 / 自动挑非思考模型)
93
+ - [ ] 右栏计划页(`ctx.sidebarRightTabs.register`,替代/补充浮层)
94
+ - [x] 路由鉴权(`connection.requestRejection` / `connection.fetch`)——已在 v0.1.1 交付
95
+ - [ ] 「核对进度」按钮(按需一次调用,输出计划 vs 实际差异清单)
96
+ - [x] 挂载时回填(打开老会话时按需生成计划/建议)——已在 v0.1.1 交付
97
+ - [ ] 每日 token 预算闸(可选)
98
+
99
+ ## 九、排障
100
+
101
+ - 运行日志:`<debugDir>/plan-completion-runtime.jsonl`(Host 侧 debugLog;含 `host-ready` / `routes-registered` / `model-usage` / `client-diag` / `user-edit`)。
102
+ - 状态自检:`GET /plan-completion/state?sessionId=…`、`GET /plan-completion/debug`。
103
+ - 客户端自检:`POST /plan-completion/diag` 上报 `build / hasPill / hasEntry / entryText / suggestVisible / rowHeight` 与计算样式。
104
+ - 插件「没显示」排查顺序:① Ctrl+R 刷新页面 → ② 确认 profile 里插件 enabled → ③ `/plan-completion/debug` 看 `host-ready` 服务是否齐全 → ④ 看 `client-diag` 是否有 `hasPill/hasEntry`。
105
+
106
+
@@ -0,0 +1,387 @@
1
+ # 会话计划补全插件 · 设计计划 v1.2
2
+
3
+ > 状态:设计确认中,**暂不实现**(本文只冻结设计,不含代码)
4
+ > 前版:v1.0 / v1.1(保留不动)
5
+ > 本版变更:① 开头加"大白话版"给人看;② 模型改为**用户自主选择**(默认经济模型);③ 详细设计整体下移给 Agent 实现用
6
+
7
+ ---
8
+
9
+ # 〇、落地状态与计划分类(2026-11-02 更新)
10
+
11
+ 一期已实现并真机验收,按分类归档:
12
+
13
+ | 分类 | 内容 | 状态 |
14
+ |---|---|---|
15
+ | `[交付]` | 建议胶囊 + Tab 填入(不发送)+ 常驻 `▤ vX.Y` 入口 + 回合结束自动刷新 + 计划 Popover(全部步骤/证据/更新原因) | ✅ 已完成 |
16
+ | `[交付]` | 暗色主题适配 + 全场景视觉验收(胶囊/入口/无建议/新会话/切会话,截图为证) | ✅ 已完成 |
17
+ | `[验证]` | 两轮隔离测试(温度 0、每组 3 次、字符切片、跨模型盲评),结论:证据 800~1200 字符、输出 200~300 tokens 足够 | ✅ 已完成 |
18
+ | `[验证]` | 6 条真机 auto-job 样本与建议质量偏差记录(输出 75~113 tok、延迟 1.25~1.53s、6/6 可用) | ✅ 已完成 |
19
+ | `[冻结]` | 生产配置写入插件 `config`:`evidenceChars=1500`、`maxTokensPatch=400`、`maxTokensRewrite=1200`(Host 启动日志与模型调用日志双确认) | ✅ 已完成 |
20
+ | `[收尾]` | README / 设计文档 / 实验数据归档与交付说明 | ✅ 已完成(`dsh-plan-completion/交付说明.md`、`research/archive/2026-11-02-plan-completion/INDEX.md`) |
21
+ | `[二期]` | 计划持久化(storageDomain)、模型三选一设置页、右栏计划页、路由鉴权、按需「核对计划」按钮 | ⬜ 待办 |
22
+
23
+ 实现位置:`dsh-plan-completion/`(Host + Client + 78 单测),运行说明见 `dsh-plan-completion/README.md`。
24
+
25
+ ---
26
+
27
+ # 一、大白话版(给人看的)
28
+
29
+ ## 这是什么
30
+
31
+ 一个输入框小助手。它在后台默默记着"这个会话到底要干什么、现在做到哪一步了",然后每次 AI 答完,在输入框下面给你一句"下一句可以这么说",你按一下 Tab,这句话就进输入框,再按回车发出去。
32
+
33
+ ## 用起来是什么感觉
34
+
35
+ 1. 你开个新会话,说"给项目加个登录页"。
36
+ 2. 它自己在后台列一份 3~7 步的小计划(**不显示给你**,除非你点开看)。
37
+ 3. AI 干完这件活,输入框下面出现一行灰字:
38
+ `Tab 填入 ·「登录页先跑起来看看效果,再继续接 token 拦截器」`
39
+ 4. 按 Tab,这句话进输入框;想改就改,不想改直接回车发出去。
40
+ 5. 输入框右下角一直有个很淡的 `▤ v2.3`。点开能看到完整计划、手动打勾/改步骤,还能一键把"我改过的计划"变成一句话发回给 AI。
41
+
42
+ ## 它不会做的事(重要)
43
+
44
+ - **不会改 AI 的行为**,不会偷偷往提示词里塞东西;
45
+ - **不会往聊天记录里写任何东西**,聊天记录里看不到"计划"这两个字;
46
+ - **不会自动替你发消息**——Tab 只是填进输入框,发不发由你;
47
+ - **不会弹窗打扰你**,出错了就安静地不显示。
48
+
49
+ ## 花钱吗
50
+
51
+ - 默认有一个"便宜快"的模型专门干这件事,每回合最多问它一次,只传最近的口头对话摘要,回一句话。
52
+ - 你也可以**完全关掉模型**,只用免费规则(比如"上一步报错了 → 建议修报错""计划下一步没做 → 建议去做"),一样能用。
53
+ - 可以设每天最多问多少次,超了就自动只用免费规则。
54
+
55
+ ## 能用什么模型(你说了算)
56
+
57
+ - 三种方式任选:**跟随当前会话的模型** / **指定某个模型** / **自动挑最便宜的**。
58
+ - 默认是"指定一个经济模型",装的时候让你选一次,之后随时能改。
59
+ - 如果选中的是"会思考"的模型(R1、o1 这类),它会提醒"这个慢而且贵",并优先降到最低思考档,或干脆换回便宜的。
60
+ - 选不到可用模型时,自动退回免费规则,不会报错给你看。
61
+
62
+ ## 现在到哪一步了
63
+
64
+ 设计已经定稿(就是本文)。**还没写一行代码**。下一步是花十几分钟做几个只读验证——确认输入框能不能塞整段文字、计划存哪里、右栏面板能不能加——验证完再开始实现。
65
+
66
+ ---
67
+
68
+ > 下面从第二节开始是给 Agent 实现用的详细规格,人不用细读。
69
+
70
+ ---
71
+
72
+ # 二、模型选择(D12,本轮新增)
73
+
74
+ ## 2.1 三种模式
75
+
76
+ | 模式 | 行为 | 默认 |
77
+ |---|---|---|
78
+ | `follow` 跟随会话 | 取本会话最后一条 assistant 消息的 provider/model | — |
79
+ | `fixed` 指定模型 | 用户从"已配置的 provider/model 列表"里选一个 | **默认** |
80
+ | `auto` 自动经济 | 从可用列表里挑最便宜/最快的路由(规则可配) | — |
81
+
82
+ 首次安装:引导用户选一次模型(默认高亮最便宜的可用路由);不选就用 `auto` 兜底。
83
+
84
+ ## 2.2 可选项从哪来
85
+
86
+ - Host 半:`llm.listProviders()` → 已注册的 provider 路由;`llm.listModels(provider)` → 该路由下的模型。
87
+ - 这些列表通过插件自己的 Remote 端点暴露给设置页下拉框。
88
+ - 不可用/未授权的路由照样可以显示但标注"不可用";被选中时调用失败 → 熔断 5 分钟并退回启发式,不打扰用户。
89
+
90
+ ## 2.3 思考模型处理
91
+
92
+ - 调用前 `llm.resolveModelInfo(provider, model)`:
93
+ - 声明了 `reasoning` → 设最低 `reasoningEffort`;不支持设置则提示一次并建议换模型;
94
+ - 未声明 → 直接调用。
95
+ - `follow` 模式下如果会话模型是思考模型,同样走上面逻辑(这也是为什么默认不选 `follow`)。
96
+
97
+ ## 2.4 覆盖粒度
98
+
99
+ - v1:**全局一个模型设置**,所有会话共用。
100
+ - 不提供按会话覆盖(后续如需要再加,见 §8 N1)。
101
+
102
+ ---
103
+
104
+ # 三、详细设计(Agent 实现用)
105
+
106
+ ## 3.1 变更记录
107
+
108
+ | 版本 | 变更 |
109
+ |---|---|
110
+ | v1.0 | 初稿:计划台账 + 建议条 + Tab 填入 + 常驻入口 |
111
+ | v1.1 | 双截止线;固定经济模型;全局并发闸;watermark 校验;锚定 Popover;同步给 Agent;TTL GC;注入防护 |
112
+ | **v1.2** | 开头加大白话版;模型改为**用户三选一**(默认经济模型,D12);细节补充模型选择实现 |
113
+
114
+ ## 3.2 已确认的决策
115
+
116
+ | # | 决策 | 结论 |
117
+ |---|---|---|
118
+ | D1 | 计划给不给主模型看 | **不给**(纯观察者,不注入 system prompt、不加工具、不改 agent 行为) |
119
+ | D2 | 计划写不写会话日志 | **不写**(插件私有存储,避免污染 log / fork / 回放) |
120
+ | D3 | 建议出现时机 | **回合结束后立即出现**(目标 1~2s 内出条) |
121
+ | D4 | 计划条目粒度 | **3~7 个里程碑**,每条挂证据,不到 todo 级 |
122
+ | D5 | 版本方案 | 双段 `gen.rev`:rewrite → `gen+1.0`;patch → `gen.rev+1`;保留 5 版 |
123
+ | D6 | 重写触发 | **证据驱动**(阶段边界)+ 用户 `/replan` 强制;不是定时器 |
124
+ | D7 | 入口位置 | 不进工具行(权限模式是 single 槽、`+` 无扩展点);放 dock 区 |
125
+ | D8 | 入口可见性 | **常驻**(无建议时也在,做淡) |
126
+ | D9 | 模型只能"提议" | Host 校验后"提交";非法输出不落地,保留 last-good |
127
+ | D10 | 默认模型 | **指定经济模型**(用户可选,见 D12) |
128
+ | D11 | 计划页形态 | **v1 锚定 Popover,v2 右栏面板** |
129
+ | **D12** | **模型是否用户可选** | **是,三选一:跟随会话 / 指定模型 / 自动经济;默认指定经济模型** |
130
+
131
+ ## 3.3 系统分层
132
+
133
+ ```
134
+ ┌─ Host 半(每会话计划作业队列 + 全局并发闸)─────────────────┐
135
+ │ session/event(turn/end) ─┐ │
136
+ │ agent/turn-stopping(预热) ┤ │
137
+ │ /replan 命令 ────────────┴─► PlanJob(串行、可取消、幂等)│
138
+ │ → 只读收集证据(最近消息/工具结果/文件变更/todo/goal)│
139
+ │ → 1 次 ctx.llm.stream()(独立于 agent loop,模型见 §2)│
140
+ │ → 输出 { op, reason, steps, suggestion } │
141
+ │ → 校验(schema/diff/版本单调/防抖/注入过滤) │
142
+ │ → 原子写计划存储(key=sessionId) │
143
+ │ → wire view 发布 { 计划摘要, suggestion } │
144
+ └───────────────────────────┬────────────────────────────────┘
145
+ │ projection(只读)
146
+ ┌─ Client 半 ───────────────┴────────────────────────────────┐
147
+ │ conversation.composer.dock:建议条 + 常驻入口 ▤ v2.3 │
148
+ │ Tab → inputActions.captureInsertion() + insertText() │
149
+ │ 入口点开 → 计划 PAGE(shell.overlay 锚定 Popover) │
150
+ └────────────────────────────────────────────────────────────┘
151
+ ```
152
+
153
+ 边界:Host 不碰 agent,只订阅事件、读状态、调 `llm.stream`、写自己的存储。
154
+ `/replan` 会留下 `command/run` / `command/done` 两条日志事件(用户主动触发,可接受)。
155
+
156
+ ## 3.4 数据模型:Plan
157
+
158
+ ```jsonc
159
+ {
160
+ "sessionId": "s_7f3a",
161
+ "version": { "gen": 2, "rev": 3 },
162
+ "status": "active", // active | completed | archived
163
+ "intent": "给 Vue 项目加登录页,接入现有鉴权",
164
+ "steps": [
165
+ { "id": "s1", "title": "摸清现有路由与鉴权结构", "status": "done",
166
+ "evidence": ["read:src/router.ts"], "source": "model" },
167
+ { "id": "s2", "title": "新增 Login.vue 与路由", "status": "doing",
168
+ "evidence": ["write:src/views/Login.vue"], "source": "model" },
169
+ { "id": "s3", "title": "接入 token 存储与请求拦截器", "status": "todo", "source": "user" },
170
+ { "id": "s4", "title": "补测试并跑通", "status": "todo" }
171
+ ],
172
+ "updatedAt": 1762000000000,
173
+ "reason": "第2步完成(已创建 Login.vue)",
174
+ "revisionOf": { "gen": 2, "rev": 2 },
175
+ "watermark": { "turn": 7, "seq": 214 },
176
+ "editedByUser": false,
177
+ "stats": { "calls": 12, "tokensIn": 18321, "tokensOut": 2014 }
178
+ }
179
+ ```
180
+
181
+ 约束:`steps` 3~7 条;`status ∈ {todo,doing,done,dropped}`;`source ∈ {model,user}`;每次 bump 必带 `reason`;只保留最近 5 版;存储用 Host 私有存储域(key=sessionId,具体 API 待 P0 探针)。
182
+
183
+ ## 3.5 重写执行模型
184
+
185
+ ### 3.5.1 流水线
186
+
187
+ ```ts
188
+ onTurnStopping(session) { evidence.prewarm(session) } // 可选:只采集
189
+ onTurnEnd(session, ev) { enqueue({ sessionId, kind: 'turn', watermark: ev.seq }) }
190
+ onCommandReplan(session, instruction) {
191
+ enqueue({ sessionId, kind: 'force-rewrite', instruction })
192
+ return { kind: 'success', text: '计划重写已开始…' } // 不阻塞命令
193
+ }
194
+
195
+ async function run(job) {
196
+ if (queue.superseded(job)) return
197
+ await globalGate.acquire() // in-flight ≤ 2
198
+ try {
199
+ const ctx = await collectEvidence(job)
200
+ const out = await callModel(ctx, { deadlineMs: 6000 }) // 计划落盘截止线
201
+ if (!valid(out)) return keepLastGood(job)
202
+ const next = job.kind === 'force-rewrite' || out.op === 'rewrite'
203
+ ? rewritePlan(ctx.plan, out) : patchPlan(ctx.plan, out)
204
+ if (!commit(next, { cas: ctx.plan.version })) return
205
+ if (job.suggestionDeadlineMet(2500) && job.suggestionStillValid()) {
206
+ publish(ctx.plan, out.suggestion) // 建议展示截止线
207
+ }
208
+ } finally { globalGate.release() }
209
+ }
210
+ ```
211
+
212
+ ### 3.5.2 幂等、合并与一致性
213
+
214
+ - 幂等键 `(sessionId, turn)`,`turn/end.seq` 作 watermark;重连/重放不重复跑。
215
+ - 同会话串行;期间新事件只标脏,跑完后按最新状态再跑一次。
216
+ - **全局并发闸**:跨会话共享信号量 `in-flight ≤ 2`(可配)。
217
+ - **一致性校验**:提交前确认当前日志仍含 `plan.watermark`;失配 → 标 `stale`,**不自动回退历史版本**,下一次 `turn/end` 重建式 rewrite。
218
+ - 事实:DSH 会话日志 append-only,**无 rewind**;回退仅以 `sessions.fork({ atSeq })` 派生新会话形式存在。fork 子会话 seq 语义待 P0 探针。
219
+
220
+ ### 3.5.3 取消与失效
221
+
222
+ | 时机 | 计划更新 | 建议发布 |
223
+ |---|---|---|
224
+ | 新一轮开始 | 允许继续提交 | **丢弃** |
225
+ | 会话切换 / 入口关闭 | 继续 | 可发布,客户端按当前会话过滤 |
226
+ | 插件卸载 / 会话归档 | 取消 | 取消 |
227
+ | 用户 Esc 关闭建议条 | 不受影响 | dismissed,本回合不弹 |
228
+
229
+ ### 3.5.4 超时、失败与熔断
230
+
231
+ | 阶段 | 截止线 | 超时行为 |
232
+ |---|---|---|
233
+ | 建议展示 | 2.5s | 不弹建议;计划仍继续落盘 |
234
+ | 计划落盘 | 6s | 放弃,保留 last-good,静默 |
235
+ | 强制重写 | 8s | 返回"重写失败,已保留原计划"(用户主动触发,可见) |
236
+ | 模型不可用 | — | 熔断 5 分钟 + 退回启发式,不打扰 |
237
+ | JSON 非法 | — | 修复重发 1 次;仍失败静默 |
238
+ | 连续 3 次失败 | — | 熔断 5 分钟;入口小黄点 |
239
+
240
+ 铁律:任何失败都不打扰用户、不阻塞发送、不阻塞 `Tab`。
241
+
242
+ ### 3.5.5 模型路由与预算
243
+
244
+ - 模型选择见 §2(D12):`follow` / `fixed` / `auto`,默认 `fixed`=经济模型。
245
+ - 调用:`ctx.llm.stream()`,独立于 agent loop;不改 agent 请求、不加工具。
246
+ - 预算:每会话 ≤ 1 次/回合;每日上限可配(默认 300);全局 in-flight ≤ 2;超限只走启发式。
247
+ - 输入成本:patch ≈ 旧计划 + 本回合摘要(~1.5k tokens);rewrite ≈ 完整计划 + 最近 3 回合 + 首条消息(~4k);输出 < 300。token 用 `StreamChunk.usage` 实算。
248
+
249
+ ### 3.5.6 状态机
250
+
251
+ ```
252
+ 计划: absent ─seed─► ready ◄─patch/rewrite─ updating ─fail─► last-good
253
+ ready ─全部 done─► completed ──► archived(并开新计划)
254
+ ready ─议题剧变─► archived(reason=topic-drift)
255
+ 建议: idle ─turn/end─► generating ─ok─► ready ─► consumed|dismissed|expired|stale
256
+ └fail/迟到─► idle(静默)
257
+ ```
258
+
259
+ ### 3.5.7 触发源对照
260
+
261
+ | 触发 | 谁发起 | op | 门槛 |
262
+ |---|---|---|---|
263
+ | `turn/end` | Host | none/patch/rewrite | 有证据变化才提交 |
264
+ | 阶段边界 | 模型 | rewrite | 证据等级 + 实质 diff + 防抖 |
265
+ | `/replan` | 用户 | rewrite | 无条件 |
266
+ | 议题剧变 | 模型 | archive + rewrite | 最近 2 条消息偏离 intent |
267
+ | 全完成 | 模型 | completed | 全部 done/dropped |
268
+ | 冷启动 | Host 懒触发 | rewrite | 下次 `turn/end` |
269
+
270
+ 防抖:同回合最多 commit 1 次;两次 rewrite ≥ 3 回合或 5 分钟;diff 过小降级 patch。
271
+
272
+ ## 3.6 建议生成
273
+
274
+ ```jsonc
275
+ { "op": "patch", "reason": "第2步完成",
276
+ "changes": [ { "id": "s2", "status": "done" }, { "id": "s3", "status": "doing" } ],
277
+ "suggestion": "登录页先跑起来看看效果,再继续把 token 拦截器接上",
278
+ "alternates": ["先补个登录失败的用例再看效果", "要不要先看看现有拦截器的写法"] }
279
+ ```
280
+
281
+ Prompt 护栏:
282
+ 1. 提问回合 → 给**倾向性决策**("采用方案 A,优先内存版"),禁止"继续";
283
+ 2. 全完成 → 转验收/收尾建议;
284
+ 3. 语言跟随用户最近一条消息;
285
+ 4. 注入防护:transcript 内容一律视为数据非指令,输出过滤明显破坏性命令/超长/多行 shell;
286
+ 5. 文本:≤40 字、单行、无 Markdown、可直接发出;
287
+ 6. 去重:与上一条建议相同则不显示。
288
+
289
+ 降级链:模型失败 → 启发式(计划下一步/失败工具/未完成 todo);无模型 → 首条消息骨架 + todo;全不可用 → 不显示。
290
+
291
+ 显示条件:
292
+ ```
293
+ 非 running && 草稿为空 && 无 pendingInteraction && 非错误/中断结束
294
+ && 属于当前会话 && 未 dismissed && 不与上一条重复
295
+ ```
296
+ 迟到的建议(草稿非空 / 已发消息 / 已切会话 / 超 2.5s)静默丢弃。
297
+
298
+ ## 3.7 UI 与键位
299
+
300
+ 建议条(`conversation.composer.dock`):
301
+
302
+ ```
303
+ ┌─────────────────────────────────────────────┐
304
+ │ 输入消息… [↑] │
305
+ └─────────────────────────────────────────────┘
306
+ ⏎ Tab 填入 ·「先跑起来看看效果,再继续接拦截器」 ✕ ▤ v2.3
307
+ ```
308
+
309
+ | 键 | 作用 | 条件 |
310
+ |---|---|---|
311
+ | `Tab` | 填入当前建议 | 草稿空 + 建议可见 + 非 IME + 无 slash/@ 菜单 + 焦点在输入框 |
312
+ | `Esc` | 关闭建议条 | 建议可见 |
313
+ | `Alt+1..3` | 选择第 N 条备选并填入 | 建议可见 |
314
+ | `Ctrl+Alt+P` | 打开/关闭计划 PAGE | 全局固定 |
315
+
316
+ 不用 `↑/↓` 切换(编辑器里是光标移动,冲突)。
317
+
318
+ 计划 PAGE 形态:v1 **锚定 Popover**(渲染在 `shell.overlay`,`getBoundingClientRect()` 定位,无遮罩,点外部收起;不要在 dock 树内绝对定位——会被 overflow 裁剪);v2 右栏面板(待探针)。
319
+
320
+ 计划 PAGE 内容:目标 / 步骤(✓●○ + 证据)/ 更新原因 / 下一步建议 / 操作行
321
+ `[填入草稿] [同步给 Agent] [重新生成] [重写计划] [历史版本] [清空]`。
322
+ 「同步给 Agent」= 把计划拼成一句指令 `insertText` 填入草稿(不发送)。
323
+
324
+ ## 3.8 用户可自定义
325
+
326
+ | 设置 | 默认 | 说明 |
327
+ |---|---|---|
328
+ | 总开关 | 开 | 关掉不生成不显示 |
329
+ | 建议来源 | 启发式+模型 | 可只保留启发式 |
330
+ | **模型模式** | **指定模型(经济)** | `follow` / `fixed` / `auto`,见 §2 |
331
+ | **模型选择** | 首次引导 | 下拉选 provider/model;标注不可用 |
332
+ | Tab 行为 | 填入草稿 | 另一档直接发送 |
333
+ | 重写档位 | 平衡 | 保守/平衡/激进 |
334
+ | 建议条位置 | composer.dock | 另一档 input.overlay |
335
+ | 入口常驻样式 | `▤ v2.3` | 另一档仅图标 |
336
+ | fork 继承计划 | 是 | 待探针确认语义 |
337
+ | 版本保留数 | 5 | 1~20 |
338
+ | 每日调用上限 | 300 | 超出走启发式 |
339
+ | 全局并发 | 2 | in-flight 上限 |
340
+ | 预热 | 开 | P2 |
341
+ | 建议语言 | 跟随用户 | 跟随/中文/英文 |
342
+ | 危险建议过滤 | 开 | 拦截破坏性形态 |
343
+ | 数据保留 | 90 天 | TTL GC |
344
+ | 快捷键 | Ctrl+Alt+P | 可改 |
345
+
346
+ 计划可编辑:手动改状态/标题/增删 → `gen+1`、`reason="user edit"`、`source="user"`、`editedByUser=true`;用户版**高优先级**,模型只能 patch 不能自动 rewrite 覆盖。
347
+
348
+ 命令:`/roadmap`(只读展示)、`/replan [说明]`(强制重写)、`/roadmap clear`(清空)。
349
+
350
+ ## 3.9 待拍板 / 待探测
351
+
352
+ | # | 问题 | 状态 |
353
+ |---|---|---|
354
+ | Q1–Q8 | 前版遗留 | 全部关闭(Q2 在 v1.2 细化为 D12) |
355
+ | D12 | 模型是否用户可选 | **已定:三选一,默认指定经济模型** |
356
+ | N1 | 是否按会话覆盖模型 | v1 不做,全局一个设置 |
357
+ | N2 | fork 子会话 seq 语义 | **待 P0 探针** |
358
+ | N3 | `InputActions` 能否整段替换草稿 | **待 P0 探针** |
359
+ | N4 | `rightbar` tab 注册对插件是否开放 | **待 P0 探针** |
360
+ | N5 | 危险建议过滤严格度 | 待定,默认只拦明显破坏性命令 |
361
+
362
+ ## 3.10 落地阶段
363
+
364
+ | 阶段 | 内容 |
365
+ |---|---|
366
+ | **P0 探针** | `InputActions` 全量方法;`storageDomain` 用法;`rightbar` tab 注册;fork seq 语义 |
367
+ | **P1 最小可用** | 计划生成(patch/rewrite)+ 存储 + 建议条 + Tab 填入 + 常驻入口 Popover(启发式先跑通) |
368
+ | **P2 模型化** | `llm.stream` 接入 + 模型三选一设置页 + 双截止线 + 全局并发 + 预算熔断 + 思考模型兜底 + 预热 + 注入过滤 |
369
+ | **P3 计划 PAGE** | 完整交互、步骤编辑、同步给 Agent、历史回滚 |
370
+ | **P4 增强按钮(B 功能)** | 复用 LLM 管道做草稿增强 |
371
+
372
+ ## 3.11 风险清单
373
+
374
+ | 风险 | 缓解 |
375
+ |---|---|
376
+ | Tab 抢键 / IME | 三条件收紧 + 可改键 + 组合期放行 |
377
+ | 每回合调用成本 | 证据跳过 + 缓存 + 预算闸 + 全局并发 + 启发式兜底 |
378
+ | 思考模型慢/贵 | 默认经济模型 + reasoning 探测兜底 + 用户可换 |
379
+ | 计划漂移 | 版本 + reason + 回滚 + 用户编辑优先 |
380
+ | 模型输出非法 | 校验提交 + last-good + 修复重发 |
381
+ | 迟到建议 | 会话过滤 + 丢弃不补弹 |
382
+ | 间接提示注入 | 只填不发 + 标记 AI 建议 + 数据非指令 + 危险形态过滤 |
383
+ | 并发打爆限流 | 全局 in-flight ≤ 2 |
384
+ | 存储泄漏 | TTL GC;不得用 `session/disposed` 当删除信号 |
385
+ | 升级稳定性 | 只用公开 slot / standard props / Service;`inputActions` 是唯一需盯接口 |
386
+ | 隐私 | 只发最近摘要;模型选择与开关可见 |
387
+
@@ -0,0 +1,36 @@
1
+ # 归档索引 · 会话计划补全插件一期(2026-11-02)
2
+
3
+ 本目录归档「会话计划补全」插件一期的实验数据、验证证据与运行日志。
4
+
5
+ ## 文件
6
+
7
+ | 文件 | 内容 | 用途 |
8
+ |---|---|---|
9
+ | `plan-completion-experiment.json` | 第二轮隔离测试全量数据(27 组:证据字符 800/2000/4000 × 输出 200/300/500 × 3 次重复;温度 0;跨模型盲评) | 复核 token 预算结论 |
10
+ | `autojob-samples.json` | 6 条真机 auto-job 样本(两批各 3 条)+ 输出长度/延迟/质量偏差统计 + 用户编辑记录 | 复核生产配置(1500/400)表现 |
11
+ | `runtime-log.jsonl` | Host 运行日志(`host-ready`、路由注册、turn-end 触发、模型选择/用量、client-diag、用户编辑等) | 排查行为与配置是否生效 |
12
+ | `ui-final6-row.png` | 浅色主题最终 UI(建议胶囊 + `▤ v2.0`,对齐输入框卡片) | 视觉验收 |
13
+ | `ui-dark2-row.png` | 暗色主题 UI | 主题适配验收 |
14
+ | `ui-plan-v2-row.png` | 分类整理后的计划与建议(`▤ v2.0` + 收尾下一步建议) | 计划/建议链路验收 |
15
+
16
+ ## 关键结论(一句话版)
17
+
18
+ - **证据**:patch 路径 800~2000 字符足够,更多无收益;三档盲评 4.67/5(天花板效应),建议质量一致。
19
+ - **输出**:200~300 tokens 足够(实测 55~85 tok);500 无提升。
20
+ - **生产配置(已冻结)**:`evidenceChars=1500`、`maxTokensPatch=400`、`maxTokensRewrite=1200`,写入 `cordis.patch.yml` 的插件行 config。
21
+ - **真机表现**:自动 job 输出 75~113 tok、延迟 1.25~1.53s、6/6 建议可用;计划无实质变化时不 bump 版本。
22
+ - **已知偏差**:仅 1 条「任务已完成但模型以为仍在进行」的状态误判;judge 为同 provider 不同模型(本机无可用跨厂商凭证)。
23
+
24
+ ## 复现方式
25
+
26
+ ```powershell
27
+ # 1) 触发一次自动 job(真实 runGeneration patch 路径)
28
+ Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:4116/plan-completion/autojob' `
29
+ -ContentType 'application/json' -Body '{"sessionId":"<sessionId>"}'
30
+
31
+ # 2) 重跑隔离测试(后台异步,结果写入 research/plan-completion-experiment.json)
32
+ Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:4116/plan-completion/experiment' `
33
+ -ContentType 'application/json' -Body '{"sessionId":"<sessionId>","evidenceChars":[800,2000,4000],"maxTokens":[200,300,500],"reps":3}'
34
+ ```
35
+
36
+ > 注:`experiment` / `autojob` / `steps` 是为验证新增的调试路由,仅监听本机回环地址,二期会按鉴权方案收敛。
@@ -0,0 +1,73 @@
1
+ # 会话计划补全插件 · 二期计划
2
+
3
+ > 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。当前未发布修复已接入鉴权、收紧模型路由及诊断数据,详见 [security.md](security.md) 和 [CHANGELOG](../CHANGELOG.md);旧记录中的“无鉴权”、原始诊断字段与独立评审不代表当前实现。
4
+
5
+ > 版本:v2.0 计划草案 · 2026-11-02
6
+ > 依据:一期交付(见 `dsh-plan-completion/交付说明.md`)+ 本次真机反馈(常驻、计划页视觉、GitHub 发布评估)
7
+ > 目标:把「能用」的一期做成「稳、好看、可发布」的二期
8
+
9
+ ---
10
+
11
+ ## 一、二期范围与优先级
12
+
13
+ ### P0(必须,决定可用性与发布)
14
+
15
+ > 更新(v0.1.1):P0-1(挂载回填)、P0-2(Hero 常驻)、P0-4(路由鉴权)已交付;以下保留原始规划记录,未完成项为 P0-3 / P0-5 / P0-6。
16
+
17
+ | # | 事项 | 现状问题 | 方案 | 验收标准 | 预估 |
18
+ |---|---|---|---|---|---|
19
+ | P0-1 | ✅ **挂载时回填(旧会话也能有建议)** | 插件只处理「激活后观察到 turn/end」的会话;老会话打开只有裸入口 | client 挂载时若 `state.stale && !plan`,POST `/plan-completion/warm {sessionId}`;host 用 `session.snapshotEvents` 回填一次计划+建议(可在会话无新回合时工作) | 打开任意历史会话 ≤5s 出现建议;同一会话只回填一次(按 session+seq 去重) | ✅ 已交付(v0.1.1) |
20
+ | P0-2 | ✅ **真·常驻(hero / 新会话也有入口)** | `conversation.input.dock` 在无 Session 的 hero 页不渲染;删除全部消息后栏位消失 | 在 `shell.overlay` 增加一个随 composer 定位的轻量 `▤ 计划` 入口(hero 时显示;已有 dock 时隐藏避免重复);无计划时保留虚线提示胶囊(一期已做) | 新会话 hero、删除全部消息、旧会话、正常会话四种状态都有入口且不重叠 | ✅ 已交付(v0.1.1) |
21
+ | P0-3 | **计划持久化** | 进程内存,DSH 重启丢失 | `storageDomain` 持久化 plan(按 sessionId 分 key)+ 启动时恢复 + 版本号 `gen.rev` 保留 | 重启 DSH 后同一会话仍显示原计划与版本;schema 变更可迁移 | 1d |
22
+ | P0-4 | ✅ **路由鉴权** | `/plan-completion/*` 调试路由无鉴权(本机回环) | 使用 `connection.requestRejection` 校验来源、或改用 `connection.fetch` 同源校验;调试路由默认仅本机、可配置关闭 | 非本机/非 DSH 页面的请求被拒;客户端不受影响 | ✅ 已交付(v0.1.1) |
23
+ | P0-5 | **「核对进度」按钮** | 计划页只能看状态,无法对照实际进度 | 计划页底部加「核对进度」→ POST `/plan-completion/verify` → 一次模型调用生成「计划 vs 实际差异 + 建议修正」→ 展示在计划页(可折叠、可采纳) | 点击后 ≤5s 返回差异清单;采纳后生成 patch;失败有降级文案 | 1d |
24
+ | P0-6 | **发布工程化** | 无 git 仓库 / LICENSE / CI / 包元数据、README 含本地绝对路径 | 见 `发布评估-GitHub.md` 的 P0 清单 | 全部 P0 项 PASS,`node --test` 绿,仓库可公开 | 1d |
25
+
26
+ ### P1(体验增强)
27
+
28
+ | # | 事项 | 方案 | 验收 |
29
+ |---|---|---|---|
30
+ | P1-1 | 模型三选一设置页 | 跟随会话默认 / 固定模型 / 自动挑非思考模型;落插件 config + 设置页 | UI 可切换并即时生效,日志记录实际路由 |
31
+ | P1-2 | 右栏计划页 | `ctx.sidebarRightTabs.register` 提供常驻计划 tab,浮层保留为快捷入口 | 右栏可打开、与浮层状态一致 |
32
+ | P1-3 | 建议「依据」展示 | 保持一行;hover tooltip 显示 `plan.reason`;`suggestionLines: 1\|2` 作为配置项(默认 1) | 默认视觉不变,hover 可看依据 |
33
+ | P1-4 | 计划页编辑能力 | 状态点击切换(走 `/steps`)、步骤增删、`/replan` 入口按钮;`editedByUser` 语义保持 | 用户编辑后 gen+1、模型只 patch |
34
+ | P1-5 | 每日 token 预算闸 | 配置 `dailyTokenBudget`,超限降级启发式并提示 | 超预算时无模型调用、UI 有提示 |
35
+ | P1-6 | 单元测试补强 | client 纯逻辑(normalize/format/fill 决策)抽到可测模块;新增 shared-host 生命周期测试 | 测试 ≥120 且 CI 绿 |
36
+
37
+ ### P2(打磨)
38
+
39
+ - 计划导出 Markdown / 复制为 checklist;
40
+ - 建议历史(最近 5 条,可回填);
41
+ - 多语言补全(en 全文案);
42
+ - 图标与空态插画;
43
+ - 性能:轮询降频策略、页面隐藏时暂停;
44
+ - 失败重试策略统一(模型超时/空输出/非法 JSON 的退避)。
45
+
46
+ ---
47
+
48
+ ## 二、本轮已顺带修复的技术债(记录)
49
+
50
+ 1. **重复 handler 泄漏**:插件 toggle/hot reload 后旧模块的 HTTP 路由仍存活,导致同一路径出现新旧两个处理实例(`/state` 读旧、`/reset` 写新)。已改为 `globalThis` 共享状态 + 注册前 dispose 旧 disposers。
51
+ 2. **客户端不清空计划**:服务端返回 `plan:null` 时客户端仍保留旧计划,导致「计划/建议不消失」。已加 `handlePlanFetch → clearPlanResult`。
52
+ 3. **常驻兜底**:无计划时显示虚线提示胶囊 + `▤ 计划` 入口(已验证 `entryText=▤计划`、`suggestVisible=false`)。
53
+ 4. **计划页视觉 v2**:卡片式面板(16px 圆角、双层阴影、分区 header/body/footer、状态胶囊、建议高亮卡、底部操作栏),截图验证通过。
54
+ 5. **调试日志路径可配置**:`config.debugDir` / `DSH_PLAN_COMPLETION_DEBUG_DIR`,为空不写文件(为发布清路径做准备)。
55
+
56
+ ---
57
+
58
+ ## 三、里程碑
59
+
60
+ | 里程碑 | 内容 | 出口标准 |
61
+ |---|---|---|
62
+ | M2.0 | P0-1/2/3(回填、常驻、持久化) | 四类会话状态都有入口;重启不丢计划 |
63
+ | M2.1 | P0-4/5(鉴权、核对按钮) | 安全审计 + 核对可用 |
64
+ | M2.2 | P0-6 + P1-1/2(发布工程化、设置页、右栏) | GitHub 公开、CI 绿、文档齐 |
65
+ | M2.3 | P1-3/4/5/6(依据、编辑、预算、测试) | 体验与稳定性达标 |
66
+
67
+ ## 四、风险
68
+
69
+ - **回填成本**:打开旧会话触发一次模型调用;用「每个会话一次 + 预算闸」控制。
70
+ - **跨版本兼容**:持久化 schema 需版本字段与迁移器。
71
+ - **鉴权改动**:可能影响现有同源 fetch 契约(一期已验证的通道),需回归。
72
+ - **用户编辑锁**:编辑后模型不能 rewrite,需在 UI 明确「/replan 可整体重写」。
73
+