@orion-agents/orion-code 0.3.1 → 0.3.2

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +39 -28
  3. package/README.zh-CN.md +32 -25
  4. package/dist/commands/model-command-handlers.d.ts.map +1 -1
  5. package/dist/commands/model-command-handlers.js +59 -14
  6. package/dist/commands/model-command-handlers.js.map +1 -1
  7. package/dist/commands/types.d.ts +6 -0
  8. package/dist/commands/types.d.ts.map +1 -1
  9. package/dist/commands/types.js.map +1 -1
  10. package/dist/commands/workflow-command-handlers.d.ts +1 -1
  11. package/dist/commands/workflow-command-handlers.d.ts.map +1 -1
  12. package/dist/commands/workflow-command-handlers.js +42 -3
  13. package/dist/commands/workflow-command-handlers.js.map +1 -1
  14. package/dist/commands/workflow-commands.d.ts.map +1 -1
  15. package/dist/commands/workflow-commands.js +2 -2
  16. package/dist/commands/workflow-commands.js.map +1 -1
  17. package/dist/runtime/agent-runtime-controller.d.ts +16 -2
  18. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  19. package/dist/runtime/agent-runtime-controller.js +115 -6
  20. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  21. package/dist/runtime/agent-runtime-protocol.d.ts +3 -0
  22. package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
  23. package/dist/runtime/agent-runtime-protocol.js.map +1 -1
  24. package/dist/runtime/agent-runtime-runner.d.ts +8 -0
  25. package/dist/runtime/agent-runtime-runner.d.ts.map +1 -1
  26. package/dist/runtime/orion-session-runner.d.ts +4 -0
  27. package/dist/runtime/orion-session-runner.d.ts.map +1 -1
  28. package/dist/runtime/orion-session-runner.js +15 -0
  29. package/dist/runtime/orion-session-runner.js.map +1 -1
  30. package/dist/runtime/plan-review.d.ts +38 -0
  31. package/dist/runtime/plan-review.d.ts.map +1 -0
  32. package/dist/runtime/plan-review.js +173 -0
  33. package/dist/runtime/plan-review.js.map +1 -0
  34. package/dist/runtime/product-bootstrap.d.ts.map +1 -1
  35. package/dist/runtime/product-bootstrap.js +28 -6
  36. package/dist/runtime/product-bootstrap.js.map +1 -1
  37. package/dist/runtime/product-orion-runtime.d.ts.map +1 -1
  38. package/dist/runtime/product-orion-runtime.js +14 -35
  39. package/dist/runtime/product-orion-runtime.js.map +1 -1
  40. package/dist/runtime/protocol/runtime-protocol-v1.d.ts +73 -0
  41. package/dist/runtime/protocol/runtime-protocol-v1.d.ts.map +1 -1
  42. package/dist/runtime/protocol/runtime-protocol-v1.js +37 -0
  43. package/dist/runtime/protocol/runtime-protocol-v1.js.map +1 -1
  44. package/dist/runtime/release-receipts.d.ts +5 -3
  45. package/dist/runtime/release-receipts.d.ts.map +1 -1
  46. package/dist/runtime/release-receipts.js +42 -1
  47. package/dist/runtime/release-receipts.js.map +1 -1
  48. package/dist/runtime/session-composer-control.d.ts +136 -0
  49. package/dist/runtime/session-composer-control.d.ts.map +1 -0
  50. package/dist/runtime/session-composer-control.js +532 -0
  51. package/dist/runtime/session-composer-control.js.map +1 -0
  52. package/dist/runtime/thread-projection.d.ts +17 -0
  53. package/dist/runtime/thread-projection.d.ts.map +1 -1
  54. package/dist/runtime/thread-projection.js +87 -0
  55. package/dist/runtime/thread-projection.js.map +1 -1
  56. package/dist/runtime/ui-events.d.ts +6 -1
  57. package/dist/runtime/ui-events.d.ts.map +1 -1
  58. package/dist/runtime/ui-events.js.map +1 -1
  59. package/dist/services/session-storage.d.ts +36 -0
  60. package/dist/services/session-storage.d.ts.map +1 -1
  61. package/dist/services/session-storage.js +77 -1
  62. package/dist/services/session-storage.js.map +1 -1
  63. package/dist/web/event-hub.js +10 -0
  64. package/dist/web/event-hub.js.map +1 -1
  65. package/dist/web/file-read-service.d.ts +9 -0
  66. package/dist/web/file-read-service.d.ts.map +1 -1
  67. package/dist/web/file-read-service.js +17 -7
  68. package/dist/web/file-read-service.js.map +1 -1
  69. package/dist/web/git-read-model-service.d.ts +8 -0
  70. package/dist/web/git-read-model-service.d.ts.map +1 -1
  71. package/dist/web/git-read-model-service.js +28 -2
  72. package/dist/web/git-read-model-service.js.map +1 -1
  73. package/dist/web/protocol.d.ts +142 -0
  74. package/dist/web/protocol.d.ts.map +1 -1
  75. package/dist/web/protocol.js +207 -0
  76. package/dist/web/protocol.js.map +1 -1
  77. package/dist/web/server.d.ts.map +1 -1
  78. package/dist/web/server.js +45 -8
  79. package/dist/web/server.js.map +1 -1
  80. package/dist/web/terminal-manager.d.ts +1 -0
  81. package/dist/web/terminal-manager.d.ts.map +1 -1
  82. package/dist/web/terminal-manager.js +41 -9
  83. package/dist/web/terminal-manager.js.map +1 -1
  84. package/dist/web/workbench-controller.d.ts +26 -9
  85. package/dist/web/workbench-controller.d.ts.map +1 -1
  86. package/dist/web/workbench-controller.js +545 -29
  87. package/dist/web/workbench-controller.js.map +1 -1
  88. package/dist/web-client/assets/{DiffViewer-DpKoD07C.js → DiffViewer-eaQNsnCh.js} +1 -1
  89. package/dist/web-client/assets/FilesPanel-DYs1MhoM.js +2 -0
  90. package/dist/web-client/assets/{GitPanel-D20StcdG.js → GitPanel-CnU4XVr7.js} +1 -1
  91. package/dist/web-client/assets/{ReviewPanel-3MVWbMQV.js → ReviewPanel-BDxG670T.js} +1 -1
  92. package/dist/web-client/assets/{TerminalPanel-BLQ592Fb.js → TerminalPanel-Q5mSSsoK.js} +15 -13
  93. package/dist/web-client/assets/index-BWV5sIvw.css +1 -0
  94. package/dist/web-client/assets/index-VVUKj6FE.js +17 -0
  95. package/dist/web-client/index.html +2 -2
  96. package/docs/architecture/agent-mode-permission-contract.md +87 -0
  97. package/docs/architecture/v0.3.1-web-api.yaml +53 -5
  98. package/docs/architecture/v0.3.2-web-api.yaml +3029 -0
  99. package/docs/migration/v0.3.1-to-v0.3.2.md +94 -0
  100. package/docs/plan/v0.3.1-web-workbench-professional-shell-plan.md +1 -1
  101. package/docs/plan/v0.3.2-web-workbench-layout-and-composer-plan.md +869 -0
  102. package/docs/readme.md +12 -2
  103. package/docs/test/v0.3.1-web-workbench-e2e-plan.md +1 -1
  104. package/docs/test/v0.3.2-web-workbench-e2e-plan.md +100 -0
  105. package/npm-shrinkwrap.json +2 -2
  106. package/package.json +6 -1
  107. package/dist/web-client/assets/FilesPanel-BkoLmgjR.js +0 -2
  108. package/dist/web-client/assets/index-BkIDl_xk.js +0 -16
  109. package/dist/web-client/assets/index-DXMpnWE8.css +0 -1
@@ -0,0 +1,869 @@
1
+ # Orion Code v0.3.2 Web Workbench 布局与 Composer Control Center 开发计划
2
+
3
+ > 版本:v0.3.2
4
+ > 状态:Proposed / 待评审
5
+ > 日期:2026-08-30
6
+ > 基线:`v0.3.1` 分支,`2274639bfc9b50e17cf7fbcd6fa6d1846fd18515`
7
+ > 交付类型:Web Workbench 产品增量,不改变 Orion Code 的本地优先和单活动 Runtime 边界
8
+
9
+ ## 0. 决策摘要
10
+
11
+ v0.3.2 把 v0.3.1 的“专业三栏工作台”继续向可控、可解释的 Agent 工作台推进,核心交付为:
12
+
13
+ 1. 左侧项目导航升级为可折叠的 48px rail 与 `240–480px` 可变宽面板;宽屏支持鼠标/触控板直接拖动,窄屏自动成为抽屉;
14
+ 2. Composer 底部改造成统一 Control Center:工作模式、权限、模型/推理强度、Context 和发送操作集中在一行;
15
+ 3. `BUILD / PLAN / AUTO` 从三枚并排按钮改为带说明和状态的下拉菜单;
16
+ 4. 新增会话级权限下拉框,清楚显示 `Ask / Allow / Deny`、有效来源和 AUTO 的覆盖关系;
17
+ 5. 新增会话级模型与推理强度切换,复用 Runtime 的事务式 ModelCoordinator,不修改项目默认模型;
18
+ 6. 新增精确 Context 占用环与详情弹层,区分 provider 计数和估算值;
19
+ 7. 纳入四项对标优化:结构化 Context 引用、Plan 审核闸门、可编辑队列、会话草稿恢复;
20
+ 8. 所有控制 mutation 使用 requestId、workspace/session/context/control revision,失败不产生跨项目或半提交副作用;
21
+ 9. 以真实安装包、真实 Chrome、真实 Runtime/ToolGateway 和确定性双模型 provider 完成 E2E,不能以静态截图代替行为证明。
22
+
23
+ 本计划只定义 v0.3.2 的产品、架构和验收合同。版本号、分支、源码、OpenAPI、测试与发布配置在计划评审通过后分阶段实施。
24
+
25
+ ## 1. 输入、对标与借鉴边界
26
+
27
+ ### 1.1 Orion v0.3.1 真实基线
28
+
29
+ 本计划建立在当前源码上,而不是从设计稿重新想象:
30
+
31
+ - `web/src/App.tsx` 已支持左栏整体展开/隐藏以及右侧工作面板偏好;
32
+ - `web/src/state/layout-preferences.ts` 已有右栏 `320–720px` clamp、持久化和中栏最小宽度约束;
33
+ - `web/src/components/Conversation.tsx` 仍用三枚按钮展示 BUILD/PLAN/AUTO;
34
+ - 当前 Composer 没有会话级权限、模型和 Context 控制;
35
+ - `src/runtime/model-coordinator.ts` 已提供模型 prepare/commit/rollback,以及切换到小窗口模型前的 compact preflight;
36
+ - `src/services/model-context.ts` 已提供 `ContextUsageSnapshot`,包含 used、window、safe budget、阈值、来源和 auto-compact 状态;
37
+ - `docs/architecture/agent-mode-permission-contract.md` 已冻结“工作模式与权限是两个独立轴”;
38
+ - `docs/architecture/v0.3.1-web-api.yaml` 已有 context/session CAS、幂等命令与恢复快照,可作为 v0.3.2 扩展基础。
39
+
40
+ ### 1.2 用户提供的 Codex 视觉参考
41
+
42
+ 用户提供的截图只作为交互层级参考,不是隐藏需求或可复制资产。可借鉴点是:
43
+
44
+ - Composer 是一个连续卡片,输入区和控制行属于同一视觉容器;
45
+ - 左侧是“添加 Context/附件”和权限状态;
46
+ - 右侧是模型/推理强度、语音入口和发送;
47
+ - 当前选择以短标签显示,详细解释放在弹层,而不是把所有选项平铺在主界面。
48
+
49
+ Orion 不复制 Codex 的商标、图标、颜色、文案或未公开协议。截图中的麦克风不自动进入 v0.3.2;没有可靠语音输入链路前不展示无效按钮。
50
+
51
+ ### 1.3 DeepSeek Harness 固定源码参考
52
+
53
+ 参考仓库:`/Users/hope/ai-project/deepseek-harness`,固定提交 `b150a55`。
54
+
55
+ 已核验并可借鉴:
56
+
57
+ - `packages/client/ui-layout/src/client/AppFrame.tsx`:左右 splitter 使用 pointer capture 和 rAF 节流;
58
+ - `packages/client/ui-layout/src/client/columns.ts`:左栏 56px rail、264–420px 拖动和中栏保护的 concession chain;
59
+ - `packages/client/ui-conversation/src/client/skeleton/InputBar.tsx`:权限、Plan、模型、Context 与发送处于同一工具行;
60
+ - `packages/client/ui-conversation/src/client/skeleton/PermissionSelect.tsx`:权限菜单和高风险确认;
61
+ - `packages/client/ui-model-selection/src/client/ModelSelect.tsx`:模型与 effort 二级菜单、Host 目录、失败保留 last-good;
62
+ - `packages/client/ui-conversation/src/client/skeleton/ContextMeter.tsx`:Context 占用环与详情弹层;
63
+ - `packages/client/ui-user-questions/src/client/PlanReviewPanel.tsx`:Plan 批准、继续规划和反馈的交互分离;
64
+ - `apps/web/tests/reference-composer.e2e.ts`、`queue-actions.e2e.ts`:结构化引用和可编辑队列的真实浏览器合同。
65
+
66
+ Orion 不照搬 DSH 的 Cordis 插件、两条下行 WebSocket 或权限 preset。Orion 继续使用自己的 React shell、POST control、可重放 SSE、ToolGateway 和 Ask/Allow/Deny 语义。
67
+
68
+ ### 1.4 WorkBuddy 产品参考
69
+
70
+ 本机只读核验 `/Applications/WorkBuddy.app` v5.3.14。其闭源 bundle 只能作为产品观察,不能作为源码权威或复制来源。可借鉴的原则是:
71
+
72
+ - Chat input 将 mode、permission、model、expert/skill、mention、command、attachment 作为可组合 provider;
73
+ - Ask/Craft/Expert 等工作姿态在输入区可见,而详细能力由 Runtime/模板承载;
74
+ - Skills、专家、连接器和自动化不是散落设置,而是任务 Context 的可选择来源;
75
+ - 长任务以明确状态、历史和可恢复控制呈现。
76
+
77
+ v0.3.2 只借鉴“可组合输入控制面”和“Context 来源可见”这两个原则,不引入 WorkBuddy 自动化平台、专家市场或云端账号体系。
78
+
79
+ ## 2. 问题定义
80
+
81
+ ### 2.1 左侧导航仍是固定宽度
82
+
83
+ v0.3.1 已能隐藏左栏,但展开宽度固定为 280px:
84
+
85
+ - 长项目名、分支、Session 标题容易截断;
86
+ - 小屏笔记本上又会占用过多中栏;
87
+ - 左右面板的交互不一致;
88
+ - 折叠后完全消失,缺少 IDE 常见的常驻 rail 和恢复位置。
89
+
90
+ ### 2.2 Composer 控制分散且缺乏解释
91
+
92
+ 当前模式在 Composer 中平铺,权限只在 header/Settings 显示,模型切换依赖 `/model`,Context 只在诊断页展示。用户在发送前无法快速回答:
93
+
94
+ - 这一条请求会以 BUILD、PLAN 还是 AUTO 运行?
95
+ - 当前工具策略来自 Session、Project 还是 AUTO?
96
+ - 下一轮实际使用哪个模型和 effort?
97
+ - Context 已使用多少,是否即将 compact?
98
+ - 当前草稿附带了哪些文件、Diff、Skill 或历史 Session Context?
99
+
100
+ ### 2.3 PLAN 仍缺少真正的人机审核节点
101
+
102
+ 模式选择如果只改变提示词,而生成 Plan 后继续自动进入执行,会让“PLAN”在用户心智上不可信。对标 Codex/DSH 后,v0.3.2 应让 Plan 成为可审核的 durable state,而不是一段普通回复。
103
+
104
+ ### 2.4 长任务控制仍不完整
105
+
106
+ 当前已经有 queue、Steer、Goal 和工具活动,但还缺:
107
+
108
+ - 编辑或重新排序排队消息;
109
+ - 刷新后的草稿恢复;
110
+ - 结构化 Context 的增删和 token 成本可见;
111
+ - 控制切换失败后的 last-good 与重试路径。
112
+
113
+ ## 3. 目标、指标与非目标
114
+
115
+ ### 3.1 P0 目标
116
+
117
+ - 左栏可以展开、折叠、鼠标/触控板拖动,桌面偏好刷新后恢复;
118
+ - 左栏拖动范围 `240–480px`,默认 `280px`,折叠 rail `48px`;
119
+ - 中栏在正常桌面布局保持至少 `560px`;不足时先收缩/折叠右栏,再进入抽屉布局;
120
+ - 模式、权限、模型/effort 和 Context 都能在 Composer 控制行查看和操作;
121
+ - 模型切换复用事务式 Runtime 路径,小上下文切换需要 compact preflight;失败恢复原模型;
122
+ - Context 百分比和 token 数来自 Host typed projection,不由浏览器分析 transcript 猜测;
123
+ - 所有操作在活动会话、workspace 和 context revision 不匹配时 409 且零副作用;
124
+ - 320、390、760、1024、1180、1440px 均无页面级横向滚动;
125
+ - 真实 E2E 的 console error、page error、HTTP 5xx、secret finding、dropped event 均为 0。
126
+
127
+ ### 3.2 同版本必交付的优化项
128
+
129
+ 以下不是未来愿望,而是 v0.3.2 的 Required scope:
130
+
131
+ 1. 结构化 Context launcher 与可移除 Context tray;
132
+ 2. durable Plan review gate;
133
+ 3. queue 编辑、删除和上下移动;
134
+ 4. 同一浏览器标签页内的 Session 草稿恢复;
135
+ 5. Composer 控制失败的 last-good、pending、retry 和 rollback 反馈;
136
+ 6. 命令、Context 与能力入口的统一 `+` 菜单。
137
+
138
+ ### 3.3 非目标
139
+
140
+ v0.3.2 不包含:
141
+
142
+ - 语音输入或语音转写;
143
+ - 任意本机文件上传、workspace 外文件引用;
144
+ - 多模型并行比较或自动路由;
145
+ - 多 Agent Runtime 同时运行;
146
+ - WorkBuddy 式自动化、专家市场或云同步;
147
+ - 自由停靠、任意拆分的 IDE 布局;
148
+ - 浏览器端编辑文件、stage/commit/push;
149
+ - 把 `Allow` 重命名为“完全访问”。Orion 的 Allow 仍受 workspace containment、硬拒绝、sandbox 和 capability policy 约束;
150
+ - 把 PLAN 当作安全模式。PLAN 与 BUILD 使用同一工具注册表和权限轴;
151
+ - 把 UI Context breakdown 通过分析 transcript 或工具文案伪造出来。
152
+
153
+ ## 4. 目标信息架构
154
+
155
+ ### 4.1 桌面布局
156
+
157
+ ```text
158
+ ┌────┬───────────────────────┬─┬──────────────────────────────────────┬─┬──────────────┐
159
+ │rail│ Project / Session nav │││ Conversation ││ Work panel │
160
+ │48px│ 240–480px │││ min 560px ││ 320–720px │
161
+ │ │ pointer resize │││ ││ or 48px rail │
162
+ └────┴───────────────────────┴─┴──────────────────────────────────────┴─┴──────────────┘
163
+ ```
164
+
165
+ 展开左栏时,48px rail 与内容合并为一个 `240–480px` 面板,不额外占 48px。折叠时保留 48px rail。
166
+
167
+ ### 4.2 Composer
168
+
169
+ ```text
170
+ ┌─────────────────────────────────────────────────────────────────────────────┐
171
+ │ [Context: file.ts] [Review: hunk 2] [remove all] │
172
+ │ 描述任务,或输入 / 查看命令… │
173
+ │ │
174
+ │ [+] [BUILD ▾] [ASK ▾] [GLM-5.2 · High ▾] [◔ 42%] [↑/■] │
175
+ └─────────────────────────────────────────────────────────────────────────────┘
176
+ ```
177
+
178
+ 控制顺序体现发送前的决策路径:
179
+
180
+ - 左侧:添加 Context、选择工作模式、选择权限;
181
+ - 右侧:选择实际模型/effort、检查 Context 压力、发送/停止;
182
+ - running 时保留 Stop、Steer 和 Queue,不让控制行被审批卡或队列撑出视口。
183
+
184
+ ### 4.3 窄屏
185
+
186
+ - `≤ 760px`:左导航和右工作面板都成为互斥 modal drawer;
187
+ - Composer 控制行保持一行,低优先级说明收进 `…`,模式/权限/模型仍可达;
188
+ - Context tray 横向滚动,不增高到覆盖输入框;
189
+ - drawer 打开时主区 inert,Escape、scrim、关闭按钮均可关闭并恢复焦点;
190
+ - 进入窄屏不改写桌面左/右宽度和展开偏好。
191
+
192
+ ## 5. 左侧项目导航设计
193
+
194
+ ### 5.1 三种状态
195
+
196
+ | 状态 | 宽度 | 内容 |
197
+ | --------- | --------- | ----------------------------------------------------------------------------- |
198
+ | Expanded | 240–480px | 品牌、项目搜索、Project/Session tree、创建 Session、Runtime 状态、设置 |
199
+ | Collapsed | 48px | 展开按钮、新建 Session、活动项目入口、设置、Runtime 状态;全部有 tooltip/名称 |
200
+ | Drawer | 自适应 | `min(storedWidth, viewport - 42px)`,完整内容;移动端状态不覆盖桌面偏好 |
201
+
202
+ ### 5.2 拖动行为
203
+
204
+ - 分隔条是 8px 透明 hit strip,视觉线保持 1px;
205
+ - 仅主鼠标按钮开始拖动,使用 pointer capture;
206
+ - pointer move 经 rAF 合帧,只修改 CSS variable,不在每一帧写 storage;
207
+ - pointerup/cancel/lost capture 时提交一次偏好;
208
+ - 双击恢复 280px;
209
+ - 拖动期间关闭 grid transition,防止边缘落后于指针;
210
+ - 折叠状态不显示 splitter;
211
+ - 不提供键盘逐像素调宽。键盘用户通过可访问的展开/折叠按钮使用完整功能;
212
+ - `prefers-reduced-motion` 下移除折叠动画,不移除状态变化。
213
+
214
+ ### 5.3 宽度求解
215
+
216
+ 新增纯函数 `computeWorkbenchColumns()`,输入 viewport、左右偏好和展开状态,输出三栏实际宽度。求解顺序:
217
+
218
+ 1. 保留展开左栏的合法偏好;
219
+ 2. 右栏从偏好收缩至 320px;
220
+ 3. 仍不足时把右栏收成 48px rail;
221
+ 4. 保持中栏至少 560px;
222
+ 5. 达到 760px 以下时左栏自动进入 drawer;
223
+ 6. 任一 derived collapse 不改写已保存偏好,窗口重新变宽后自动恢复。
224
+
225
+ ### 5.4 偏好迁移
226
+
227
+ 新增 `WorkbenchLayoutPreferenceV2`:
228
+
229
+ ```ts
230
+ interface WorkbenchLayoutPreferenceV2 {
231
+ schemaVersion: 2;
232
+ projectNavigation: {
233
+ expanded: boolean;
234
+ widthPx: number;
235
+ };
236
+ workPanel: {
237
+ expanded: boolean;
238
+ widthPx: number;
239
+ activePanel: 'agent' | 'review' | 'terminal' | 'files' | 'git';
240
+ agentPanel: 'goal' | 'activity' | 'integrations' | 'diagnostics';
241
+ };
242
+ }
243
+ ```
244
+
245
+ - 从 `orion.web.work-panel.v1` 迁移右栏值;
246
+ - 左栏无旧偏好时使用 280px + expanded;
247
+ - 非法 JSON、NaN、越界值回退默认;
248
+ - localStorage 失败不影响 Workbench;
249
+ - 偏好只存尺寸/面板 ID,不存项目路径、Session 文本或草稿。
250
+
251
+ ## 6. Composer Control Center
252
+
253
+ ### 6.1 工作模式下拉框
254
+
255
+ 触发器显示 `BUILD`、`PLAN` 或 `AUTO`,菜单用 `menuitemradio` 展示:
256
+
257
+ | 模式 | 用户文案 | Runtime 语义 |
258
+ | ----- | -------------------- | ---------------------------------------------------------------- |
259
+ | BUILD | 实现、修改和验证 | 完整工具集;遵循当前权限策略和 durable grants |
260
+ | PLAN | 调研并生成可审核计划 | 完整工具集;不自动执行生成的 Plan;进入 Plan review gate |
261
+ | AUTO | 无人值守执行 | 硬拒绝、显式 deny 和安全边界仍优先;其余 invocation 不弹交互审批 |
262
+
263
+ 规则:
264
+
265
+ - 模式与权限保持两个独立控件;
266
+ - idle 时立即生效,active turn 时显示 `BUILD → PLAN` 等 pending 状态,并在下一逻辑请求边界生效;
267
+ - 进入 AUTO 需要风险确认,文案明确“prompt-free 不等于 policy-free”;
268
+ - mutation 失败恢复 Host last-good,不保留客户端乐观值;
269
+ - 菜单 footer 提供“查看模式与权限说明”,打开本地帮助,不跳外部网页。
270
+
271
+ ### 6.2 权限下拉框
272
+
273
+ P0 采用“当前 Session override + Project default”的明确作用域:
274
+
275
+ - `Use project default`:清除 Session override;
276
+ - `Ask`:需要时询问;
277
+ - `Allow`:减少常规询问,但仍受硬拒绝、workspace、sandbox 和 capability policy 约束;
278
+ - `Deny`:默认拒绝没有更强显式授权的工具调用。
279
+
280
+ 触发器显示有效值和来源,例如:
281
+
282
+ - `ASK · Project`
283
+ - `ALLOW · Session`
284
+ - `DENY · Session`
285
+ - `AUTO · hard policy active`
286
+
287
+ 规则:
288
+
289
+ - 选择 Allow 必须经过与 Settings 相同的风险确认;
290
+ - Session override durable 保存,恢复 Session 后保持;
291
+ - Settings 继续管理 Project default;二者不能互相伪装;
292
+ - 已经弹出的 approval 不因切换 policy 自动通过或拒绝;
293
+ - active turn 中的新 policy 从下一次工具 admission 生效;
294
+ - AUTO 下仍展示保存的 Ask/Allow/Deny,但明确当前调用由 AUTO precedence 决定;显式 Deny 仍应高于 AUTO;
295
+ - 每个 tool card 继续显示 authorization provenance。
296
+
297
+ ### 6.3 模型与推理强度下拉框
298
+
299
+ 触发器显示 `模型名 · Effort`,弹层分两层:
300
+
301
+ 1. 根层:Model、Reasoning effort 两行;
302
+ 2. Model 层:按 provider 分组,显示名称、说明、上下文窗口和 capability;
303
+ 3. Effort 层:只显示该模型由 Host 声明的选项,不在浏览器维护私有枚举;
304
+ 4. 模型超过 8 个时提供本地过滤;不可用 provider 分组显示错误和 Retry,其他分组仍可使用。
305
+
306
+ 权威规则:
307
+
308
+ - current selection、catalog、effort 和 capability 均来自 Host;
309
+ - Composer 切换只更新当前 Session,不修改 Settings 中的 Project default;
310
+ - 选择成功后更新 Session metadata、LLM client、effort context 和 context budget;
311
+ - active turn 中不替换正在执行的模型,切换进入 pending 并在逻辑边界提交;
312
+ - 切换到更小上下文窗口时必须运行 ModelCoordinator compact preflight;
313
+ - compact、provider client、metadata 任一步失败都回滚到原 selection;
314
+ - 浏览器只收到 credential slot 状态,不收到 API key、base auth header 或 secret;
315
+ - subagent/不支持切换的 Session 显示原因并禁用,而不是静默隐藏选择结果。
316
+
317
+ ### 6.4 Context Meter
318
+
319
+ 控制行使用 14–16px 环形占用指示器,点击打开 264–300px 弹层。
320
+
321
+ P0 展示 Host 已有的精确字段:
322
+
323
+ - `usedTokens / safeInputBudget` 和百分比;
324
+ - raw model context window;
325
+ - reserved output tokens 与 safety margin;
326
+ - 数据来源:provider、provider-adjusted 或 estimated;
327
+ - warning/auto-compact 阈值和 auto-compact 是否启用;
328
+ - 当前模型 ID;
329
+ - 最近一次 compact 状态和“立即压缩”操作(只有 Runtime 支持时出现)。
330
+
331
+ 显示规则:
332
+
333
+ - provider 计数直接显示;estimated 值使用 `~` 和“估算”说明;
334
+ - 没有 capacity 时不画 0% 假环,显示不可用状态;
335
+ - 模型切换期间清除旧 capacity,不能短暂把旧模型窗口显示为新模型;
336
+ - 接近 warning 阈值改变文字/图标,不只依赖颜色;
337
+ - breakdown 只有 Runtime 提供 system/tools/messages 的同一 revision 计数时才展示;v0.3.2 不从 transcript 反推;
338
+ - Context 数据不得包含 prompt 原文、工具参数、绝对路径或 secret。
339
+
340
+ ### 6.5 `+` Context Launcher
341
+
342
+ `+` 菜单统一当前分散入口:
343
+
344
+ - 引用 workspace 内文件或目录;
345
+ - 引用 Review 中的文件/hunk;
346
+ - 引用一个历史 Session 的受控摘要;
347
+ - 选择 user-invocable Skill;
348
+ - 打开命令菜单;
349
+ - 清除当前草稿 Context。
350
+
351
+ 所有项目都生成结构化 `ContextReferenceV1`,而不是把不透明 Markdown 直接拼进 textarea:
352
+
353
+ ```ts
354
+ type ContextReferenceV1 =
355
+ | { kind: 'file'; id: string; label: string; revision: string }
356
+ | { kind: 'folder'; id: string; label: string; revision: string }
357
+ | { kind: 'review'; id: string; label: string; gitRevision: string }
358
+ | { kind: 'session'; id: string; label: string; digest: string }
359
+ | { kind: 'skill'; id: string; label: string; digest: string };
360
+ ```
361
+
362
+ Context tray 显示 chip、来源、stale 状态和删除按钮;Host 在 submit admission 时重新校验 workspace、revision、digest 和权限。失效引用应阻止发送并给出修复选项,不能静默发送旧内容。
363
+
364
+ ### 6.6 Running、Steer 与 Queue
365
+
366
+ - primary action 在 idle 为 Send,在 running 为 Stop;
367
+ - running 时普通 Enter 依当前偏好 Queue,显式 Steer 保留独立按钮;
368
+ - queue 面板固定在 Composer 上方的受限高度内,不能把 textarea 推出视口;
369
+ - queue item 支持编辑、删除、上移和下移;
370
+ - 所有 queue mutation 带 item revision 和 expected session;
371
+ - queue/Steer 切换模式、模型或权限时,必须冻结每条消息实际使用的控制快照,或明确标记“执行时采用最新控制”;v0.3.2 决定采用后者,并在队列头显示说明;
372
+ - interrupted/removed Session 的 queue 不跨 Session 迁移。
373
+
374
+ ## 7. 同版本新增优化
375
+
376
+ ### 7.1 Durable Plan Review Gate
377
+
378
+ PLAN turn 生成有效 Plan 后进入 `awaiting_review`,不自动排队执行。用户有三个明确操作:
379
+
380
+ 1. `批准并进入 BUILD`:对 exact plan digest 提交 approve,切 BUILD,并从新逻辑请求开始执行;
381
+ 2. `继续规划`:保留 PLAN,把反馈作为新用户消息;
382
+ 3. `取消计划`:关闭 review,不执行。
383
+
384
+ Plan review 必须:
385
+
386
+ - 由 Thread durable event/TurnCommit authority 驱动,刷新/重连后恢复;
387
+ - 包含 plan digest、revision、创建模型、创建时间和当前状态;
388
+ - stale digest 返回 409,绝不批准新 Plan 的旧按钮;
389
+ - 不把“HTTP 202”当批准成功,UI 等待 durable state;
390
+ - approval、Plan review 和 permission approval 是三种不同状态,不共用 requestId 或按钮语义。
391
+
392
+ ### 7.2 会话草稿恢复
393
+
394
+ - 以 opaque workspaceId + sessionId 保存当前标签页草稿和 ContextReference ID;
395
+ - 使用 `sessionStorage`,不跨浏览器重启或设备同步;
396
+ - 每个草稿上限 256KiB,最多保留 20 个 Session,LRU 淘汰前清除引用对象 URL;
397
+ - send 成功后删除,send 失败保留;
398
+ - Session 删除、workspace 移除或用户“清除草稿”时删除;
399
+ - 草稿不进入 Host 日志、SSE、release receipt 或 telemetry;
400
+ - UI 明示“此标签页草稿”,避免被误解为 durable Session 内容。
401
+
402
+ ### 7.3 控制状态与恢复
403
+
404
+ - 所有下拉框都显示 loading、pending、last-good、error、retry;
405
+ - Host restart/replay reset 后关闭菜单、暂停 mutation、加载 matching snapshot 后恢复;
406
+ - 多标签页发生 CAS conflict 时,不自动重放高风险选择;刷新 control state 后让用户确认;
407
+ - 控制状态变化写入可审计事件,但不进入模型 transcript;
408
+ - header 只保留运行状态与 Session 名,避免与 Composer 重复显示模式/权限。
409
+
410
+ ### 7.4 快捷键与命令一致性
411
+
412
+ - `Cmd/Ctrl+B` 继续展开/折叠左栏;
413
+ - 模式、权限、模型的菜单入口有 command palette action,但不增加键盘调宽;
414
+ - `/model`、`/effort`、`/permission` 与 Composer 必须共用相同 service 和 state,不允许两套写入链;
415
+ - Composer 改动后 slash command 的成功/失败会立即投影回 dropdown;
416
+ - `?` 或帮助菜单展示实际可用快捷键,避免靠 placeholder 记忆。
417
+
418
+ ## 8. 响应式与视觉状态
419
+
420
+ ### 8.1 断点
421
+
422
+ | 容器宽度 | 左栏 | 右栏 | Composer |
423
+ | ------------ | ----------------------------- | ----------------------------- | -------------------------- |
424
+ | `> 1180px` | Dock,48px rail 或 240–480px | Dock/rail,按 concession 求解 | 全控制行 |
425
+ | `761–1180px` | Dock,必要时 clamp | modal drawer | 完整控件,说明收进 tooltip |
426
+ | `≤ 760px` | modal drawer,桌面偏好不变 | modal drawer,互斥 | 紧凑触发器 + overflow |
427
+ | `≤ 390px` | drawer 宽 `viewport - 42px` | drawer 宽 `viewport - 42px` | Context tray 横向滚动 |
428
+ | `320px` | 无页面横向滚动,44px 控件目标 | 无隐藏关闭按钮 | textarea 和 Send 始终可见 |
429
+
430
+ 优先使用 Workbench 容器的 ResizeObserver,而不是只读取 `window.innerWidth`,使浏览器侧栏、缩放和嵌入窗口都能触发正确求解。
431
+
432
+ ### 8.2 状态矩阵
433
+
434
+ 每个 Composer 控件至少覆盖:
435
+
436
+ - ready;
437
+ - loading;
438
+ - disabled with reason;
439
+ - pending current → target;
440
+ - success;
441
+ - conflict/stale;
442
+ - provider unavailable;
443
+ - runtime busy/deferred;
444
+ - offline/replay-required;
445
+ - Host restart/recovered;
446
+ - read-only/unconfigured。
447
+
448
+ ## 9. API 与状态合同
449
+
450
+ ### 9.1 新增只读合同
451
+
452
+ 建议新增:
453
+
454
+ - `GET /api/v1/sessions/{sessionId}/composer-state`
455
+ - query 必须包含 `workspaceId`、`expectedContextRevision`;
456
+ - 返回 `controlRevision`、mode、permission、model selection、context usage、plan review、queue summary;
457
+ - `GET /api/v1/sessions/{sessionId}/model-catalog`
458
+ - cursor 分页;按 provider 分组;返回 Host 声明的 effort/capability/context window;
459
+ - partial provider failure 不丢弃 last-good 组。
460
+
461
+ `SessionSnapshot` 同时携带同一 `controlRevision` 的 composer baseline,避免刷新必须先接 SSE 才知道当前控制状态。
462
+
463
+ ### 9.2 新增 mutation 合同
464
+
465
+ 建议新增统一入口:
466
+
467
+ `POST /api/v1/sessions/{sessionId}/composer-actions`
468
+
469
+ 公共字段:
470
+
471
+ ```ts
472
+ interface ComposerActionBaseV1 {
473
+ requestId: string; // UUID
474
+ workspaceId: string;
475
+ expectedContextRevision: string;
476
+ expectedSessionId: string;
477
+ expectedControlRevision: string;
478
+ }
479
+ ```
480
+
481
+ body 使用 `oneOf` + discriminator:
482
+
483
+ - `set_agent_mode`;
484
+ - `set_permission_override` / `clear_permission_override`;
485
+ - `select_model`,含 modelId + optional effort;
486
+ - `compact_context`;
487
+ - `review_plan`,含 planDigest + approve/continue/cancel;
488
+ - `edit_queue_item` / `move_queue_item` / `remove_queue_item`。
489
+
490
+ 响应必须返回:
491
+
492
+ - requestId;
493
+ - 新 controlRevision;
494
+ - accepted/deferred/applied;
495
+ - matching composer state;
496
+ - model switch 或 plan review 的 typed receipt;
497
+ - 失败时 RFC 9457 problem code。
498
+
499
+ ### 9.3 错误码
500
+
501
+ 至少冻结:
502
+
503
+ - `context_revision_conflict`;
504
+ - `active_session_changed`;
505
+ - `composer_control_conflict`;
506
+ - `runtime_busy`;
507
+ - `model_unavailable`;
508
+ - `model_effort_unsupported`;
509
+ - `model_compaction_required`;
510
+ - `model_switch_rolled_back`;
511
+ - `permission_override_rejected`;
512
+ - `plan_review_stale`;
513
+ - `context_reference_stale`;
514
+ - `context_reference_forbidden`;
515
+ - `queue_item_conflict`。
516
+
517
+ ### 9.4 SSE 事件
518
+
519
+ 新增 typed event:
520
+
521
+ - `composer_state_changed`;
522
+ - `context_usage_changed`;
523
+ - `plan_review_requested`;
524
+ - `plan_review_resolved`。
525
+
526
+ 要求:
527
+
528
+ - 每个 event 带 sessionId、workspaceId、contextRevision、controlRevision;
529
+ - stale Session event 不得更新当前 Composer;
530
+ - replay_reset 是硬屏障,之后 live event 不能越过 snapshot recovery;
531
+ - Context 高频更新可以 coalesce,但 finalize/阈值跨越必须送达;
532
+ - transient delta 不得挤掉 durable control event 的恢复窗口。
533
+
534
+ ## 10. Runtime 与持久化
535
+
536
+ ### 10.1 Model
537
+
538
+ - 抽出 Web/TUI 共用 `SessionModelSelectionServiceV1`;
539
+ - 复用 ModelCoordinator 的 resolve、client validation、compact preflight、commit 和 rollback;
540
+ - `updateSessionModel` 与 effort metadata 在同一逻辑事务内提交;
541
+ - 新模型 selection 以 revision/digest 绑定;
542
+ - 恢复 Session 时按 session override > project default > global default 重建。
543
+
544
+ ### 10.2 Permission
545
+
546
+ - SessionMeta 或 Thread durable state 新增 optional tool-confirmation override;
547
+ - effective policy 同时返回 value 和 source;
548
+ - Project default 仍由 SettingsCoordinator 管理;
549
+ - ToolGateway 每次 admission 捕获 exact policy receipt;
550
+ - 切换不能修改已经 started/pending 的 invocation。
551
+
552
+ ### 10.3 Plan review
553
+
554
+ - 引入 `PlanReviewStateV1` durable event family;
555
+ - authority 为最新有效 Plan digest + review transition;
556
+ - `approve` 只创建新的执行 logical request,不在 review mutation 内直接跑工具;
557
+ - restart、resume、fork 的继承规则写入迁移文档;fork 默认复制已批准前的 Plan 内容,但不复制 pending approval。
558
+
559
+ ### 10.4 Context references
560
+
561
+ - 浏览器只持 opaque ID;
562
+ - Host 在 prompt assembly 时解析为 bounded、sanitized、workspace-contained content;
563
+ - file/folder 使用 file read model;review 使用 exact Git revision;Session 引用使用受控摘要;Skill 使用 digest;
564
+ - prompt manifest 记录引用 digest 和 token/byte budget,不记录 secret 原文;
565
+ - stale/forbidden 引用 fail closed。
566
+
567
+ ## 11. 安全与隐私
568
+
569
+ - Allow/AUTO 风险确认不能用默认 checked checkbox;
570
+ - 权限菜单不得暗示绕过 sandbox、workspace containment 或硬拒绝;
571
+ - 模型目录不包含 API key、auth header、raw provider error body;
572
+ - Context popover不显示 prompt 原文、工具参数、绝对路径或环境变量;
573
+ - Context 引用不能读取 workspace 外 symlink、`.env`、key、credential 或受保护 artifact;
574
+ - 所有 mutation 在 action 前完成 context/session/control CAS;
575
+ - requestId ledger 满时在副作用前 503,不静默淘汰旧幂等记录;
576
+ - SSE、snapshot、problem detail、截图和 E2E evidence 统一运行 secret sentinel 扫描;
577
+ - Draft 只放 sessionStorage;不进入 localStorage、Host、telemetry 或 release evidence;
578
+ - 不从 WorkBuddy bundle复制代码、图标、文案或闭源资源。
579
+
580
+ ## 12. 可访问性
581
+
582
+ - 下拉触发器使用 button + `aria-haspopup="menu"` + `aria-expanded`;
583
+ - 选项使用 `menuitemradio`/`aria-checked`,不是只改变颜色;
584
+ - Escape 从子层返回根层,再关闭并恢复触发器焦点;
585
+ - Allow/AUTO 风险确认使用 `alertdialog`,首焦点落在说明/确认框;
586
+ - Context popover使用 dialog 语义,百分比有完整 accessible name;
587
+ - left rail 图标全部有名称和 tooltip;
588
+ - 折叠左栏时,若焦点在即将隐藏的内容中,焦点返回 toggle;
589
+ - drawer 主区 inert,Tab 不逃逸,关闭后返回触发器;
590
+ - 控件目标至少 44×44 CSS px;高密度桌面视觉按钮可为 28–32px,但必须有不重叠的 44px hit area;
591
+ - 200% zoom、forced colors、reduced motion、light/dark 均纳入 gate;
592
+ - pointer splitter 不加入伪 slider 语义;键盘替代是展开/折叠,不是逐像素调整。
593
+
594
+ ## 13. 性能与预算
595
+
596
+ - splitter pointer-to-paint p95 ≤ 16ms,目标 55–60fps;
597
+ - 菜单首次打开 p95 ≤ 100ms,model catalog warm open ≤ 50ms;
598
+ - model catalog 只在首次打开/失效时请求,不能每次 render 请求;
599
+ - Composer state baseline p95 ≤ 250ms,不随 transcript 长度增长;
600
+ - Context event 到 UI p95 ≤ 250ms;
601
+ - Context meter 更新不触发 transcript 列表重渲染;
602
+ - 100 个模型时菜单 DOM ≤ 80 个 option,使用分组窗口或 virtualization;
603
+ - 20 个 Context chip 时 Composer 高度有上限,tray 横向滚动;
604
+ - draft autosave debounce 250ms,单标签总量 ≤ 5MiB;
605
+ - session switch 不预取全部模型 catalog、Context 内容或历史 transcript;
606
+ - release evidence 记录 RPC count、DOM nodes、drag frames、menu latency、SSE dropped events。
607
+
608
+ ## 14. 实施阶段
609
+
610
+ ### Phase 0:版本与合同冻结
611
+
612
+ 交付:
613
+
614
+ - 评审并冻结本计划;
615
+ - 在干净、已验证的 v0.3.1 release baseline 上创建 `v0.3.2` 实施分支;
616
+ - 对现有 dirty/untracked 文件逐项确认归属,不覆盖用户改动;
617
+ - 更新 OpenAPI 草案、状态机和 error code;
618
+ - 写失败优先的 protocol/layout/model/permission/plan tests。
619
+
620
+ 退出条件:需求—API—测试 ID 一一对应,OpenAPI parse/ref/discriminator/security/CAS 检查通过。
621
+
622
+ ### Phase 1:统一布局求解与左栏
623
+
624
+ 交付:
625
+
626
+ - WorkbenchLayoutPreferenceV2 与迁移;
627
+ - `computeWorkbenchColumns()`;
628
+ - 共用 pointer splitter primitive;
629
+ - 左栏 48px rail、展开/折叠、240–480px drag、双击复位;
630
+ - 760px drawer 和焦点管理。
631
+
632
+ 退出条件:layout unit + 真实 Chrome 指针拖动、刷新恢复、断点/200% zoom 全通过。
633
+
634
+ ### Phase 2:Composer state 与 UI shell
635
+
636
+ 交付:
637
+
638
+ - ComposerControlStateV1、snapshot/SSE baseline;
639
+ - mode/permission/model/context 四个触发器;
640
+ - menu primitive、nested pane、pending/last-good/error/retry;
641
+ - header 去除重复 mode/permission chip;
642
+ - 320–1440px responsive toolbar。
643
+
644
+ 退出条件:纯 UI fixture 和 Host-backed state round-trip 通过,无跨 Session stale update。
645
+
646
+ ### Phase 3:模型、权限与 Context Runtime
647
+
648
+ 交付:
649
+
650
+ - SessionModelSelectionServiceV1;
651
+ - Session permission override + source;
652
+ - typed ContextUsageViewV1;
653
+ - model catalog、model/effort mutation、compact preflight/rollback;
654
+ - AUTO/Allow risk flow与 authorization provenance。
655
+
656
+ 退出条件:双模型真实 provider、真实 ToolGateway、切小窗口 compact、rollback、restart restore 全通过。
657
+
658
+ ### Phase 4:结构化 Context 与 Plan review
659
+
660
+ 交付:
661
+
662
+ - `+` launcher、Context tray、file/review/session/skill reference;
663
+ - Host prompt assembly validation 与 token budget;
664
+ - durable PlanReviewStateV1 和三种操作;
665
+ - queue edit/move/remove;
666
+ - sessionStorage 草稿恢复。
667
+
668
+ 退出条件:stale/forbidden/secret/refresh/reconnect/多标签 CAS 和 Plan digest race 全通过。
669
+
670
+ ### Phase 5:可访问性、性能与发布证据
671
+
672
+ 交付:
673
+
674
+ - light/dark/forced-colors/reduced-motion/axe;
675
+ - 性能 budgets 与 counters;
676
+ - source-clean exact tgz;
677
+ - Node 22.12/24/26 同一 tgz 的 critical Chrome evidence;
678
+ - README、CHANGELOG、migration、OpenAPI 和截图。
679
+
680
+ 退出条件:第 17 节 release gate 全部 GO。
681
+
682
+ ## 15. 文件级实施建议
683
+
684
+ 预计新增或重构:
685
+
686
+ - `web/src/layout/WorkbenchShell.tsx`
687
+ - `web/src/layout/PanelResizeHandle.tsx`
688
+ - `web/src/state/layout-preferences.ts`
689
+ - `web/src/components/projects/ProjectNavigator.tsx`
690
+ - `web/src/components/composer/Composer.tsx`
691
+ - `web/src/components/composer/ComposerControlBar.tsx`
692
+ - `web/src/components/composer/AgentModeMenu.tsx`
693
+ - `web/src/components/composer/PermissionMenu.tsx`
694
+ - `web/src/components/composer/ModelMenu.tsx`
695
+ - `web/src/components/composer/ContextMeter.tsx`
696
+ - `web/src/components/composer/ContextTray.tsx`
697
+ - `web/src/components/composer/PlanReviewCard.tsx`
698
+ - `web/src/components/composer/QueueEditor.tsx`
699
+ - `web/src/state/composer-controls.ts`
700
+ - `web/src/state/draft-store.ts`
701
+ - `src/web/protocol.ts`
702
+ - `src/web/workbench-controller.ts`
703
+ - `src/runtime/session-model-selection-service.ts`
704
+ - `src/runtime/model-coordinator.ts`
705
+ - `src/runtime/agent-runtime-controller.ts`
706
+ - `src/runtime/runtime-protocol-v1.ts`
707
+ - `src/services/session-storage.ts`
708
+ - `src/services/model-context.ts`
709
+ - `docs/architecture/v0.3.2-web-api.yaml`
710
+ - `docs/architecture/agent-mode-permission-contract.md`
711
+ - `docs/migration/v0.3.1-to-v0.3.2.md`
712
+
713
+ 优先拆分 `Conversation.tsx`,避免继续把 timeline、approval、queue 和所有 Composer 控制堆在一个组件中。
714
+
715
+ ## 16. 测试方案
716
+
717
+ ### 16.1 单元与合同
718
+
719
+ - `tests/web-layout-preferences-v2.test.ts`:迁移、clamp、concession、derived drawer;
720
+ - `tests/web-composer-protocol.test.ts`:oneOf、UUID、CAS、problem code、unknown field rejection;
721
+ - `tests/web-session-model-selection.test.ts`:provider/effort、compact、rollback、metadata;
722
+ - `tests/web-session-permission.test.ts`:override/default/AUTO precedence、pending approval;
723
+ - `tests/web-context-usage.test.ts`:provider/estimated、safe budget、threshold、model reset;
724
+ - `tests/web-context-reference.test.ts`:opaque ID、revision、symlink、sensitive file、budget;
725
+ - `tests/web-plan-review.test.ts`:durable state、approve/continue/cancel、stale digest;
726
+ - `tests/web-queue-editor.test.ts`:edit/move/remove、item revision、wrong Session;
727
+ - `tests/web-draft-store.test.ts`:scope、limit、LRU、send/delete cleanup;
728
+ - reducer tests:replay_reset barrier、stale Session/control revision、last-good rollback。
729
+
730
+ ### 16.2 组件与可访问性
731
+
732
+ - left rail expanded/collapsed focus and accessible names;
733
+ - pointer splitter drag/double-click/lost capture;
734
+ - nested model/effort menu keyboard and focus return;
735
+ - permission/AUTO alertdialog;
736
+ - Context meter dialog and threshold text;
737
+ - Context tray removal and horizontal overflow;
738
+ - queue editor at 320px;
739
+ - Plan review card screen-reader order;
740
+ - light/dark/forced-colors/reduced-motion/200% zoom。
741
+
742
+ ### 16.3 真实 E2E 场景
743
+
744
+ | 场景 ID | 真实旅程 |
745
+ | ----------- | --------------------------------------------------------------------------------------------- |
746
+ | WEB32-P0-01 | 左栏 mouse-pointer min/default/max 拖动、双击复位、48px rail、refresh persistence |
747
+ | WEB32-P0-02 | 左右偏好同时最大时 concession 保持 center ≥560;扩窄窗口自动恢复 |
748
+ | WEB32-P0-03 | 320/390/760/1024/1180/1440 drawer、互斥、焦点和桌面偏好不被覆盖 |
749
+ | WEB32-P0-04 | BUILD/PLAN/AUTO 菜单 immediate/deferred、AUTO risk、模式与权限独立 |
750
+ | WEB32-P0-05 | Session Ask/Allow/Deny/default、Allow risk、AUTO/explicit deny、真实 ToolGateway side effect |
751
+ | WEB32-P0-06 | 双模型 catalog + effort,切小窗口触发真实 compact;provider/metadata 失败完整 rollback |
752
+ | WEB32-P0-07 | provider/estimated Context、阈值、auto-compact、模型切换清旧值、手动 compact |
753
+ | WEB32-P0-08 | file/review/session/skill Context add/remove/stale/forbidden/secret=0,prompt manifest digest |
754
+ | WEB32-P0-09 | PLAN 生成后不执行;approve/continue/cancel;refresh/restart;stale digest 409 |
755
+ | WEB32-P0-10 | queue edit/move/remove、Steer、Session 草稿切换/刷新、无跨 Session 串线 |
756
+ | WEB32-P0-11 | 双 tab control CAS、Host restart、replay_reset、matching snapshot 前 Composer disabled |
757
+ | WEB32-P0-12 | normal CSP、axe、light/dark/reduced-motion/200% zoom、console/page/HTTP5xx/secret/dropped=0 |
758
+
759
+ E2E 必须:
760
+
761
+ - 从 source-clean tgz 安装,不导入源码 Host;
762
+ - 使用真实 Chrome 和 normal CSP,axe 可使用单独 context,但核心旅程不得 `bypassCSP`;
763
+ - 使用 loopback deterministic provider,至少提供大/小两种 context window 和多个 effort;
764
+ - 真实调用 Runtime、ModelCoordinator、compact transaction、ToolGateway、Session storage 和 Thread journal;
765
+ - side effect 以文件、durable event、receipt digest 和 snapshot 核验,不以 UI 文案自证;
766
+ - screenshot 只截 Workbench,对绝对路径和敏感内容脱敏并绑定 manifest digest;
767
+ - 不允许 skip、expected-fail 或 DOM 注入伪造状态。
768
+
769
+ ### 16.4 性能与规模
770
+
771
+ - 100 项目 × 200 Session,左栏 drag/search 不因列表规模卡顿;
772
+ - 100 model entries、10 provider group、2 partial failure;
773
+ - 20 Context refs、256KiB draft、10 queued messages;
774
+ - 500 次 Context update,验证 coalesce、finalize 和 durable event retention;
775
+ - 50k-turn Session 的 composer-state/context GET 不重放完整 transcript;
776
+ - slow provider + active turn + model pending,验证 UI 和 admission 不死锁。
777
+
778
+ ## 17. 验收标准与 Release Gate
779
+
780
+ ### 17.1 Given / When / Then
781
+
782
+ #### AC-01 左栏
783
+
784
+ Given 桌面 Workbench,When 用户用鼠标拖动左侧分隔条,Then 宽度在 240–480px 内跟随指针;中栏不小于 560px;双击恢复 280px;折叠为 48px rail;刷新和窄屏往返后恢复桌面偏好。
785
+
786
+ #### AC-02 Composer 控制
787
+
788
+ Given 一个活动 Session,When 用户打开模式、权限、模型和 Context 控件,Then 每个控件显示 Host current/last-good、来源、说明和 pending/error;控件不会遮住 textarea 或发送按钮。
789
+
790
+ #### AC-03 模型
791
+
792
+ Given 一个大上下文模型和一个小上下文模型,When 当前 Context 超过小模型 safe budget 并请求切换,Then Host 先提交可验证 compact,再原子切换;compact 或 persistence 失败时原模型、effort、Session metadata 和下一次请求完全不变。
793
+
794
+ #### AC-04 权限
795
+
796
+ Given Project default Ask,When Session 选择 Allow,Then该 Session 显示 Allow/Session,其他 Session 仍显示 Ask/Project;硬拒绝、workspace escape 和显式 deny 仍失败,receipt 的 authorization provenance 正确。
797
+
798
+ #### AC-05 Context
799
+
800
+ Given provider usage,When turn 流式执行并结束,Then Context 环和 token 详情按 Host projection 更新;estimated 数据有 `~`;模型切换不显示旧 capacity;敏感内容出现 0 次。
801
+
802
+ #### AC-06 Plan
803
+
804
+ Given PLAN mode,When模型提交 Plan,Then Runtime 进入 awaiting_review 且没有执行副作用;只有 exact digest 的“批准并进入 BUILD”能创建执行请求;继续规划和取消不会执行。
805
+
806
+ #### AC-07 结构化引用
807
+
808
+ Given workspace 文件、Diff hunk、历史 Session 和 Skill,When用户加入 Context 并发送,Then prompt manifest 绑定 exact ID/revision/digest;stale、敏感或越界引用阻止发送且无泄漏。
809
+
810
+ #### AC-08 恢复
811
+
812
+ Given两个标签页和一次 Host restart/replay reset,When另一个标签页改变 Session/控制状态,Then stale mutation 409、零副作用;当前页先恢复 matching snapshot,再开放发送;不会自动重放高风险操作。
813
+
814
+ ### 17.2 发布必须同时满足
815
+
816
+ - `npm run lint`、`npm run build`、完整 Jest、Web/E2E TypeScript、Prettier、`git diff --check`;
817
+ - OpenAPI parse/ref/operationId/discriminator/security/requestId/revision/cursor 自动检查;
818
+ - WEB32-P0-01..12 全部真实 Chrome GO,无 skip/expected-fail;
819
+ - Node 22.12、24.0、26.0 使用同一个 source-clean tgz,critical Web journey 全 GO;
820
+ - console error、warning、page error、HTTP 5xx、secret finding、dropped event、orphan process 均为 0;
821
+ - axe blocking 0,320–1440px 与 200% zoom 无页面横向滚动;
822
+ - package version、README、CHANGELOG、migration、OpenAPI、截图、receipt digest 一致;
823
+ - 本地源码 build 或旧 v0.3.1 运行页面不能作为 v0.3.2 发布证明。
824
+
825
+ ## 18. 风险与缓解
826
+
827
+ | 风险 | 影响 | 缓解 |
828
+ | ------------------------------ | ----------------------------- | ----------------------------------------------------------------------------------- |
829
+ | 左右都可拖动导致中栏被挤压 | Composer 不可用、控件遮挡 | 纯 column solver、center floor、先让右栏 concession、窄屏 drawer |
830
+ | 模型切换跨越 active turn | 同一 turn 使用两套模型/预算 | logical-boundary pending、exact selection revision、compact preflight、rollback |
831
+ | 权限 scope 语义混乱 | 一个 Session 操作改变整个项目 | Session override 与 Project default 分层显示,source 进入合同 |
832
+ | AUTO 被误解为绕过安全 | 高风险工具被错误授权 | 风险确认、explicit deny/hard policy 优先、receipt provenance |
833
+ | Context 数字看似精确但实际估算 | 用户对预算产生错误判断 | source 字段、estimated `~`、不做浏览器端 breakdown 推断 |
834
+ | Plan review 只做 UI 假闸门 | 刷新后继续自动执行 | durable PlanReviewState、digest CAS、执行作为独立 logical request |
835
+ | Context reference 读取敏感文件 | 本地秘密泄漏到模型/浏览器 | opaque ID、realpath containment、sensitive deny、sanitized derivative、sentinel E2E |
836
+ | 高频 Context 事件挤掉恢复窗口 | 重连后状态过期 | coalesce transient、durable control retention、snapshot hard barrier |
837
+ | sessionStorage 草稿含敏感文本 | 同一浏览器 profile 可读取 | 仅当前 tab、上限、显式清除、不跨重启;设置中提供关闭草稿恢复 |
838
+ | 对标导致范围膨胀 | v0.3.2 无法按期发布 | 非目标冻结;不做语音、自动化、专家市场、多 Runtime、任意上传 |
839
+
840
+ ## 19. 需求追踪
841
+
842
+ | 用户需求 | 设计章节 | 主要测试 |
843
+ | ---------------------- | -------------- | --------------- |
844
+ | 左栏展开/折叠 | 5.1 | WEB32-P0-01 |
845
+ | 左栏鼠标拖动 | 5.2 | WEB32-P0-01/02 |
846
+ | 窄屏自动抽屉 | 4.3、8.1 | WEB32-P0-03 |
847
+ | BUILD/PLAN/AUTO 改下拉 | 6.1 | WEB32-P0-04 |
848
+ | 权限下拉 | 6.2 | WEB32-P0-05 |
849
+ | Context | 6.4、6.5 | WEB32-P0-07/08 |
850
+ | 模型切换 | 6.3 | WEB32-P0-06 |
851
+ | 对齐 Codex | 1.2、4.2 | WEB32-P0-04..07 |
852
+ | 对齐 DSH | 1.3、5、6、7.1 | WEB32-P0-01..09 |
853
+ | 对齐 WorkBuddy | 1.4、6.5、7 | WEB32-P0-08/10 |
854
+ | 其他优化进入 v0.3.2 | 7 | WEB32-P0-08..10 |
855
+
856
+ ## 20. Definition of Done
857
+
858
+ v0.3.2 只有同时满足以下条件才可称完成:
859
+
860
+ - 本计划中的 Required scope 全部落地,没有用静态占位控件代替 Host 行为;
861
+ - 左栏、模式、权限、模型、Context、Plan review、Context refs、queue 和 draft 均有失败路径;
862
+ - Web/TUI/slash command 共用同一 model/permission/mode service;
863
+ - OpenAPI、TypeScript 类型、Runtime state、snapshot、SSE 和 UI 文案语义一致;
864
+ - 所有高风险 mutation 具备 context/session/control CAS、幂等和 zero-side-effect negative test;
865
+ - 真实浏览器在 exact installed tgz 上完成 WEB32-P0-01..12;
866
+ - Node 22.12/24/26 同一 artifact 的证据进入最终 release receipt;
867
+ - 文档、迁移、截图和用户帮助完成;
868
+ - 没有覆盖当前工作树中用户拥有的未提交文件;
869
+ - 计划、代码 diff 或本地页面看起来正常,都不能替代最终 GO receipt。