Merge remote-tracking branch 'origin/master' into codex/concrete-provenance-prose

# Conflicts:
#	.agents/notes/implemented/architecture/2026-07-29-terminal-llm-stream-failures.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-29-terminal-llm-stream-failures.zh.md
#	.agents/notes/implemented/feature/2026-07-27-trajectory-inspection-ledger.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-27-trajectory-inspection-ledger.zh.md
#	.agents/notes/implemented/process/2026-08-08-browser-gif-evidence-chain.i18n.yaml
#	.agents/notes/implemented/process/2026-08-08-browser-gif-evidence-chain.zh.md
#	docs/architecture.i18n.yaml
#	docs/architecture.zh.md
#	packages/boot/app-boot/README.i18n.yaml
#	packages/boot/app-boot/README.zh.md
This commit is contained in:
Turtle
2026-08-09 16:39:34 +08:00
376 changed files with 9620 additions and 1106 deletions
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-27-intent-named-subagent-continuation-operations.md
2026-07-27-intent-named-subagent-continuation-operations.md: e74d62b7582e92f8e5ce68327a677259c8453d24
2026-07-27-intent-named-subagent-continuation-operations.zh.md: dae4dd37fa9950f0b8d1ba6ec5c46b99977a7201
2026-07-27-intent-named-subagent-continuation-operations.zh.md: ae7b370441d8e0ee045f4d0fcf851d28d055b295
@@ -14,7 +14,7 @@ Status: implemented
## 决策
`SubagentService` 分离四种执行意图:`start(name, request)` 返回普通的、由持有方负责的 one-shot run;`startContinuable(spec)` 建立持久化 child,并返回其 id 与已接受的初始 `MessageId`;`followup(parent, childId, content, { source, signal })` 发送后续 parent 内容;`reportFrom(child, content, { delivery, signal })` 将选定的 child 内容发送给其直接 parent。`followup` 与 `Agent.followup()` 一致,而 `SubagentRun.steer()` 仍是范围更窄的能力,仅向已确认仍在运行的 run 提供 steering。面向模型的工具保留稳定的 `send_message` 与 `report` 名称,并将路由委托给对应的意图方法。
`SubagentService` 分离四种执行意图:`start(name, request)` 返回普通的、由持有方负责的 one-shot run;`startContinuable(spec)` 建立持久化 child,并返回其 id 与已接受的初始 `MessageId`;`followup(parent, childId, content, { source, signal })` 发送后续 parent 内容;`reportFrom(child, content, { delivery, signal })` 将选定的 child 内容发送给其直接 parent。`followup` 与 `Agent.followup()` 一致,而 `SubagentRun.steer()` 仍是范围更窄的能力,仅向已确认仍在运行的 run 提供 steering(中途引导)。面向模型的工具保留稳定的 `send_message` 与 `report` 名称,并将路由委托给对应的意图方法。
调用方请求与提供方请求相互分离。`SubagentStartRequest` 包含调用方提供的 one-shot 数据;`ResolvedSubagentStartRequest` 会在调用 `SubagentProvider.start()` 前加入由服务解析的描述符。创建可继续 child 时,管理器将 `ContinuableCreateRequest` 传给可选的 `SubagentProvider.prepareContinuable()`,且只接收分离的创建数据。`SubagentService.resume()` 与提供方恢复分发均不存在:继续执行管理器加载描述符、对 parent 进行鉴权,并负责 Agent 实体化、提示词投递、冷恢复与 teardown。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.md
2026-07-28-remove-synthetic-log-only-turns.md: 61c1aa4b1599fe020e0cd96c32daea01d419e95a
2026-07-28-remove-synthetic-log-only-turns.zh.md: 1661045ca9fb340ebffc9b749263262cb178eae4
2026-07-28-remove-synthetic-log-only-turns.zh.md: 65281d8342997165b465a744bb08dd2d815e014e
@@ -16,9 +16,9 @@ Status: implemented
`SessionStore.appendOutOfBand()`、`OutOfBandSessionEventMap` 和 `OutOfBandSessionEventType` 均不再存在。拥有纯日志事件的插件通过 `Session` 追加该事件;当操作承诺持久性时,插件会显式等待 `ctx.sessions.flush(session)`。系统不会仅为获得该检查点而打开轮次。
核心会话不变量继续强制核心所属的执行关系:轮次与步骤编号、steering、助手、工具、待办和请求头事件的封闭,以及同一步骤内的工具调用/结果配对。核心允许可合并扩展事件位于轮次之间,因为只有声明它们的插件知道这些事件受执行作用域约束,还是可以独立存在。插件的不变量配套组件仍负责其自身的事件关系。
核心会话不变量继续强制核心所属的执行关系:轮次与步骤编号、steering(中途引导)、助手、工具、待办和请求头事件的封闭,以及同一步骤内的工具调用/结果配对。核心允许可合并扩展事件位于轮次之间,因为只有声明它们的插件知道这些事件受执行作用域约束,还是可以独立存在。插件的不变量配套组件仍负责其自身的事件关系。
标题服务会在完成既有的服务状态、修订、取消和实时会话检查后,直接追加 `session/title`。随附模型辅助函数会在发起调用前追加其字面量 `session/title-llm-request` 记录。持久化通过有界 `session/event` 路径接纳两者,并在常规检查点与生命周期 teardown 时排空;二者都不会仅因为位于轮次之间就强制 flush。因此,回退标题、辅助请求记录或已接受的提供方标题可以出现在 `turn/end` 之后、下一个 `turn/start` 之前。手动压缩(compaction)利用同一项轮次间能力记录 `compact/* { turn: null }` 标记对,但会显式 flush 已闭合的尝试,因为 `/compact` 承诺在释放排队提示词接纳预留前完成持久化。
标题服务会在完成既有的服务状态、修订、取消和活跃会话检查后,直接追加 `session/title`。随附模型辅助函数会在发起调用前追加其字面量 `session/title-llm-request` 记录。持久化通过有界 `session/event` 路径接纳两者,并在常规检查点与生命周期 teardown 时排空;二者都不会仅因为位于轮次之间就强制 flush。因此,回退标题、辅助请求记录或已接受的提供方标题可以出现在 `turn/end` 之后、下一个 `turn/start` 之前。手动压缩(compaction)利用同一项轮次间能力记录 `compact/* { turn: null }` 标记对,但会显式 flush 已闭合的尝试,因为 `/compact` 承诺在放行排队中的提示词前完成持久化。
会话 fork 可以结束于开放轮次之外的任意稳定事件位置,而不限于 `turn/end`。这样,默认 fork 会保留独立标题和其他插件所属的纯日志记录,同时仍拒绝在活跃执行过程中截断前缀。
@@ -36,7 +36,7 @@ Status: implemented
## 验证
核心不变量测试会接受轮次之间的未知插件事件,同时继续拒绝位于该处的内置执行事件。钩子、plan-mode、Code Mode 分发和审批的不变量配套组件会在没有开放轮次时拒绝其执行作用域事件;压缩配套组件则另外接受轮次之间平衡的 `turn: null` 手动标记对,并要求数字 owner 匹配一个开放轮次。会话标题服务测试会在并发刷新、会话脱离拒绝和最新修订接受场景下,固定一个直接追加的回退事件。JSONL 和 SQLite 往返测试会通过持久化生命周期排空保留追加在 `turn/end` 之后的标题;fork 测试会保留独立纯日志尾部,同时拒绝位于开放轮次内的边界。一个无密钥、经完整组装的 ACP(Agent Client Protocol)快照会将模型生成的标题延迟到 `turn/end` 之后,并固定一个不含合成轮次的独立提供方标题。生成的 API 和类型等价性目录不含任何已移除符号。
核心不变量测试会接受轮次之间的未知插件事件,同时继续拒绝位于该处的内置执行事件。钩子、plan-mode、Code Mode 分发和审批的不变量配套组件会在没有开放轮次时拒绝其执行作用域事件;压缩配套组件则另外接受轮次之间平衡的 `turn: null` 手动标记对,并要求数字 owner 匹配一个开放轮次。会话标题服务测试会在并发刷新、拒绝已脱离会话和接受最新修订的场景下,固定一个直接追加的回退事件。JSONL 和 SQLite 往返测试会通过持久化生命周期排空保留追加在 `turn/end` 之后的标题;fork 测试会保留独立纯日志尾部,同时拒绝位于开放轮次内的边界。一个无密钥、经完整组装的 ACP(Agent Client Protocol)快照会将模型生成的标题延迟到 `turn/end` 之后,并固定一个不含合成轮次的独立提供方标题。生成的 API 和类型等价性目录不含任何已移除符号。
## 后果
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-29-shared-base-config-overlays.md
2026-07-29-shared-base-config-overlays.md: b2ca5cc8d3a160dd09550827d5f8148c14048ea4
2026-07-29-shared-base-config-overlays.zh.md: e14aa87bdd6ed3bd711bb9786220b5baf0566e8e
2026-07-29-shared-base-config-overlays.zh.md: adec7c793839e84edb14173d71735d264cf2ec28
@@ -6,7 +6,7 @@ Status: implemented
## 问题
`dsh` 交付了两棵完整的配置树,其中 43 个配置项完全相同。`apps/cli/cordis.yml` 以 74 个平铺配置项组合 web surface,而 TUI 启动的是 `examples/tui-agent/cordis.yml`——其中单独一行 `@deepseek-ai/dsh-tui-demo` 挂载了十二个插件,并把它们的配置重新声明为自己那份二十个键、仅作透传的 `Config`。
`dsh` 交付了两棵完整的配置树,其中有 43 个共享配置项。`apps/cli/cordis.yml` 以 74 个平铺配置项组合 web surface,而 TUI 启动的是 `examples/tui-agent/cordis.yml`——其中单独一行 `@deepseek-ai/dsh-tui-demo` 挂载了十二个插件,并把它们的配置重新声明为自己那份二十个键、仅作透传的 `Config`。
这两份文件都名不副实。`examples/tui-agent` 并不是示例:`apps/cli/src/tui.ts` 把它硬编码为产品的默认配置;它还拥有 TUI 的 PTY 冒烟测试、八个终端快照场景,以及被 `cordis-agent` 叶节点 import 的 PTY harness。`dsh-tui-demo` 也不是 demo——它就是应用本身,由交付的二进制从 `packages/examples/` 中挂载。
@@ -20,11 +20,11 @@ Status: implemented
优先级即列表顺序,逐配置项后写者胜:base,然后是 surface overlay,接着是 `--config` overlay 或个人 `~/.dsh/config.yaml`,最后是启动器自身的 flag 与 profile patch。
`--config <path>` 现在应用一个 overlay 来**取代**个人 overlay,因此 demo 或测试用的树绝不会继承用户的 provider 与 model。`--config-replace <path>` 则把某个文件作为整棵树启动,同时绕过 base、surface overlay 与个人 overlay;这正是旧 `--config` 的行为,所以像 `examples/web-cordis` 这样的树改用了新 flag。两个 flag 都会在 `/resume` 的 execve 交接中保留,否则 resume 会静默更换 agent。
`--config <path>` 现在应用一个 overlay 来**取代**个人 overlay,因此 demo 或测试用的树绝不会继承用户的提供方与 model。`--config-replace <path>` 则把某个文件作为整棵树启动,同时绕过 base、surface overlay 与个人 overlay;这正是旧 `--config` 的行为,所以像 `examples/web-cordis` 这样的树改用了新 flag。两个 flag 都会在 `/resume` 的 execve 交接中保留,否则恢复时会静默更换 agent(智能体)。
patch 会整体替换目标配置项的 `config` 而不合并,这决定了拆分方式:取值因 surface 而异的配置项住在 overlay 中,绝不住在 base 里,从而没有任何配置项会被三层同时 patch。因此会话身份根本不能经由配置键传递——它迁移到了 `dsh-agent-loop` 的 `CONFIGURED_AGENT_IDENTITIES_KEY`,正如启动器持有身份的记录所述。
`examples/tui-agent`、`examples/cordis-agent`、`examples/code-mode` 与 `packages/examples/tui-demo` 均被删除。TUI 测试迁往 `apps/cli/tests/`,cordis 工具集的 e2e 迁入 `packages/self-modification/tool-cordis/tests/`,受支持的 Code Mode demo 则保留为 `examples/acp-agent/code-mode.cordis.yml` 中的 ACP overlay。
`examples/tui-agent`、`examples/cordis-agent`、`examples/code-mode` 与 `packages/examples/tui-demo` 均被删除。TUI 测试迁往 `apps/cli/tests/`,cordis 工具集的 e2e 迁入 `packages/self-modification/tool-cordis/tests/`,受支持的 Code Mode demo 则保留为 `examples/acp-agent/code-mode.cordis.yml` 中的 ACP(Agent Client Protocol)overlay。
## 备选方案
@@ -38,16 +38,16 @@ patch 会整体替换目标配置项的 `config` 而不合并,这决定了拆
## 影响
指名 `@deepseek-ai/dsh-tui-demo`、或 patch `tui-agent` 配置项的 overlay 与 `--config` 树将不再可解析。overlay 现在要 patch 拥有对应键的那一行:模型路由在 `agent-loop`,人设在 `system-prompt`,呈现设置在 `tui`。
指名 `@deepseek-ai/dsh-tui-demo` 或 patch `tui-agent` 配置项的 overlay 或 `--config` 树将不再可解析。overlay 现在要 patch 拥有对应键的那一行:模型路由在 `agent-loop`,人设在 `system-prompt`,呈现设置在 `tui`。
若某个 patch 的 `id` 不匹配任何配置项,Loader 仍只告警而不报错。这是有意为之:同一份个人 overlay 会跨 surface 共用,而 `insert` 配置项按设计本就不匹配任何目标,因此仅在 `web` 下存在的配置项不能让 TUI 启动失败。
`dsh web` 新增 `--config`,作为一份额外 overlay 传入 `AppCLIEntry`。Web 保留沙箱化 Bash 与文件系统提供方,以及审批、权限预设、目录选择和浏览器权限界面;覆盖层会禁用共享的本地提供方,因为补丁可以禁用条目但不能删除条目。TUI 查询索引使用进程唯一的临时数据库,因为 SQLite 后端要求单写入者所有权。该索引是每个进程重新构建的可丢弃派生数据;`/resume` 直接列出底层语料,不依赖索引复用。`AppCLIEntry` 在为自身 patch 合并恢复配置项默认值时会同时读取 base 与其 surface overlay,因为 flag 覆盖必须保留同一配置项上 overlay 的其他字段。
`dsh web` 新增 `--config`,作为一份额外 overlay 传入 `AppCLIEntry`。Web 保留沙箱化 Bash 与文件系统提供方,以及审批、权限预设、目录选择和浏览器权限界面;覆盖层会禁用共享的本地提供方,因为补丁可以禁用条目但不能删除条目。TUI 查询索引使用每个进程独有的临时数据库,因为 SQLite 后端要求单写入者所有权。该索引是每个进程重新构建的可丢弃派生数据;`/resume` 直接列出底层语料,不依赖索引复用。`AppCLIEntry` 在为自身 patch 合并恢复配置项默认值时会同时读取 base 与其 surface overlay,因为 flag 覆盖必须保留同一配置项上 overlay 的其他字段。
## 验证
组合的正确性通过用真实 Loader 启动每棵树并检查已就绪的条目来核对,而不是靠阅读 YAML:两个界面都能稳定完成且没有未加载项;Web 会以沙箱化 Bash 与文件系统提供方启动 `httpServer`。Code Mode 继续由 ACP overlay 与程序化 TUI 快照覆盖,而不再维护独立交付的 TUI 应用。
组合的正确性通过用真实 Loader 启动每棵树并检查已就绪的条目来核对,而不是靠阅读 YAML:两个界面都能完全就绪,且没有未加载项;Web 会以沙箱化 Bash 与文件系统提供方启动 `httpServer`。Code Mode 继续由 ACP overlay 与程序化 TUI 快照覆盖,而不再维护独立交付的 TUI 应用。
全部八个终端快照场景在迁移后逐字节重放一致,14 个用例的 PTY 冒烟测试全部通过,其中两个用例断言个人 overlay 能触达一个 **insert 进来的**配置项——这正是 vendored `plugin-include` 修复所启用的行为([`vendor/README.md`](../../../../vendor/README.md) 本地修改第 8 条,由 `packages/boot/app-boot/tests/config-reload.spec.ts` 覆盖)。
平铺过程暴露出三处潜伏缺陷,均在此一并修复:TUI 曾在构造时一次性捕获可选的 `sessionQuery` 服务,因此在挂载竞争中胜出时会永久禁用 `/resume`;交付的会话存储根目录曾静默退回项目本地的 `./.sessions`;`--config-replace` 曾在 resume 交接中被丢弃。
平铺过程暴露出三处潜伏缺陷,均在此一并修复:TUI 曾在构造时一次性捕获可选的 `sessionQuery` 服务,因此在挂载竞争中胜出时会永久禁用 `/resume`;交付的会话存储根目录曾静默退回项目本地的 `./.sessions`;`--config-replace` 曾在恢复交接中被丢弃。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-30-private-agent-send.md
2026-07-30-private-agent-send.md: 43353c309f98eab1bff91b8fbe0c9cfaa2bbc69b
2026-07-30-private-agent-send.zh.md: 52ead82bb4e39a39e6c578ddb4311d1bae4afe69
2026-07-30-private-agent-send.zh.md: a475c39fc378bca609f747795e185d25accf4489
@@ -6,7 +6,7 @@ Status: implemented
## 问题
公开的 `Agent.send()` 方法暴露了实体循环的路由矩阵,但生产调用方只使用语义明确的 `followup()`、`steer()` 和 `inject()` 操作。第四种组合,即 `next-turn` 配合 `wakeup: false`,除测试外没有消费方。将这项潜在能力保留为公开接口,还会迫使其他 `Agent` 实现和测试替身接受实现层的路由策略。
公开的 `Agent.send()` 方法暴露了具体循环实现的路由矩阵,但生产调用方只使用语义明确的 `followup()`、`steer()` 和 `inject()` 操作。第四种组合,即 `next-turn` 配合 `wakeup: false`,除测试外没有消费方。将这项潜在能力保留为公开接口,还会迫使其他 `Agent` 实现和测试替身接受实现层的路由策略。
## 决策
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-31-capability-neutral-sandbox-policy-context.md
2026-07-31-capability-neutral-sandbox-policy-context.md: d45c6c652fb7a061c2f92b7e03c41cce39da2a30
2026-07-31-capability-neutral-sandbox-policy-context.zh.md: c5aa9cc1f03fa83a3b6d9606afc30f6135681ae0
2026-07-31-capability-neutral-sandbox-policy-context.zh.md: fb030e8cd77ceb9935dd9d8f9bd9de2ce2c0fdbc
@@ -18,7 +18,7 @@ Status: implemented
哪些操作可用仍以工具 schema 为准。针对具体操作的拒绝以及获批后的更宽松模式重试,仍以工具结果为准。文件系统、一次性 bash 与终端实现继续解析并强制执行相同的逐调用策略;移除的只有面向模型的冗余能力清单。
本决策取代了[当前沙箱策略上下文决策](../feature/2026-07-30-current-sandbox-policy-context.md)中关于家族注册与按组合条件化措辞的部分内容。缓存安全的上下文交付、持久快照具体化、措辞证据,以及引导与强制执行之间的边界,仍以该 Agent Note 为归属文档。
本决策取代了[当前沙箱策略上下文决策](../feature/2026-07-30-current-sandbox-policy-context.md)中关于家族注册与按组合条件化措辞的部分内容。缓存安全的上下文交付、持久快照物化、措辞证据,以及引导与强制执行之间的边界,仍以该 Agent Note 为归属文档。
## 曾考虑的替代方案
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-07-31-drop-user-message-edit-stub.md
2026-07-31-drop-user-message-edit-stub.md: 5a34be1dd285f4aced9e6cfb2e324b20ec734bed
2026-07-31-drop-user-message-edit-stub.zh.md: 768cb4618b4a85f61e6348f8c6fb6a93d686e7d3
2026-07-31-drop-user-message-edit-stub.zh.md: 288f2bda5d41cf0a331f69f134c8cf1e61a801ef
@@ -10,7 +10,7 @@ user 气泡的 IconActions 行在复制和分支旁边还有一个编辑按钮
## 决策
`MessageIconActions` 只渲染时钟/复制/分支,其 `edit` prop 随按钮一并删除;`MessageItem` 不再传入该 prop。现在 user 气泡与 assistant chrome 只在时钟位置上不同。包 README 在 Known Limitations 中记录这项缺失的能力,web 的 message-actions 金样固定了不含该控件的动作行。
`MessageIconActions` 只渲染时钟/复制/分支,其 `edit` prop 随按钮一并删除;`MessageItem` 不再传入该 prop。现在 user 气泡与 assistant chrome 只在时钟位置上不同。包 README 在 Known Limitations 中记录这项缺失的能力,web 的 message-actions 预期输出固定了不含该控件的动作行。
公共 locale 保留通用的 `edit` 词条:它是共享词汇,而非本组件的文案。
@@ -20,8 +20,8 @@ user 气泡的 IconActions 行在复制和分支旁边还有一个编辑按钮
**把按钮置灰并加提示。** 一个可见但无效的控件仍在宣告可以编辑,解释成本相同;直接移除才是诚实的状态。
**接到队列编辑器上。** 队列编辑的是尚未发送的消息。已定稿的 user 消息已经进入转录和模型上下文,复用该编辑器会让同一个动作悄悄变成另一件事。
**接到队列编辑器上。** 队列编辑的是尚未发送的消息。已定稿的 user 消息已经进入 transcript(文本记录)和模型上下文,复用该编辑器会让同一个动作悄悄变成另一件事。
## 后果
Web 没有任何途径修正已发送的消息;从该消息分支是最接近的现有手势。由于动作行的内容完全由 props 组合而来,能力就绪后重新引入只是一次纯 UI 改动。
Web 没有任何途径修正已发送的消息;从该消息分支是最接近的现有手势。由于动作行的内容完全由 props 组合而来,client 变更就绪后重新引入只是一次纯 UI 改动。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-08-03-omit-invariants-from-shipped-config.md
2026-08-03-omit-invariants-from-shipped-config.md: a691a0949806a752203a41bd6c66a50e97d8f1d7
2026-08-03-omit-invariants-from-shipped-config.zh.md: cb2b04de6f4634e9107052c6855cba532b3030ba
2026-08-03-omit-invariants-from-shipped-config.zh.md: aa1f775804de1bee7b69f355fc4afddf48c63a61
@@ -6,7 +6,7 @@ Status: implemented
## 问题
`@deepseek-ai/dsh-invariants` 与各包(package)拥有的 `./invariant` 伴随插件是可选的开发诊断。交付的 TUI 挂载了该服务和四个有状态伴随插件,而交付的 Web 配置树省略了这些条目,导致两个产品 surface 的诊断成本和失败行为不同。即使始终启用的产品边界仍负责会话验证与不可变历史,关系断言失败也可能终止普通的 TUI 运行。
`@deepseek-ai/dsh-invariants` 与各包拥有的 `./invariant` 伴随插件是可选的开发诊断。交付的 TUI 挂载了该服务和四个有状态伴随插件,而交付的 Web 配置树省略了这些条目,导致两个产品 surface 的诊断成本和失败行为不同。即使始终启用的产品边界仍负责会话验证与不可变历史,关系断言失败也可能终止普通的 TUI 运行。
## 决策
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-08-04-drop-windows-powershell-picker-fallback.md
2026-08-04-drop-windows-powershell-picker-fallback.md: 619afd31d9ec78cdb8565e29fa942b7db8749365
2026-08-04-drop-windows-powershell-picker-fallback.zh.md: e14904db46a955d4cf40da195a39bf62cbef96ff
2026-08-04-drop-windows-powershell-picker-fallback.zh.md: 14716f405e98385b7277ce0814adf32190be36b7
@@ -1,38 +1,38 @@
# Agent Note:删除 Windows PowerShell 选择器回退
# Agent Note: 删除 Windows PowerShell 选择器回退
Status: implemented
[English](2026-08-04-drop-windows-powershell-picker-fallback.md) | 中文
## Problem
## 问题
原生目录选择器的 win32 分支在 koffi `IFileOpenDialog` 子进程之下保留了一条两级 PowerShell 回退:先 `pwsh.exe`,再 `powershell.exe`(Windows PowerShell 5.1),两者运行同一个带 `SetProcessDPIAware` 开关的 WinForms 脚本。该链的存在是为了在 koffi 层"不可用"时仍能给出一个可用的选择器,但它可能保护的每一个触发条件都是我们自己打包或部署的失败,而不是操作系统的:
原生目录选择器的 win32 分支在 koffi `IFileOpenDialog` 子进程之下保留了一条两级 PowerShell 回退:先 `pwsh.exe`,再 `powershell.exe`(Windows PowerShell 5.1),两者运行同一个主动启用 `SetProcessDPIAware` 的 WinForms 脚本。该链的存在是为了在 koffi 层「不可用」时仍能给出一个可用的选择器,但它可能保护的每一个触发条件都是我们自己打包或部署的失败,而不是操作系统的:
- koffi 的原生二进制作为普通 optional 依赖(`@koromix/koffi-win32-x64`,无 install script)分发;能装上该包的宿主就一定有二进制,装不上的宿主会在安装期大声失败——回退代码同样不会加载。
- "上古 Windows"不可能出现:本仓库支持的 Node 版本运行在远比 Vista 时代 `IFileOpenDialog` ABI 新的 Windows 世代上。
- koffi 的原生二进制作为普通的可选 NPM 依赖(`@koromix/koffi-win32-x64`,无 install script)分发;能装上该包的宿主就一定有二进制,装不上的宿主会在安装期明确报错——回退代码也根本不会得到加载。
- 「上古 Windows」不可能出现:本仓库支持的 Node 版本运行在远比 Vista 时代 `IFileOpenDialog` ABI 新的 Windows 世代上。
- koffi/COM 缺陷只崩对话框子进程(crash isolation);对我们自己 bug 的正确反应是上报失败,而不是静默降级到旧版对话框。
这条链还付出了真实的复杂度:两个 spawn 层运行同一脚本、把回退触发从 `ENOENT` 拓宽为 pwsh 的任何失败以关闭 PowerShell 6(无 WinForms)回归、携带全部三个原因的三连败 `AggregateError`,以及每层的 abort 重检。seam 早已拥有唯一重要的回退——组合层面的 `browse` 后端,由 `directory-picker-auto` 在启动时选择一次。
这条链还付出了真实的复杂度:两个 spawn 层运行同一脚本、把回退触发从 `ENOENT` 拓宽为 pwsh 的任何失败以修复 PowerShell 6(无 WinForms)回归问题、携带全部三个原因的三连败 `AggregateError`,以及每层的 abort 重检。seam 早已拥有唯一重要的回退——组合层面的 `browse` 后端,由 `directory-picker-auto` 在启动时选择一次。
## Decision
## 决策
win32 层恰好就是 koffi `IFileOpenDialog` 子进程;任何失败原样上报,无回退。PowerShell 链——`pwsh` → Windows PowerShell 5.1 级联、DPI 修正的 WinForms 脚本、`AggregateError` 聚合——被删除,`pickNativeDirectory` 的 win32 分支成为单次调用。`dsh-native-command` 仍为 POSIX 层保留依赖。
本包其余部分早已遵循的回退判据现在统一适用:回退层只存在于操作系统/桌面环境提供且可能缺失的工具(Linux 的 `zenity` → `kdialog`,启动探针同样采样它们);我们自己打包的工具(`koffi`)失败即大声报错。macOS `osascript` 与之前一样保持无回退。
本包其余部分早已遵循的回退判据现在统一适用:回退层只存在于操作系统/桌面环境提供且可能缺失的工具(Linux 的 `zenity` → `kdialog`,启动探针同样采样它们);我们自己打包的工具(`koffi`)失败即明确报错。macOS `osascript` 与之前一样保持无回退。
本次变更合并并删除了 pwsh 优先的 DPI 选择器修复 Note:其决策在此被完全反转,其保留的 rationale 对只含 koffi 的层不再指导未来工作。其中真实的部分:PowerShell 7 呈现基于 `IFileDialog` 的现代文件夹选择器,而 5.1 的 `FolderBrowserDialog` 被硬连到旧版 `SHBrowseForFolder` 树;脚本的 `SetProcessDPIAware` 修正了 spawn 的系统 DPI 上限;pwsh→5.1 的跳转存在是因为可解析的 PowerShell 6 没有 WinForms(退出码 1,而非 `ENOENT`)。其被拒绝的替代方案(要求 PowerShell 7、导入 `resolvePwshPath`、在 harness 进程设置 DPI 感知)随链删除而失去意义。
本次变更合并并删除了 pwsh 优先的 DPI 选择器修复 Agent Note:其决策在此被完全反转,其保留的理由对只含 koffi 的层不再指导未来工作。其中真实的部分:PowerShell 7 呈现基于 `IFileDialog` 的现代文件夹选择器,而 5.1 的 `FolderBrowserDialog` 被硬连到旧版 `SHBrowseForFolder` 树;脚本的 `SetProcessDPIAware` 修正了 spawn 的系统 DPI 上限;pwsh→5.1 的跳转存在是因为可解析的 PowerShell 6 没有 WinForms(退出码 1,而非 `ENOENT`)。其被拒绝的替代方案(要求 PowerShell 7、导入 `resolvePwshPath`、在 harness 进程设置 DPI 感知)随链删除而失去意义。
## Alternatives considered
## 考虑过的替代方案
**保留链但去掉 pwsh 质量层(`koffi` → Windows PowerShell 5.1)。** 拒绝:剩下的层仍在为我们自己打包的依赖辩护,仍要付出脚本、拓宽的触发与聚合的代价,仍会把我们自己的 vtable/COM 缺陷藏到旧版对话框后面。"仅对外部提供的工具回退"的判据不接受任何 Windows 层。
**保留链但去掉 pwsh 质量层(`koffi` → Windows PowerShell 5.1)。**拒绝:剩下的层仍在防范我们自己打包的依赖发生故障,仍要付出脚本、拓宽的触发与聚合的代价,仍会把我们自己的 vtable/COM 缺陷藏到旧版对话框后面。「仅对外部提供的工具回退」的判据不接受任何 Windows 层。
**原样保留链。** 拒绝:它是选择器面上唯一的二级运行时回退,其触发条件是本就大声失败的部署侧失败,并且它把失败的 pick 降级成一个最具可操作性的条目是 PowerShell 宿主的 `AggregateError`。
**原样保留链。**拒绝:它是选择器中唯一的二级运行时回退,其触发条件是本就会明确报错的部署侧失败,并且它把一次失败的选择操作降级为 `AggregateError`,其中最具可操作性的条目却指向 PowerShell 宿主。
**原生 pick 失败时在运行时回退到 `browse`。** 拒绝:seam 的流程洞是 `single` kind,`-auto` 组合已在启动时选择一个后端;运行时跨 kind 跳转会双挂两个后端并模糊能力边界。
**原生 pick 失败时在运行时回退到 `browse`。**拒绝:seam 的流程洞属于 `single` 类型,`-auto` 组合已在启动时选择一个后端;运行时跨类型跳转会同时挂载两个后端并模糊能力边界。
## Consequences
## 后果
- win32 选择器的失败面是来自单一层的一个错误;调用方看到真实原因(koffi 加载失败、COM 拒绝、对话框崩溃),而不是链式聚合的错误。
- 本包不再调用 `pwsh`/`powershell.exe`;WinForms 脚本、其 `SetProcessDPIAware` 修正与 `-STA` 标志随之消失。
- 测试相应缩减:pwsh/5.1 级联与三连败用例被一个"失败原样上报、无回退"用例取代;默认适配器测试改驱动 Linux 层。
- 测试相应缩减:pwsh/5.1 级联与三连败用例被一个「失败原样上报、无回退」用例取代;默认适配器测试改驱动 Linux 层。
- 重新引入条件:未来出现在我们打包链之外的 win32 机制(我们不随包分发的系统提供的对话框宿主)才值得在同一判据下保留一层回退。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-08-04-remove-tui-package.md
2026-08-04-remove-tui-package.md: 1057243c70f6f2775a5d0c5f5eddcb72cbad699e
2026-08-04-remove-tui-package.zh.md: 0e03d6913aafaa3ce01c0f5732935d2c304c8e0e
2026-08-04-remove-tui-package.zh.md: 17a75921f3e8f227772133243bc0ea5b8ba927a7
@@ -1,4 +1,4 @@
# Agent Note: 移除 TUI 包(package)
# Agent Note: 移除 TUI 包
Status: implemented
@@ -12,17 +12,17 @@ Status: implemented
## 决策
删除 `packages/ui/tui` 包,不提供兼容包或别名。其源码、包测试、终端快照、依赖声明、已打补丁的 `pi-tui` 产物、workspace 引用、生成的服务目录条目和文档会一并移除。通用宿主能力与 agent-loop 能力保持不变。
删除 `packages/ui/tui` 包,不提供兼容包或别名。其源码、包测试、终端快照、依赖声明、已打补丁的 `pi-tui` 产物、workspace 引用、生成的服务目录条目和文档会一并移除。通用宿主能力与 agent loop(智能体循环)能力保持不变。
SDK 的运行接口联合类型现在只包含 `acp` 与 `embed`。`create-sdk` 默认使用 ACP,生成的模板不再包含终端启动、恢复、会话环境或模型参数分支;内置的 `ask-user` 功能也被移除,因为剩余两个生成接口都不提供 `UserInteractionProvider`。宿主应用仍可直接挂载提供方无关的 `dsh-user-interaction`、`dsh-commands` 和呈现 seam。
本决策取代[显式配置 `dsh` 入口决策](../../archived/simplification/2026-08-03-explicit-config-dsh-entrypoint.md)中保留可复用包的决定,也使已归档 TUI 实现记录不再适用于当前状态。这些历史记录继续保持冻结,但不再作为受支持包或应用清单的依据。
本记录汇总了删除后无法继续保持当前状态的仅限包记录。终端 UI 曾在长对话期间保持会话身份可见、移除重复模型标签、为消息附加耗时与阶段状态、在提示词旁显示 workspace 与分支上下文,并保守地解析完整 XML 包装层,以生成人类可读的回退输出。这些选择改善了一个终端前端,但没有部署时不足以证明应保留它。未来的 XML 回退仍必须使用真实解析器而非正则表达式。
本记录汇总了因移除该包而无法继续保持现行有效、现已删除的仅涉及该包的记录。终端 UI 曾在长对话期间保持会话身份可见、移除重复模型标签、为消息附加耗时与阶段状态、在提示词旁显示 workspace 与分支上下文,并保守地解析完整 XML 包装层,以生成人类可读的回退输出。这些选择改善了一个终端前端,但在没有部署的情况下,并不足以证明应保留它。未来的 XML 回退仍必须使用真正的解析器而非正则表达式。
## 验证
仓库搜索结果与生成目录中不再包含 TUI 包、依赖补丁、SDK 接口选项、服务键或包链接。聚焦的 SDK 测试覆盖 ACP 与嵌入式创建、配置、模板和快照。常规源码构建、类型检查、lint、hygiene、文档门禁以及其余组装快照测试套件均可在没有已删除 workspace 的情况下运行。
仓库搜索结果与生成的服务目录中不再包含 TUI 包、依赖补丁、SDK 接口选项、服务键或包链接。专项 SDK 测试覆盖 ACP 和嵌入式两种模式的创建、配置、模板与快照。常规源码构建、类型检查、lint、hygiene、文档门禁以及其余组装快照测试套件均可在没有已删除 workspace 的情况下运行。
## 考虑过的替代方案
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-08-06-user-bubbles-drop-the-branch-action.md
2026-08-06-user-bubbles-drop-the-branch-action.md: 817b5e72b7e18b03ddb3a160e6f7a86b04f02764
2026-08-06-user-bubbles-drop-the-branch-action.zh.md: dab3890818d872d7bbb3ac9bcab014ce9b829a61
2026-08-06-user-bubbles-drop-the-branch-action.zh.md: a80f723242383cc9a35b9933dcc9c63ebeacd9d8
@@ -1,4 +1,4 @@
# Agent Note:user 与 steering 气泡移除分支操作
# Agent Note: user 与 steering 气泡移除分支操作
Status: implemented
@@ -6,11 +6,11 @@ Status: implemented
## 问题
每个 user 气泡和已消费的 steering(中途引导)气泡都渲染分支控件,受[已完成轮次尾部决策](../bug-fix/2026-08-02-message-fork-actions-require-completed-turn-tail.md)的门禁约束。在这些气泡上,该门禁实际上是永久性的:开轮的 user 消息后面必然跟着本轮自己的节点,已消费的 steering 消息按构造就处在轮次中间,因此只有当轮次结束时该消息之后一个节点都没有——即在第一个模型事件之前就取消——控件才可能启用。读者因此看到一个永远不会启用的控件,tooltip 许诺的是这个按钮到达不了的状态。这个操作入口本身也有误导:在消息 seq 处 fork 会切在所在轮次的 `turn/end`,"在我的消息处分支"实际会把下方的回答一并带走,与在自己气泡上看到分支时"分叉重问"的直觉预期恰好相反。
每个 user 气泡和已消费的 steering(中途引导)气泡都渲染分支控件,受[已完成轮次尾部决策](../bug-fix/2026-08-02-message-fork-actions-require-completed-turn-tail.md)的门禁约束。在这些气泡上,该门禁实际上是永久性的:开轮的 user 消息后面必然跟着本轮自己的节点,已消费的 steering 消息按构造就处在轮次中间,因此只有当轮次结束时该消息之后一个节点都没有——即在第一个模型事件之前就取消——控件才可能启用。读者因此看到一个永远不会启用的控件,tooltip 许诺的是这个按钮到达不了的状态。这个操作入口本身也有误导:在消息 seq 处 fork 会切在所在轮次的 `turn/end`,「在我的消息处分支」实际会把下方的回答一并带走,与在自己气泡上看到分支时「分叉重问」的直觉预期恰好相反。
## 决策
user 与 steering 气泡不再渲染分支操作。`MessageItem` 移除其 fork props,`PendingSteeringBubble` 移除其 `showBranch` 特例,`messageBranchSeqs` 收窄为 `assistantBranchSeqs`:只有已完成轮次的 transcript 尾部、且该尾部是本轮自己的带 text 内容 assistant 节点才可 fork。分支入口只存在于已定稿的回答之下。
user 与 steering 气泡不再渲染分支操作。`MessageItem` 移除其 fork props,`PendingSteeringBubble` 移除其 `showBranch` 特例,`messageBranchSeqs` 收窄为 `assistantBranchSeqs`:只有已完成轮次的 transcript(文本记录)尾部、且该尾部是本轮自己的带 text 内容 assistant 节点才可 fork。分支入口只存在于已定稿的回答之下。
含有 steer 的轮次的 fork 点保持不变:fork 是切在 `turn/end` 上的日志前缀,steer 是子会话必须继承的模型可见历史,因此被引导过的轮次的已定稿回答与其他轮次一样可以 fork。assistant 侧的门禁及其可见但不可用的呈现也保持不变——在回答之下,不可用是一个短暂且可到达的状态(当前尾部被后续工具行或错误行占据),这正是 tooltip 的用武之地。