docs(i18n): reproofread updated Chinese documentation

This commit is contained in:
xjt
2026-08-09 03:20:17 +08:00
parent 0543a6f79d
commit bd659179f6
212 changed files with 315 additions and 315 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/architecture/2026-06-11-content-block-vocabulary.md
2026-06-11-content-block-vocabulary.md: 5228724bb9101307db9929aaf7831b477c2a6022
2026-06-11-content-block-vocabulary.zh.md: ffcbbc13dfe9176941f4838b0078d3850403a16c
2026-06-11-content-block-vocabulary.zh.md: b43de335917c6b5304a94b296fcb8253827b0600
@@ -12,7 +12,7 @@ harness 需要一套统一的内部消息语言,供 agent loop(智能体循
自主拥有词汇:消息是类型化内容块的数组(`text``reasoning``tool-call``tool-result`),其联合类型派生自可合并扩展的 `ContentBlockMap`,插件通过声明合并添加新的块类型。同一可合并扩展映射模式为所有「字符串化」字段提供类型(`MessageSource``FinishReason``TurnTrigger``TurnEndReason`)。流式输出采用原始分片协议;`BlockAssembler` 是唯一的共享组装实现。适配器负责转换为提供方的协议格式(wire format)——映射成本留在适配器中,正是它该在的地方。
会话内上下文注入(`context/message`)和轮次中途 steering 最初渲染为带标签的 user-role 信封(system-reminder 模式),而非引入新角色,因此适配器无需承担额外负担。如今两者都投影为无包装的普通用户内容;见[注入内容信封 Agent Note](../simplification/2026-07-20-unwrap-injected-content-envelopes.md)。实际适配器验证已确认此渲染方式符合当前 DeepSeek 的行为;如果未来某提供方出现不兼容,应在该适配器内处理,而非引入新的规范角色。
会话内上下文注入(`context/message`)和轮次中途 steering(中途引导)最初渲染为带标签的 user-role 信封(system-reminder 模式),而非引入新角色,因此适配器无需承担额外负担。如今两者都投影为无包装的普通用户内容;见[注入内容信封 Agent Note](../simplification/2026-07-20-unwrap-injected-content-envelopes.md)。实际适配器验证已确认此渲染方式符合当前 DeepSeek 的行为;如果未来某提供方出现不兼容,应在该适配器内处理,而非引入新的规范角色。
## 曾考虑的替代方案
@@ -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/architecture/2026-06-11-event-sourced-sessions.md
2026-06-11-event-sourced-sessions.md: 01f9628c1cfc000aca8654caf5edeff09411fdcc
2026-06-11-event-sourced-sessions.zh.md: ec5c3e766dfa97c5612827023c5cc66bf01a8e6c
2026-06-11-event-sourced-sessions.zh.md: 5de230de9e9f1303943b43f6fdeefaf8264aa3bf
@@ -14,7 +14,7 @@ MVP 要求严格的基于事件的追踪,以及完全可回放的会话(严
追加操作是同步的(热路径从不阻塞于 I/O);`session/event` 是同步通知;持久化插件在后台缓冲写入,并在每个轮次结束时触发的 `session/flush` 检查点处等待排空。
顺序约:agent loop(智能体循环)先领取 inbox 消息,再运行 `agent/pre-step`;只有 enter 决策才打开 `step/start`,随后在请求派生前追加返回的 `user/message` 批次。提供方输出组装并以 `assistant/message` 追加后才分派工具,因此持久日志记录工具实际遵循的确切消息。回归测试固定了这一顺序。
顺序约:agent loop(智能体循环)先领取 inbox 消息,再运行 `agent/pre-step`;只有 enter 决策才打开 `step/start`,随后在请求派生前追加返回的 `user/message` 批次。提供方输出组装并以 `assistant/message` 追加后才分派工具,因此持久日志记录工具实际遵循的确切消息。回归测试固定了这一顺序。
## 曾考虑的替代方案
@@ -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/architecture/2026-06-11-microkernel-event-taxonomy.md
2026-06-11-microkernel-event-taxonomy.md: 202595fed125966a5d77920536e7f4ee88f875fe
2026-06-11-microkernel-event-taxonomy.zh.md: 899c96d86cb7e37d90df349ce5f3f932e0a72f95
2026-06-11-microkernel-event-taxonomy.zh.md: 1365910157c36699bfe1fdb3a10aa0785948e0fa
@@ -15,7 +15,7 @@ Status: implemented
- **waterfall(瀑布式事件)**around-middleware):插件可变换、否决、恢复或包装:`agent/pre-step``agent/request``agent/request-error``tools/pre-execute``tools/execute``tools/post-execute``llm/stream``system-prompt/assemble`
- **serial**(按监听器顺序依次 await):用于 `agent/turn-stopping` 等有序检查点。
- **parallel**await 扇出):每个监听器都必须获得独立执行的机会:`session/flush` 持久性检查点。
- **emit**(同步 fire-and-forget):用于 inbox 转换、生命周期、错误,以及包含不可变 `tools/result` 观测的事件。轮次与步骤边界由持久会话事件拥有。
- **emit**(同步 fire-and-forget):用于通知:inbox 转换、生命周期、错误,以及受错误隔离的 `tools/result` 观测;该观测接收不可变的最终结果。轮次与步骤边界由持久会话事件拥有。
事件词汇定义在接口包中(dsh-agent 声明 agent/* 事件);`@deepseek-ai/dsh-agent-loop` 是唯一的具体循环插件,且自身可替换——外部不得依赖它。
@@ -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/architecture/2026-06-14-session-persistence.md
2026-06-14-session-persistence.md: 00e129e57c7144fd62eec26f5854ee21dec4e964
2026-06-14-session-persistence.zh.md: 1c98d5771819e8776d9f5cae4a147d2016ee5978
2026-06-14-session-persistence.zh.md: 6e4e9ece56332ce74b01d60a111617c70e2a874a
@@ -19,11 +19,11 @@ Status: implemented
以下关键选择记录于此,因为它们长期有效、存在争议且出人意料:
- **规范的持久日志无损保留每个 `SessionEvent`,包括 `assistant/chunk`。** JSONL 存储可以将一段连续的增量事件编码为一条打包行,但逻辑读取方会重建精确的事件边界、序号与时间戳。`deriveMessages()` 跳过分片,而过滤分片的方案(Codex 的 `policy.rs`)很有吸引力,但 `seq = log.length` 以及 `events[i].seq === i` 验证要求*连续*的逻辑日志;过滤掉分片会留下空洞,同时破坏约和恢复功能。基于分片过滤的投影可以作为派生视图在后续实现(带有自己的重新编号),但它不是规范日志。
- **仅追加;崩溃的轮次被关闭,而非截断。** 已刷写的事件永不被重写。[语义检查点策略](../bug-fix/2026-07-21-semantic-session-checkpoints.md)会在模型分发前排空请求、在工具分发前排空已记录的顶层调用,并在步骤结束后排空完整的响应/结果批次;循环则排空最终轮次边界。由于一个被中断的轮次可能包含大量有效工作,冷检查会保留其连续、可解析的事件,并在内存逻辑视图中为未应答的 assistant 调用添加按风险分类的错误结果、补一个缺失的 `step/end`,以及带 `{ kind: 'interrupted' }``turn/end``prepare``load` 在返回可恢复视图前提交这些 closer;合成结果保证恢复后的提供方 transcript(文本记录)仍然有效。只有不完整的最后一条记录会在提交修复时被丢弃;在最后一个真实 `turn/end` 处或之前出现解析错误或序号间隙,属于数据损坏,会使该会话不可加载。
- **文件后端为规范实现,数据库后端为经过验证的直接替换。** `SessionEvent` 1:1 映射到一行 `(session_id, seq, type, time, data)``append` 是 INSERT(在一个断言连续 seq 约的事务中),读取使用 SELECT … ORDER BY seq。`dsh-session-persistence-sqlite` 正是如此:一个 `SessionPersistence` 子类,接口无变化(opencode 在 SQLite/WAL 上运行的正是这个形状),且通过与 JSONL 后端相同的 `runPersistenceContract` 测试套件。该约以相同的语义约束两个后端(惰性物化、逻辑关闭中断轮次、修复只提交一次、连续 seq),一次表达在文件字节上,一次表达在数据库行上。其数据库拥有专用的 application id 与单调递增的 schema 版本。系统会在一个事务中为全新文件创建所有表并写入这两个 header 值;未版本化文件若带有任何用户定义的 schema 对象或应用标识、当前版本文件若带有外部应用标识,以及任何非当前版本文件,都会在修改日志模式之前被拒绝。
- **规范的持久日志无损保留每个 `SessionEvent`,包括 `assistant/chunk`。** JSONL 存储可以将一段连续的增量事件编码为一条打包行,但逻辑读取方会重建精确的事件边界、序号与时间戳。`deriveMessages()` 跳过分片,而过滤分片的方案(Codex 的 `policy.rs`)很有吸引力,但 `seq = log.length` 以及 `events[i].seq === i` 验证要求*连续*的逻辑日志;过滤掉分片会留下空洞,同时破坏约和恢复功能。基于分片过滤的投影可以作为派生视图在后续实现(带有自己的重新编号),但它不是规范日志。
- **仅追加;崩溃的轮次被关闭,而非截断。** 已刷写的事件永不被重写。[语义检查点策略](../bug-fix/2026-07-21-semantic-session-checkpoints.md)会在模型分发前排空请求、在工具分发前排空已记录的顶层调用,并在步骤结束后排空完整的响应/结果批次;循环则排空最终轮次边界。由于一个被中断的轮次可能包含大量有效工作,冷检查会保留其连续、可解析的事件,并在内存逻辑视图中为未应答的 assistant 调用添加按风险分类的错误结果、补一个缺失的 `step/end`,以及带 `{ kind: 'interrupted' }``turn/end``prepare``load` 在返回可恢复视图前提交这些收尾事件;合成结果保证恢复后的提供方 transcript(文本记录)仍然有效。只有不完整的最后一条记录会在提交修复时被丢弃;在最后一个真实 `turn/end` 处或之前出现解析错误或序号间隙,属于数据损坏,会使该会话不可加载。
- **文件后端为规范实现,数据库后端为经过验证的直接替换。** `SessionEvent` 1:1 映射到一行 `(session_id, seq, type, time, data)``append` 是 INSERT(在一个断言连续 seq 约定成立的事务中),读取使用 SELECT … ORDER BY seq。`dsh-session-persistence-sqlite` 正是如此:一个 `SessionPersistence` 子类,接口无变化(opencode 在 SQLite/WAL 上运行的正是这个形状),且通过与 JSONL 后端相同的 `runPersistenceContract` 测试套件。该约以相同的语义约束两个后端(惰性物化、逻辑关闭中断轮次、修复只提交一次、连续 seq),一次表达在文件字节上,一次表达在数据库行上。其数据库拥有专用的 application id 与单调递增的 schema 版本。系统会在一个事务中为全新文件创建所有表并写入这两个 header 值;未版本化文件若带有任何用户定义的 schema 对象或应用标识、当前版本文件若带有外部应用标识,以及任何非当前版本文件,都会在修改日志模式之前被拒绝。
- **元数据在日志之外。** 格式版本、cwd 和谱系是存储关注点,不是可回放的对话状态,因此它们存放在 `dsh-session` 拥有的 `SessionHeader` 中,并通过新的只读属性 `session.header` 附加到 `Session` 上——永远不进入 `SessionEventMap`,永远不到达 `deriveMessages()``createdAt` 是以 Unix epoch 毫秒表示的非负安全整数:运行时创建和持久化注册会拒绝小数值,JSONL 会验证解码后的 headerSQLite 则将其存入严格的 `INTEGER` 列。替代方案(一个可合并扩展的 `session/meta` 事件作为日志第 0 行)被否决:日志内事件会自然随 seed/fork 的会话携带,但元数据不是可回放状态,因此显式的日志外 header seam 是更清晰的取舍。(header 最初被拆分为不可变的 `SessionHeader` 加可变的 `SessionSummary`,二者的联合类型为 `SessionMeta`;可变 summary 后来因属于死状态而被移除——见 [移除可变会话摘要](../simplification/2026-06-19-drop-mutable-session-summary.md)。)
- **`ctx.agents.create()``ctx.agents.resume()` 是异步工厂;恢复还跨越持久化边界。** `ctx.agents.resume({ resumeSessionId })` 通过 `ctx.sessionPersistence.prepare()` 取得精确的未发布 Session,以持久化 id 发布,并续其投影。[Session 准备阶段决策](2026-08-05-session-preparation.md)定义历史检查与恢复之间的复用。agent loop(智能体循环)不会硬注入 `sessionPersistence`(那样会让非持久化的演示永远挂起);当它不存在时,`resume` 会以明确的错误拒绝。
- **`ctx.agents.create()``ctx.agents.resume()` 是异步工厂;恢复还跨越持久化边界。** `ctx.agents.resume({ resumeSessionId })` 通过 `ctx.sessionPersistence.prepare()` 取得原本那个尚未发布的会话,以持久化 id 发布该会话,并续其投影。[会话准备决策](2026-08-05-session-preparation.md)负责历史检查与恢复之间的复用。agent loop(智能体循环)不会硬注入 `sessionPersistence`(那样会让非持久化的演示永远挂起);当它不存在时,`resume` 会以明确的错误拒绝。
## 曾考虑的替代方案
@@ -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/architecture/2026-06-18-shared-persistence-write-coordinator.md
2026-06-18-shared-persistence-write-coordinator.md: 12131ebf8380fb8ba816618f0cbaf72cb004623a
2026-06-18-shared-persistence-write-coordinator.zh.md: a11926b602651d8a7dc641f61371e882685b508b
2026-06-18-shared-persistence-write-coordinator.zh.md: 7c04ee921c9b3f965ef6bd6d2d203cf4cbd50ea2
@@ -12,7 +12,7 @@ Status: implemented
将一个后端无关的 `PersistenceCoordinator` 提取到 `dsh-session-persistence` 中。协调器统一拥有编排逻辑;每个第一方后端组合一个协调器实例(`new PersistenceCoordinator(ctx, this)`),实现一个小型 `PersistenceBackend` 钩子接口,并将其有状态的公开方法(`create`/`append`/`prepare`/`load`/`inspect`/`readFrom`)委托给协调器。由后端拥有的元数据与修订版本列举会绕过协调器。
组合,而非继承。协调器是后端持有的具体类,不是后端继承的基类。本 Agent Note 的风险——「协调器不得让非常规后端与继承层级作斗争」——由此规避:后端只暴露钩子,无法触及协调器的私有编排状态。第三方后端仍然可以完全不使用协调器、直接实现抽象服务,包括不可变逻辑检查,以及通过 `load` 实现的默认准备回退。
组合,而非继承。协调器是后端持有的具体类,不是后端继承的基类。本 Agent Note 的风险——「协调器不得让非常规后端与继承层级作斗争」——由此规避:后端只暴露钩子,无法触及协调器的私有编排状态。第三方后端仍然可以完全不使用协调器、直接实现抽象服务,包括不修改状态的逻辑检查,以及通过 `load` 实现的默认准备回退。
协调器为每个存活的 `Session` 实例持有一个生命周期条目:初始化,加上一个包私有写入控制器,后者负责待处理事件、固定批处理截止时间、活跃写入、失败保留和共享 flush 屏障。每个 `session/event` 都进入这条有界写入路径,`session/flush` 则绕过等待以观察完全停稳。控制器归并由 [flush 控制器简化](../simplification/2026-07-23-collapse-persistence-flush-state.md)定义;调度节奏由[有界批处理决策](2026-08-08-bounded-session-persistence-write-batching.md)定义。
@@ -35,7 +35,7 @@ Status: implemented
## 测试
共享的 `runPersistenceContract`(公开 API 约)为每个后端运行,并证明 `inspect` 会配平被中断的逻辑视图但不改变存储或修订版本,随后由 `prepare``load` 提交恢复。`runCoordinatorContract``tests/coordinator-contract.ts`)通过内存参考实现、JSONL 与 SQLite 覆盖接管、HMR、碰撞、会话与后端 dispose 排空和崩溃尾部修复。`persistence.spec.ts``preparations.spec.ts``write-behind.spec.ts` 覆盖准备复用与预留、有界准备状态淘汰、固定窗口后续批次、存活控制器清理、同 id 链尾竞态、失败批次重试与关闭顺序。各后端自身的测试规格只保留存储机制。每个真实后端都有一个经由协调器的崩溃尾部修复测试,以覆盖不透明 marker 分支,因为约中的崩溃用例会产生合成 closers,却不会产生 torn marker。
共享的 `runPersistenceContract`(公开 API 约)为每个后端运行,并证明 `inspect` 会配平被中断的逻辑视图但不改变存储或修订版本,随后由 `prepare``load` 提交恢复。`runCoordinatorContract``tests/coordinator-contract.ts`)通过内存参考实现、JSONL 与 SQLite 覆盖接管、HMR、碰撞、会话与后端 dispose 排空和崩溃尾部修复。`persistence.spec.ts``preparations.spec.ts``write-behind.spec.ts` 覆盖准备复用与预留、有界准备状态淘汰、固定窗口后续批次、存活控制器清理、同 id 链尾竞态、失败批次重试与关闭顺序。各后端自身的测试规格只保留存储机制。每个真实后端都有一个经由协调器的崩溃尾部修复测试,以覆盖不透明 marker 分支,因为约中的崩溃用例会产生合成 closers,却不会产生 torn marker。
## 曾考虑的替代方案
@@ -44,4 +44,4 @@ Status: implemented
## 后果
协调器增加了一层间接、一个不透明的 torn marker、脱离会话生命周期的退役任务,以及有界的已准备 Session 状态,但将此前每个后端重复的、对正确性要求很高的编排逻辑集中到一处。会话 dispose 仍是仅观察事件,因此会话所有者不会等待持久化退役;协调器会收容失败、在存活控制器中保留待处理事件,并以后端 teardown 为完全停稳边界。其钩子面保持窄小:标识校验、接管、碰撞检查、准备与不可变检查共用 `loadStored`;物化保持在 `appendBatch` 内原子完成;列举绕过协调器。读模型使用 `inspect` 而非 `load`,因此观察已持久化但仍开放的轮次时不会提交中断 closers;复用、预留与发布由 [Session 准备阶段决策](2026-08-05-session-preparation.md)定义。新后端只需实现存储原语,而无需复制有界写入生命周期。
协调器增加了一层间接、一个不透明的 torn marker、脱离会话生命周期的退役任务,以及有界的已准备 Session 状态,但将此前每个后端重复的、对正确性要求很高的编排逻辑集中到一处。会话 dispose 仍是仅观察事件,因此会话所有者不会等待持久化退役;协调器会收容失败、在存活控制器中保留待处理事件,并以后端 teardown 为完全停稳边界。其钩子面保持窄小:标识校验、接管、碰撞检查、准备与不修改状态的检查共用 `loadStored`;物化保持在 `appendBatch` 内原子完成;列举绕过协调器。读模型使用 `inspect` 而非 `load`,因此观察已持久化但仍开放的轮次时不会提交中断 closers;复用、预留与发布由 [Session 准备阶段决策](2026-08-05-session-preparation.md)定义。新后端只需实现存储原语,而无需复制有界写入生命周期。
@@ -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/architecture/2026-06-21-bounded-llm-request-recovery.md
2026-06-21-bounded-llm-request-recovery.md: 587f2d26eee922e91c8797018964f983890eb8ec
2026-06-21-bounded-llm-request-recovery.zh.md: be52ece63ce794cb13cdf657b73bae6e9f42cf6d
2026-06-21-bounded-llm-request-recovery.zh.md: 4efe4f946dac90f65f5b8af4a7c36037413616b6
@@ -4,11 +4,11 @@ Status: implemented
[English](2026-06-21-bounded-llm-request-recovery.md) | 中文
[按提供方配置的请求重试策略](../feature/2026-07-24-provider-retry-policies.md)在此基础上增加了确切提供方配置与显式无界 mode。本说明继续负责结构化失败事实、已关闭步骤的恢复边界、normal mode 的暂时性默认值、可见的单次尝试和持久重试状态。[LLM 流的终止失败](2026-07-29-terminal-llm-stream-failures.md)取代了其中关于抛出错误身份和 stream sidecar 的机制。
[按提供方配置的请求重试策略](../feature/2026-07-24-provider-retry-policies.md)在此基础上增加了确切提供方配置与显式无界 mode。本说明继续负责结构化失败事实、已关闭步骤的恢复边界、normal mode 的暂时性默认值、可见的单次尝试和持久重试状态。[LLM 流的终止失败](2026-07-29-terminal-llm-stream-failures.md)取代了其中关于抛出错误身份和流伴随状态的机制。
## 问题
提供方适配器可能在分发或迭代时抛出异常,也可能以 `finish { kind: 'error' | 'aborted' }` 结束。最终适配器边界会在 `dsh-agent-loop` 接收前把抛出值规范化为该终止 finish 协议;middleware 与结果处理缺陷仍会抛出。loop 会将终止模型请求失败交给 `agent/request-error`。未被处理的失败是终态;处理失败的监听器修复策略自有状态,返回 `{ kind: 'retry' }`,并停止 waterfall 委托。[重试动作决策](../simplification/2026-07-27-request-error-retry-action.md)规定这一返回约。
提供方适配器可能在分发或迭代时抛出异常,也可能以 `finish { kind: 'error' | 'aborted' }` 结束。最终适配器边界会在 `dsh-agent-loop` 接收前把抛出值规范化为该终止 finish 协议;middleware 与结果处理缺陷仍会抛出。loop 会将终止模型请求失败交给 `agent/request-error`。未被处理的失败是终态;处理失败的监听器修复策略自有状态,返回 `{ kind: 'retry' }`,并停止 waterfall(瀑布式事件)委托。[重试动作决策](../simplification/2026-07-27-request-error-retry-action.md)规定这一返回约
该边界已能安全地再次发起请求。原始 `assistant/chunk` 事件携带失败的 `turn``step`;除非某条成功的 `assistant/message` 引用这些事件,否则消息派生会忽略它们。只有终止性 finish 成功且组装完成后,系统才会分发工具调用;重试则会从持久日志开启新的编号轮次。因此,harness 无需引入第二套响应生命周期或暂定输出协议,即可分隔两次尝试。
@@ -40,7 +40,7 @@ interface LlmFailure {
`code` 仍是 `HarnessError` 建立的提供方无关机器路由分类体系;新字段是在提供方边界观测到的事实。`ProviderRequestId` 由 `dsh-llm` 拥有并构造,序列化后为提供方发放的字符串。该载荷有意不包含 `retryable`、`failover`、`partialOutput`、提供方、模型、阶段或路由 id 字段。是否可重试属于策略,提供方/模型已位于持久请求头中,部分输出则从失败步骤的 `assistant/chunk` 事件派生。
`LlmError` 携带 `failure: LlmFailure`,并保持 `failure.code === error.code`。`FinishReasonMap.error` 和 `FinishReasonMap.aborted` 携带同一载荷,而不是并行的失败形状。最终适配器边界会从适配器抛出值中分离这些事实,并发出相应的终止 finish;未知 SDK 异常会获得 `UNKNOWN` 载荷。精确的抛出对象身份不会跨越 LLM stream seam。
`LlmError` 携带 `failure: LlmFailure`,并保持 `failure.code === error.code`。`FinishReasonMap.error` 和 `FinishReasonMap.aborted` 携带同一载荷,而不是并行的失败形状。最终适配器边界会从适配器抛出值中分离这些事实,并发出相应的终止 finish;未知 SDK 异常会获得 `UNKNOWN` 载荷。精确的抛出对象身份不会跨越 LLM seam。
agent loop(智能体循环)会将终止 finish 的 `LlmFailure` 传给 `agent/request-error`,并在记录未恢复的 `turn/end.reason` 时使用同一载荷。
@@ -107,7 +107,7 @@ agent-spine 演示组合包加载该插件,因此共享的 stdio/TUI、一次
## 验证
- `LlmFailure` 是适配器抛出、错误 finish 和中止 finish 使用的唯一可序列化载荷;在可用时,规范化保留稳定 code、状态、重试延迟、品牌化的提供方请求 id,以及调用方中止与适配器超时之间的分类。
- 适配器抛出值会在抵达消费方前成为终止失败 chunkmiddleware 与消费方异常仍在模型请求恢复之外抛出。
- 适配器抛出值会在抵达消费方前成为终止失败分片middleware 与消费方异常仍在模型请求恢复之外抛出。
- DeepSeek 和 pi-ai 适配器测试覆盖具有代表性的 400、401/403、429、5xx、连接、格式错误/截断流、超时、中止、Retry-After 秒数/日期、请求 id 和未知 SDK 错误路径,恢复策略无需解析消息文本。
- pi-ai 将 SDK 选项固定为零次重试,并针对可重试的提供方响应执行一次可观测的线路请求尝试;独立测试确保移除任一边界都会失败。
- `agent/request-error` 携带当前失败事实、不可变的先前已重试失败事实,以及实际服务注册所对应的不可变重试策略;成功会清除历史,暂时性失败/上下文溢出交替发生的集成测试证明两种策略只消耗各自的有限预算。
@@ -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/architecture/2026-06-30-event-domain-semantics.md
2026-06-30-event-domain-semantics.md: 14102b105e7bcaa6a00ac9c406933f772fc45a6f
2026-06-30-event-domain-semantics.zh.md: 91e490b57bcdc1ed95f0b8b40cc9e16ef4d36f29
2026-06-30-event-domain-semantics.zh.md: 2607e59490d8ba139bb1289d0f2941894281f6c6
@@ -21,7 +21,7 @@ harness 通过 Cordis 事件分类体系扩展 agent loop(智能体循环)
**三个域,各司其职,以一条边界规则统一。**
- **`session/*`——持久的、可回放的事实日志。** 拥有 `SessionEventMap`;每条记录仅含 JSON(无活对象)。每次追加触发一次 `session/event` emit,加上 `session/flush` 并行持久性检查点。它同时也是实时 transcript(文本记录)源:想渲染或响应已发生事件的消费方在此订阅,因此实时渲染与回放投影共享同一路径。
- **`agent/*`——运行时实时表面。** 始终携带活的 `Agent`。拦截 waterfall(瀑布式事件)(`agent/pre-step``agent/request``agent/request-error`)负责变换、拒绝或恢复;awaited `agent/turn-stopping` 观察停止边界;瞬态 emit 报告生命周期、状态、inbox 插入/领取/丢弃与错误。轮次和步骤边界不在此处——它们是持久的会话事件,从 `session/event` 读取;token 流(`assistant/chunk`)和中途 steering(以 `user/message` 呈现)同理。
- **`agent/*`——运行时实时表面。** 始终携带活的 `Agent`。拦截 waterfall(瀑布式事件)(`agent/pre-step``agent/request``agent/request-error`)负责变换、拒绝或恢复;awaited `agent/turn-stopping` 观察停止边界;瞬态 emit 报告生命周期、状态、inbox 插入/领取/丢弃与错误。轮次和步骤边界不在此处——它们是持久的会话事件,从 `session/event` 读取;token 流(`assistant/chunk`)和作为一条 `user/message` 的轮次中途 steering(中途引导)同理。
- **`tools/*`——工具注册表与执行 seam。**
**边界规则:** 持久的、可回放的事实是 `SessionEvent`;实时拦截或瞬态/活对象信号是 `agent`/`tools` Cordis 事件。轮次或步骤边界是持久事实,因此存在于会话日志中并从 `session/event` 源读取——不会被镜像为 `agent/*` emit。
@@ -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/architecture/2026-07-05-reconstructable-requests.md
2026-07-05-reconstructable-requests.md: ebca9b99cad791159302da9c2bbce9f4df147aab
2026-07-05-reconstructable-requests.zh.md: 91ef3fd04502f2c2f092da60a8bd909cf7fa25df
2026-07-05-reconstructable-requests.zh.md: e7ed83bbc553e86d53d88e442b0fd8992973c9e7
@@ -47,9 +47,9 @@ Status: implemented
## 后果
- 一个日志无法解释的请求不可能被意外构造——无论是循环还是监听器;变异已构建的请求会抛异常;每个 header 变更都是持久的、可 diff 的日志事件。
- 模型可见上下文使用已记录消息通道。`agent.inject()` 与工具 `additionalContexts` 进入 inbox,等待后续领取;必须与当前已领取批次一起结算的上下文由 `agent/pre-step` 返回。每个进入步骤的值都是带来源的持久 `user/message`,只付出一次代价并在后续成为可缓存前缀,代价是会在历史中累积直至压缩。
- 在提供方处仍需全价计算的内容是固有的且已记录的:压缩(其 `compact/*` 事件和替换条目)、真正的提示词、工具或配置变更(reason 为 `change``request/header`),或带漂移的进程边界(不同的 `resume` 快照)。提供方自身的 reasoning-content 排除由服务端管理。
- 模型可见上下文使用已记录消息通道。`agent.inject()` 与工具 `additionalContexts` 进入 inbox,等待后续领取;必须与当前已领取批次一起结算的上下文由 `agent/pre-step` 返回。每个进入步骤的值都是带来源的持久 `user/message`,只付出一次代价,此后即被前缀缓存,代价是会在历史中累积直至压缩。
- 在提供方处仍需全价计算的内容是固有的且已记录的:压缩(其 `compact/*` 事件和替换条目)、真正的提示词、工具或配置变更(reason 为 `change``request/header`),或带漂移的进程边界(不同的 `resume` 快照)。提供方自身对思考内容的排除由服务端管理。
- `agent/pre-step` 是当前请求的消息 seam;直接修改 inbox 则是最终进入后续请求的 seam。
- 工具结果裁剪(计划中)无需新机制:一个已记录的单条目 surface replace`start === end`),携带同一 `callId` 下裁剪后的 `tool/result`——属压缩家族,回放正确,缓存击穿由相同的压力逻辑批量处理。
- 工具结果裁剪(计划中)无需新机制:一个已记录的单条目 surface replace`start === end`),携带同一 `callId` 下裁剪后的 `tool/result`——属压缩家族,回放正确,缓存失效由相同的压力逻辑批量处理。
- 会话日志每个循环实例增长一个 `request/header` 快照,并在真正变更时增加快照。它比 delta 编解码器更大,但相对分片密集型日志仍然很小,并只保留一种回放表示。`SESSION_FORMAT_VERSION` 保持 `0`;旧的 delta 事件被拒绝而非迁移。
- 快照预期输出变更一次(每个 transcript(文本记录)增加其 header 事件);写入文件系统的 fixture(测试前置数据)以规范化的撰写形式存储,工具参数使用 cwd 相对路径,因为回放只对 cwd 无关的参数路径做往返。
@@ -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/architecture/2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md
2026-07-10-after-call-compaction-pressure-and-overflow-recovery.md: 6fbd5e2c9d57da3f25c72c652ca50eb45b84323c
2026-07-10-after-call-compaction-pressure-and-overflow-recovery.zh.md: c46b3eeeb446165192ef44de938c9a5a657a02f8
2026-07-10-after-call-compaction-pressure-and-overflow-recovery.zh.md: 8a480999038cf892842290e3ea3902683d87e431
@@ -22,9 +22,9 @@ Compact-basic 会在每个拟议请求之前包装 `agent/pre-step`。在续步
### 请求恢复只覆盖最终模型边界
`agent/request-error` 表示来自最终适配器边界的终止失败。适配器选择、分发、iterator 构造与迭代抛出会在 agent loop 消费前成为终止 `error``aborted` finish;适配器直接发出的终止 finish 进入同一路径。提示词装配、请求 middleware、请求日志、结果处理、工具、step 监听器与清理仍属于普通失败。[LLM 流的终止失败](2026-07-29-terminal-llm-stream-failures.md)规定这一规范化边界。
`agent/request-error` 表示来自最终适配器边界的终止失败。适配器选择、分发、iterator 构造与迭代抛出会在 agent loop(智能体循环)消费前成为终止 `error``aborted` finish;适配器直接发出的终止 finish 进入同一路径。提示词装配、请求 middleware、请求日志、结果处理、工具、step 监听器与清理仍属于普通失败。[LLM(大语言模型)流的终止失败](2026-07-29-terminal-llm-stream-failures.md)规定这一规范化边界。
恢复运行前,失败 step 已经关闭。负责处理的监听器修复持久状态、返回 `{ kind: 'retry' }`,并停止 waterfall 委托。循环随后关闭失败 turn,并从持久日志开启一个重试 turn,中间不发布空闲通知。重试策略与尝试计数由插件自己拥有;compact-basic 在链路到达终态 `agent/settled` 时清除对应 agent 的溢出计数。两个 DeepSeek 适配器都把识别出的提供方上下文限制错误规范化为 `CONTEXT_WINDOW_EXCEEDED`。[重试动作决策](../simplification/2026-07-27-request-error-retry-action.md)规定这一返回边界。
恢复运行前,失败 step 已经关闭。负责处理的监听器修复持久状态、返回 `{ kind: 'retry' }`,并停止 waterfall(瀑布式事件)委托。循环随后关闭失败 turn,并从持久日志开启一个重试 turn,中间不发布空闲通知。重试策略与尝试计数由插件自己拥有;compact-basic 在链路到达终态 `agent/settled` 时清除对应 agent 的溢出计数。两个 DeepSeek 适配器都把识别出的提供方上下文限制错误规范化为 `CONTEXT_WINDOW_EXCEEDED`。[重试动作决策](../simplification/2026-07-27-request-error-retry-action.md)规定这一返回边界。
如果取消发生在 assistant 工具调用已经持久化之后、所有调用完成分发之前,循环会为每个尚未分发的调用记录一对合成的 `tool/call` 与 aborted `tool/result`,随后进入正常中止路径。因此,表层不会仅因取消赢得竞态而留下孤立的持久工具调用。
@@ -42,7 +42,7 @@ Compact-basic 会在每个拟议请求之前包装 `agent/pre-step`。在续步
## 测试
单元测试覆盖最终适配器规范化边界、已关闭 turn 的重试编号与重置、取消与销毁、step 边界顺序、已路由信封压力、压力门控剪枝、剪枝独立解除压力、从已剪枝输入生成摘要、平衡溢出缩减、后续失败前已落盘的剪枝进展、generation 证明、上限、委托与辅助调用路由。真实循环测试覆盖抛出式和带内溢出,并验证剪枝或摘要压缩后重试请求从替换表层重建
单元测试覆盖最终适配器规范化边界、已关闭 turn 的重试编号与重置、取消与销毁、step 边界顺序、已路由信封压力、压力门控剪枝、剪枝独立解除压力、从已剪枝输入生成摘要、平衡溢出缩减、后续失败前已落盘的剪枝进展、generation 证明、上限、委托与辅助调用路由。真实循环测试覆盖抛出式和带内溢出在经剪枝或摘要压缩后重建重试请求的过程
## 考虑过的替代方案
@@ -58,4 +58,4 @@ Compact-basic 会在每个拟议请求之前包装 `agent/pre-step`。在续步
代价是在共享 pre-step waterfall 中执行压力工作,并需要适配器持续维护溢出分类。提供方措辞与启发式字符密度仍是维护风险。表层压缩依然无法修复仅信封本身就超出窗口的情况,也不能拆分不可分割的非工具节点,或修复非可剪枝剩余部分仍然过大的工具单元。若可移除的文本工具结果是主要体积,可选剪枝器仍可修复原本不可分割的工具配对。
[已领取 pre-step 生命周期](2026-07-31-claimed-pre-step-inbox-lifecycle.md)取代了本记录原先的 post-step 触发方式。服务拆分、独立 token meter、平衡范围约、日志记录锁、摘要替换与唯一 `summarize()` 子类 hook 均保持不变。
[已领取 pre-step 生命周期](2026-07-31-claimed-pre-step-inbox-lifecycle.md)取代了本记录原先的 post-step 触发方式。服务拆分、独立 token meter、平衡范围约、日志记录锁、摘要替换与唯一 `summarize()` 子类 hook 均保持不变。
@@ -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/architecture/2026-07-22-unified-send-and-coalesced-user-messages.md
2026-07-22-unified-send-and-coalesced-user-messages.md: f32d6ca65d5236e1fabdd177cdf54e36929c853f
2026-07-22-unified-send-and-coalesced-user-messages.zh.md: 7647742166bb760139506f93af672f323b3b7b97
2026-07-22-unified-send-and-coalesced-user-messages.zh.md: af829714276b1a77b8147d89a9be1c9ff6a007a5
@@ -18,13 +18,13 @@ agent 的对外驱动接口逐渐长出三个近乎平行的动词——`send`
**context/message 已移除。** 注入的上下文在 inbox 中使用同一个 `UserMessage` 值,并在获准时成为 `user/message` 事件;上下文生产方显式提供合适的非 `user` 类别 `source`,类型化 source 变体携带所有特定于领域的持久来源信息。对外接口、派生逻辑和 `SurfaceEventType` 都不再包含 `context/message`;需要判断“这是不是一条人类提示词?”的消费方改为读取 `source.kind === 'user'`,而不是事件类型。
**Goal 继续执行归属使用正数 Round。** Goal 生命周期状态通过后续 [Goal 自有持久事件决策](2026-07-31-goal-owned-durable-events.md)定义的领域自有 `goal/change` 事件提交。正数 Round 只从已准入的继续执行 `user/message` 推进;goal 持久化不使用注入或 inbox 状态。
**Goal 继续执行归属使用正数 Round。** Goal 生命周期状态通过后续[Goal 自有持久事件决策](2026-07-31-goal-owned-durable-events.md)定义的领域自有 `goal/change` 事件提交。正数 Round 只从已准入的继续执行 `user/message` 推进;goal 持久化不使用注入或 inbox 状态。
**`send` 不返回标识。** 调用方已经持有完整消息及其不透明的 `MessageId`;消息的创建与冻结由[带标识的不可变消息值决策](2026-07-28-identified-immutable-message-values.md)负责,而不是由路由负责。
**Inbox 变更只有一份持久投影和三种最小实时通知。** 每次 append、prepend、编辑、删除、取消与领取都会记录规范化的 `agent/inbox/spliced` 坐标。插入会发出 `agent/inbox/inserted { message }`;普通删除携带持久 `outcome: 'canceled'`,并发出 `agent/inbox/discarded { message }`;循环的原子 `claim()` 会记录纯删除 splice,随后发出 `agent/inbox/claimed { message, turn }``MessageId` 是唯一的单次出现标识,并在两个待处理列表间保持唯一。实时载荷刻意不携带 placement、outcome 或批次封套,因为这些事实由持久 splice 持有。
**pre-step 会领取 next-step 输入,但不会为它单独创建轮次。** steering 和注入始终进入同一个 next-step inboxsteering 会唤醒驱动器,注入则不会。在轮次边界,驱动器会原子领取待处理的 next-step 输入,再领取一条排队提示词;在步骤之间则只领取 next-step 输入。领取会记录纯删除 splice,并针对每条消息发出一次 `agent/inbox/claimed { message, turn }`。随后 `agent/pre-step` 会拒绝拟议步骤,或返回进入步骤的完整批次。reject 与监听器失败都会让已领取批次保持已删除;领取后才到达的输入会等待后续边界。
**pre-step 会领取 next-step 输入,但不会为它单独创建轮次。** steering 和注入始终进入同一个 next-step inboxsteering 会唤醒驱动器,注入则不会。在轮次边界,驱动器会原子领取待处理的 next-step 输入,再领取一条排队提示词;在步骤之间则只领取 next-step 输入。领取会记录纯删除 splice,并针对每条消息发出一次 `agent/inbox/claimed { message, turn }`。随后 `agent/pre-step` 会拒绝拟议步骤,或返回进入步骤的完整批次。拒绝与监听器失败都会让已领取批次保持已删除;领取后才到达的输入会等待后续边界。
**一条已接受消息只保留一种表示。** 持久的用户角色输入和附加的模型可见上下文都直接使用带标识且冻结的 `UserMessage`。循环把该值与私有路由状态存放在一起,不会将其标识、内容或来源复制到另一种公开形状中。steering、注入和工具产生的上下文都会在 next-step inbox 中保留各自带标识的消息。[带标识的不可变消息值决策](2026-07-28-identified-immutable-message-values.md)取代了本记录此前的 `UserMessageData`/`AgentMessage` 层级,并将这一表示扩展到 assistant 消息和工具结果消息。
@@ -41,7 +41,7 @@ agent 的对外驱动接口逐渐长出三个近乎平行的动词——`send`
## 后果
投递接口现在是一个原语加三个自解释的预设,(`target` × `wakeup`) 矩阵把此前无法表达的组合显式化。同一个带标识消息值同时服务提示词、注入的上下文和 Goal Round,因此每一处“是否人类提示词?”检查都简化为一次 `source` 判断。`Agent` 约仍是接口,因此其他实现和对象字面量形式的测试替身只需实现同一个最小结构接口。正数 Goal Round 从已准入的 `user/message` 事件折叠,而 goal 生命周期状态位于投递接口之外。空闲注入会保持待处理,不打开轮次也不运行模型;后续会唤醒的投递在 pre-step 将其放入进入步骤的批次时,它才成为 `user/message`
投递接口现在是一个原语加三个自解释的预设,(`target` × `wakeup`) 矩阵把此前无法表达的组合显式化。同一个带标识消息值同时服务提示词、注入的上下文和 Goal Round,因此每一处“是否人类提示词?”检查都简化为一次 `source` 判断。`Agent`仍是接口,因此其他实现和对象字面量形式的测试替身只需实现同一个最小结构接口。正数 Goal Round 从已准入的 `user/message` 事件折叠,而 goal 生命周期状态位于投递接口之外。空闲注入会保持待处理,不打开轮次也不运行模型;后续会唤醒的投递在 pre-step 将其放入进入步骤的批次时,它才成为 `user/message`
`wakeup` 是“模型是否应当运行”的信号,因此 inbox 会区分能唤醒的排队工作与任何可领取的项:一个孤立的 `next-turn`/no-wakeup 队列项会停泊在空闲状态,并随下一次唤醒 send 一同带出,而 `whenIdle`/`cancel` 依据唤醒信号来结算完全停稳。每次插入与退出都会发布对应的实时通知,特定于领域的持久事实则通过类型化消息 source 传递,而非通过平行的元数据通道。直接使用待处理消息的表示方式,使持久 splice 与实时事件保持可关联,既无需维护第二个 steering 包装层,也避免数据发生分歧。后续的[已领取 pre-step inbox 生命周期](2026-07-31-claimed-pre-step-inbox-lifecycle.md)决策保留通过 `MessageId` 寻址的实时队列变更,并把单消息生命周期通知与持久的整体队列 splice 投影分离。
@@ -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/architecture/2026-07-23-toolview-dissolution.md
2026-07-23-toolview-dissolution.md: be1bd9d161714194855988d76a632c667fae8c84
2026-07-23-toolview-dissolution.zh.md: afe5e03e8345fca2a0f097d86873974f6d417ff2
2026-07-23-toolview-dissolution.zh.md: c3f301002aac4c443dedc76fa747d8b45be6577a
@@ -4,7 +4,7 @@ Status: implemented
[English](2026-07-23-toolview-dissolution.md) | 中文
> 范围:独立工具环(ToolViewRegistry/ctx.toolviews/outlet)为何退役、被什么取代。本决策产出的落地态叙述归 [Web 客户端架构注](2026-07-19-gui-web-client-architecture.md);一切现在所运行其上的注册模型归 [slot 体系标准](2026-07-22-slot-type-chain-implementation.md)所有。后续的 [Client Tool 展示所有权](2026-08-08-client-tool-presentation-ownership.md)决策仅取代本篇的 per-view 放置方式:Tool 名称分发仍使用 keyed slot,而非平行注册表。
> 范围:独立工具环(ToolViewRegistry/ctx.toolviews/outlet)为何退役、被什么取代。本决策产出的落地态叙述归 [Web 客户端架构注](2026-07-19-gui-web-client-architecture.md);一切现在所运行其上的注册模型归 [slot 体系标准](2026-07-22-slot-type-chain-implementation.md) 所有。后续的 [Client Tool 展示所有权](2026-08-08-client-tool-presentation-ownership.md) 决策仅取代本篇的 per-view 放置方式:Tool 名称分发仍使用 keyed slot,而非平行注册表。
## Problem
@@ -14,15 +14,15 @@ Status: implemented
工具环作为独立基础设施已消失:工具行是**各视图为自己声明的 keyed 子槽**,client 全域只剩一种注册模型。上述理由是空的——keyed slot 的 *key 空间*本就运行时开放(SlotMap 声明槽、从不声明 keyask-user composer 的 `key: 'question'` 即先例),开放的 tool 名集合天然适配 `entryKey` 分发。
本决策最初把 `'conversation.chat.toolview'` 放在 chat 条目下,由 chat 渲染点逐行分发。后续的 [Tool 展示所有权](2026-08-08-client-tool-presentation-ownership.md)入整体 Tool 席位,并让 `ui-tool` 拥有唯一的 keyed `'tool.call.toolview'` 子 slot。后续决策改变的是展示所有者,而非本决策的核心约束:Tool 注册继续使用普通 keyed-slot 机制,激活、替换、缓存、错误隔离、版本与 fallback 行为仍归框架所有。
本决策最初把 `'conversation.chat.toolview'` 放在 chat 条目下,由 chat 渲染点逐行分发。后续的 [Tool 展示所有权](2026-08-08-client-tool-presentation-ownership.md) 将该放置方式移入整体 Tool 席位,并让 `ui-tool` 拥有一个 keyed `'tool.call.toolview'` 子 slot。后续决策改变的是展示所有者,而非本决策的核心约束:Tool 注册继续使用普通 keyed-slot 机制,激活、替换、缓存、错误隔离、版本与 fallback 行为仍归框架所有。
## 接受的语义变化
四项行为增量是刻意接受而非疏漏。跨视图出场最初采用逐视图注册;后续 Note 记录了 root/subcall 编排为何足以支持一个 Tool 级展示所有者。同 key 重复注册从注册表的 later-wins 静默覆盖变为 loud throw——纪律修正而非损失。会话维分发若行需要,归组件内部(标配 kit 已带 `useSessions`),不走注册表谓词——今天没有已落地的会话变体样例。第三方在 registry 级覆盖形态(scoped 注册压过 global)不复存在;真出现的未来需求走 key 命名空间约定或组件内小 resolver,永不复活平行注册表。
四项行为增量是刻意接受而非疏漏。跨视图出场最初采用逐视图注册;后续 Note 记录了为何 root/subcall 编排后来证明由一个 Tool 级展示所有者统一负责是合理的。同 key 重复注册从注册表的 later-wins 静默覆盖变为 loud throw——纪律修正而非损失。会话维分发若行需要,归组件内部(标配 kit 已带 `useSessions`),不走注册表谓词——今天没有已落地的会话变体样例。第三方在 registry 级覆盖形态(scoped 注册压过 global)不复存在;真出现的未来需求走 key 命名空间约定或组件内小 resolver,永不复活平行注册表。
## Alternatives considered
**保留独立注册表(原形态)。** 拒绝:其多维分发的每一维都有更正确的家——展示所有权归显式声明的子 slot,会话维归已持有标配 kit 的组件内部。两步移完后剩下的只是一份没有任何独有能力的 slot 机器副本。
**保留独立注册表(原形态)。** 拒绝:其多维分发的每一维都有更正确的家——展示所有权归显式声明的子 slot,会话维归已持有标配 kit 的组件内部。剩下的只是一份没有任何独有能力的 slot 机器副本。
**把 `renderToolView` 提进标配 kit、注册表迁入 runtime 包。** 拒绝:Tool 展示是 Client UI 词汇;上提进 runtime 会把展示概念泄漏进数据对象层,且依然留着两套注册模型。
@@ -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/architecture/2026-07-24-separate-context-injection-from-turn-execution.md
2026-07-24-separate-context-injection-from-turn-execution.md: bb28d96cf1494d94ad7a7d4a4714e536c7072d2f
2026-07-24-separate-context-injection-from-turn-execution.zh.md: fa9b9275cae67f4d5a34dac8a0236558596ab28f
2026-07-24-separate-context-injection-from-turn-execution.zh.md: 6debc6cf5e8fb7358ca8697fa64f175fb700d991
@@ -68,7 +68,7 @@ enter 分支的 `PreStepDecision.messages` 是拟议步骤的完整批次。wate
## 后果
- idle 注入要到后续 pre-step 让它进入步骤后才会对模型可见,并可能被取消或 dispose 丢弃,而其持久 inbox 生命周期仍会保留记录。
- idle 注入只有在后续 pre-step 接纳它后才会对模型可见,并可能被取消或 dispose 丢弃,而其持久 inbox 生命周期仍会保留记录。
- 两条连续的 user-role 消息会取代一条烘焙后的提示词消息;提供方适配器会保留这一顺序。
- 必须影响当前请求的上下文要从 `agent/pre-step` 返回;普通注入只保证由最近的后续边界交付。
- 必须影响当前请求的上下文要从 `agent/pre-step` 返回;普通注入只支持在最近的后续边界交付。
- 公共投递契约和收件箱记录保持精简:没有上下文附件、上下文放置元数据、提示词封套或重复的持久事件类型。
@@ -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/architecture/2026-07-25-web-client-session-scope-and-provide-channel.md
2026-07-25-web-client-session-scope-and-provide-channel.md: 3c51f06fca23a495f0fbc0cc4f1c289edea07b3b
2026-07-25-web-client-session-scope-and-provide-channel.zh.md: 06fc1005785d9d11b52839f91c3bb4b99cad7d63
2026-07-25-web-client-session-scope-and-provide-channel.zh.md: 1319a0d5a4eba4d70b0268605a0957c9a2eca7a9
@@ -4,7 +4,7 @@ Status: implemented
[English](2026-07-25-web-client-session-scope-and-provide-channel.md) | 中文
> 范围:client Agent scopeactx)与定向事件、client/host 实体化对等模型、空会话 blank 位与复用(`connectWorkspace`)、per-session 供数通道(`sessions.provide`),以及承载这些能力的 host wire 小件(summary `blank` 列、`host/session-added` 帧字段、`host/commands-changed` 帧)。输入状态机与 slash 管线见[输入状态机 note](2026-07-25-web-input-machine-and-slash-pipeline.md);命令业务面见[命令业务面 note](2026-07-25-web-command-surfaces-and-assembly.md)。
> 范围:client Agent scopeactx)与定向事件、client/host 实体化对等模型、空会话 blank 位与复用(`connectWorkspace`)、逐会话供数通道(`sessions.provide`),以及承载这些能力的 host wire 小件(summary `blank` 列、`host/session-added` 帧字段、`host/commands-changed` 帧)。输入状态机与 slash 管线见[输入状态机 note](2026-07-25-web-input-machine-and-slash-pipeline.md);命令业务面见[命令业务面 note](2026-07-25-web-command-surfaces-and-assembly.md)。
## 问题
@@ -74,7 +74,7 @@ Session 实例与 scope 同生命周期,存活资格 = host listed(一个判
`workspaces.connectWorkspace(workspaceId): Promise<SessionId>`(归属 WorkspacesService——它同时持有 workspace 规范 path 与 sessions 引用):
- 复用臂:list mirror 中找 `blank && cwd == workspace.path && sessionIds.includes(id)`——host 自己的成员规则,绝不只按 cwd。没有账户槽位的 cwd 匹配(CLI/TUI 在 host cwd 创建的会话,或已删除/重建的注册)会打开一个任何分组表面都无法显示在该工作区下的会话,因此落到新建臂(见[成员复用修复](../bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md));命中直接返回该 id,不新建。
- 复用臂:list mirror 中找 `blank && cwd == workspace.path && sessionIds.includes(id)`——host 自己的成员规则,绝不只按 cwd。没有账户槽位的 cwd 匹配(CLI(命令行界面)/TUI 在 host cwd 创建的会话,或已删除/重建的注册)会打开一个任何分组表面都无法显示在该工作区下的会话,因此落到新建臂(见[成员复用修复](../bug-fix/2026-08-05-workspace-blank-session-reuse-membership.md));命中直接返回该 id,不新建。
- 新建臂:未命中则 `session.create({workspaceId})`,返回新 id。
- 未知 workspaceId fail loud(不静默创建到别处)。
- 解析保证(两臂同契约):promise resolve 时返回的 id 已在 list store 且 `sessions.binding(id)` 同步可解析——`SessionsService.create` 在 RPC 成功后同步投影列表再 resolve,使 draft 搬运方可以在 open 之前往新 scope 的 machine 写文本,不等 notifier flush。
@@ -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/architecture/2026-07-27-dispose-ladder-to-consumer.md
2026-07-27-dispose-ladder-to-consumer.md: e9af88e8e7ef962213a74e96a249241cbe8d5994
2026-07-27-dispose-ladder-to-consumer.zh.md: 89f8e107c56d42787c59bc6f8fa8fc7b3ef73208
2026-07-27-dispose-ladder-to-consumer.zh.md: 74cbc624e9153637dcdceb521c04af811799fdb0
@@ -10,7 +10,7 @@ Status: implemented
## 决策
阶梯移入其唯一消费方。`dsh-subagent-acp` 拥有 `disposeAcpChild(child, eofGraceMs)`,完全构建在 seam 的公开动词之上:关闭 `stdin`,以 `eofGraceMs` 约束一次 `waitForExit`,随后调用 `terminate()`(其 SIGTERM→spec 宽限期→SIGKILL 升级已拥有信号定时器),再无界等待 `waitForExit()`,由子进程责任方证明整棵进程树已经退出。seam 保留 `kill``terminate``waitForExit`——机制而非策略——而 `waitForExit(signal?)` 恰是消费方阶梯在协作层确认进程树真正退出所需的停稳探针,无需从终止宽限期再派生一个定时器。seam 的句柄少了一个方法和一个导出接口。
阶梯移入其唯一消费方。`dsh-subagent-acp` 拥有 `disposeAcpChild(child, eofGraceMs)`,完全构建在 seam 的公开动词之上:关闭 `stdin`,以 `eofGraceMs` 约束一次 `waitForExit`,随后调用 `terminate()`(其 SIGTERM→spec 宽限期→SIGKILL 升级已拥有信号定时器),再无界等待 `waitForExit()`,由子进程所有者证明整棵进程树已经退出。seam 保留 `kill``terminate``waitForExit`——机制而非策略——而 `waitForExit(signal?)` 恰是消费方阶梯在协作层确认进程树真正退出所需的完全停稳探针,无需从终止宽限期再派生一个定时器。seam 的句柄少了一个方法和一个导出接口。
## 曾考虑的替代方案
@@ -20,4 +20,4 @@ Status: implemented
## 后果
买到的:seam 少了一个方法和一个类型;实现只四个动词,不欠拆卸策略;协作式 EOF 时间窗与调节它的 ACP 配置字段住在一起,而终止时间窗与最终的整树退出等待仅由子进程责任方拥有。代价:未来想要 EOF 打头拆卸的后端需针对这些动词写约 20 行(或直接搬 ACP 的辅助函数);阶梯的层级测试位于 ACP 套件,seam 套件转而钉住阶梯所组合的动词(升级前有界 `waitForExit` 返回假,升级后无界等待整棵进程树退出),而非组合后的策略。
换来的是:seam 少了一个方法和一个类型;实现只需提供四个动词,无需提供拆卸策略;协作式 EOF 时间窗与调节它的 ACP 配置字段住在一起,而终止时间窗与最终的整树退出等待仅由子进程所有者拥有。代价:未来想要 EOF 打头拆卸的后端需针对这些动词写约 20 行(或直接搬 ACP 的辅助函数);阶梯的层级测试位于 ACP 套件,seam 套件转而钉住阶梯所组合的动词(升级前有界 `waitForExit` 返回假,升级后无界等待整棵进程树退出),而非组合后的策略。
@@ -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/architecture/2026-07-29-dsh-source-launch-tsx-esm.md
2026-07-29-dsh-source-launch-tsx-esm.md: aabe6c2d6a92b5cc6568eee1d42df3a6998cf0ca
2026-07-29-dsh-source-launch-tsx-esm.zh.md: fdeb8a976e02bb9311a6ec9d097d09151a998a04
2026-07-29-dsh-source-launch-tsx-esm.zh.md: ecda8b273b3efc4773dbe3ed25a9ef0572b79ce4
@@ -4,7 +4,7 @@ Status: implemented
[English](2026-07-29-dsh-source-launch-tsx-esm.md) | 中文
> 取代[已归档的原生 TypeScript 源码启动](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md)Node 移除了该决策所依赖的能力。
> 取代[原生 TypeScript 源码启动](../../archived/architecture/2026-07-28-dsh-native-typescript-source-launch.md)Node 移除了该决策所依赖的能力。
## 问题
@@ -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/architecture/2026-07-29-request-level-llm-config-credentials.md
2026-07-29-request-level-llm-config-credentials.md: 238400ea41f25a716729d1721c113645c2c8ba72
2026-07-29-request-level-llm-config-credentials.zh.md: b0d04d4303bf0ccf5ebc74af8c4a3e493f861d63
2026-07-29-request-level-llm-config-credentials.zh.md: b2075798c9eb77d8fd8baecbbea77993ccda328f
@@ -14,7 +14,7 @@ Status: implemented
**按请求解析,而非重建 fiber。**适配器改为接收一个 options thunk(外加按流调用的凭据解析器),不再持有冻结的构造期事实,每个操作解析一次——即 Pi 的模式,连同其经测试固定的语义:跨越一次变更的两个请求看到两份配置,一个请求恰好解析一次,进行中的流保持其起始事实。这删掉了重建式设计所需的整套切换机制(`DUPLICATE_ADAPTER` 顺序问题、`NO_ADAPTER` 窗口、延迟激活状态机),并把密钥缺失变成*请求时*可行动的失败(`MISSING_CREDENTIAL` 点名每个配置入口),同时路由保持注册、catalog 保持可浏览。唯一在注册期捕获的事实——`ctx.llm` 注册表在 `registerAdapter` 时快照的重试策略(外加 pi-ai 的路由*集合*)——在其变化时于一个同步区段内原地重新注册同一适配器实例。
**机密是引用,值藏在 `ctx.credentials` 背后。**配置(两个面)携带 `apiKeyEnv: DEEPSEEK_API_KEY`;三包凭据 seam 按操作解析它。`credentials-local` 把活跃进程环境(只读、优先——启动时覆盖是操作者意图,必须*可见地*只读,因此被遮蔽的写入直接拒绝而不是表面成功)叠加在 provider 管理的文档之上(可写、重载时整体替换快照使删除的条目绝不滞留——来自 Claude Code 增量重放(additive reapply)的教训)。该文档当时是 dotenv 形式的 `$DSH_HOME/.env`[凭据文档拆分](2026-08-04-credentials-yaml-and-user-environment-layer.md)后来把它移到 `$DSH_HOME/.credentials.yaml`,并让旧路径转为用户的环境层。适配器通过 seam 解析该引用;仅在未挂载 seam 时,才通过各环境层解析。
**机密是引用,值藏在 `ctx.credentials` 背后。**配置(两个面)携带 `apiKeyEnv: DEEPSEEK_API_KEY`;三包凭据 seam 按操作解析它。`credentials-local` 把活跃进程环境(只读、优先——启动时覆盖是操作者意图,必须*可见地*只读,因此被遮蔽的写入直接拒绝而不是表面成功)叠加在提供方管理的文档之上(可写、重载时整体替换快照使删除的条目绝不滞留——来自 Claude Code 增量重放(additive reapply)的教训)。该文档当时是 dotenv 形式的 `$DSH_HOME/.env`[凭据文档拆分](2026-08-04-credentials-yaml-and-user-environment-layer.md)后来把它移到 `$DSH_HOME/.credentials.yaml`,并让旧路径转为用户的环境层。适配器通过 seam 解析该引用;仅在未挂载 seam 时,才通过各环境层解析。
**按插件划分 namespaceschema ≡ `Config`。**每个适配器注册自己的 namespace(`llm-deepseek``llm-pi-ai`),schema 用其插件 `Config` schema,组合 `base` 用其 `cordis.yml` 条目——settings 分节与 entry 配置是同一种 YAML 形状,`resolveAdapterOptions`/`resolveProfiles` 对两者仍是唯一的显式 resolve 步骤。存活快照若违反 schema 之外的约束,则保留最后可用事实(seam 的最后可用值哲学向上延伸一层);entry 配置本身仍会加载失败。pi-ai 的 `providers` 改为以路由为键的字典,base 层与用户层因此按提供方合并,路由集合也由结构直接表达;数组形状响亮失败并给出迁移指引,而空字典是合法的休眠姿态——组合可以裸挂该适配器,把每一条路由都留给用户面决定。
@@ -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/architecture/2026-07-30-credential-boundaries-and-atomic-registration.md
2026-07-30-credential-boundaries-and-atomic-registration.md: 94b32c3cfaa3e1c5059573881a2f393d29aed3ac
2026-07-30-credential-boundaries-and-atomic-registration.zh.md: 2506ce391c9125323faf107e3c04c52785e0cc98
2026-07-30-credential-boundaries-and-atomic-registration.zh.md: 088e24cbfd7db9da73c2ed3e3d11fdef24249c37
@@ -18,7 +18,7 @@ Status: implemented
## 决策
**凭据文档只归凭据提供方所有。**没有任何一个面会把它加载进 `process.env`。当时该文档是 `$DSH_HOME/.env`[凭据文档拆分](2026-08-04-credentials-yaml-and-user-environment-layer.md)后来把它移到 `$DSH_HOME/.credentials.yaml`,因此如今被加载的正是那条旧路径——作为用户的普通环境层,其中不含任何 provider 管理的密钥。真正的启动环境,以及调用目录中由 bin 加载的 `.env`,仍然是那一层只读的环境来源,因此不挂载该提供方的组合,解析密钥的方式与从前完全一致,而存下的密钥跨重启仍然来源于文件、仍然可写——这一点由 Loader 组合中的一次真实重启来证明,而不是靠对 `describe()` 的单元断言。
**凭据文档只归凭据提供方所有。**没有任何一个面会把它加载进 `process.env`。当时该文档是 `$DSH_HOME/.env`[凭据文档拆分](2026-08-04-credentials-yaml-and-user-environment-layer.md)后来把它移到 `$DSH_HOME/.credentials.yaml`,因此如今被加载的正是那条旧路径——作为用户的普通环境层,其中不含任何提供方管理的密钥。真正的启动环境,以及调用目录中由 bin 加载的 `.env`,仍然是那一层只读的环境来源,因此不挂载该提供方的组合,解析密钥的方式与从前完全一致,而存下的密钥跨重启仍然来源于文件、仍然可写——这一点由 Loader 组合中的一次真实重启来证明,而不是靠对 `describe()` 的单元断言。
**存下的凭据对模型没有边界,而 README 就是这么写的。**`0700` 目录下的 `0600` 挡得住其他 OS 用户;模型的 bash 与文件系统工具正是以同一用户身份运行,而已交付的默认值不约束任何东西。harness 真正守住的更窄,也就照这个宽度写下来:没有任何一个面会把该文档提升进 `process.env`,模型也从不会拿到它的解析后路径,因此要拿到这个值,需要刻意去读一条并未交给它的路径。OS 钥匙串(keychain)提供方——一个模型的进程根本读不到的存储——被记录为真正的答案,而不是靠一个残缺的方案去暗示它。
@@ -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/architecture/2026-07-30-web-config-plane.md
2026-07-30-web-config-plane.md: 483058c8a24ad8c414278eba84f4226e973f2923
2026-07-30-web-config-plane.zh.md: 9247aac52401cd570d37f4aad47d9da6bb194d19
2026-07-30-web-config-plane.zh.md: 840ac7f2e46c82e7b858c353086e1a9541db563e
@@ -12,7 +12,7 @@ PR1 让 LLM(大语言模型)适配器配置在 seam 层面免重启,但唯
## 决策
**wire 领域挂上编译期 RPC 映射,拒绝落为错误码,失效落为帧。**`settings.describe/openDocument/update/replace/mutate``credentials.describe/set/unset``llm.providers``llm.models`(认领预留的 `host.listModels` 面)一同加入 `RpcMethodMap`,七处由编译器锁定的接线位点因此让约、schema、处理器与客户端保持步调一致。seam 侧的拒绝折叠为 `settings-rejected {ns}`/`credential-rejected {ref}` 业务错误(HTTP 仍只是载体),三个 `HostFrame`——`host/settings-changed {ns}``host/credentials-changed {ref}``host/models-changed`——沿用 `host/commands-changed` 的形状,因此每个客户端都无需轮询即可收敛。settings 读取、原生操作与写入和 `pickDirectory`/`openPath` 一起进入连接守卫的特权集合:回环 + 同源,否则 403,因为暴露在局域网上的 dsh web 绝不能接受来自其他源的配置访问。
**wire 领域挂上编译期 RPC 映射,拒绝落为错误码,失效落为帧。**`settings.describe/openDocument/update/replace/mutate``credentials.describe/set/unset``llm.providers``llm.models`(认领预留的 `host.listModels` 面)一同加入 `RpcMethodMap`,七处由编译器锁定的接线位点因此让约、schema、处理器与客户端保持步调一致。seam 侧的拒绝折叠为 `settings-rejected {ns}`/`credential-rejected {ref}` 业务错误(HTTP 仍只是载体),三个 `HostFrame`——`host/settings-changed {ns}``host/credentials-changed {ref}``host/models-changed`——沿用 `host/commands-changed` 的形状,因此每个客户端都无需轮询即可收敛。settings 读取、原生操作与写入和 `pickDirectory`/`openPath` 一起进入连接守卫的特权集合:回环 + 同源,否则 403,因为暴露在局域网上的 dsh web 绝不能接受来自其他源的配置访问。
**`describe()` 增加分层与结构化 secret 脱敏。**`SettingsDescriptor` 在生效值之外携带 `base`/`user`,表单据此按「字段是否出现在用户层」来标记「已覆盖」,而非按值是否不等(与 base *相等*的覆盖仍然是覆盖)。`describe({ redactSecrets: true })`——在每个 wire 面都强制启用——经由对 schema 的纯结构遍历(object/dict/array 容器;secret 角色子树整体是一个不透明叶节点)从全部三层剥除 `role('secret')` 子树,并把剥除的槽位枚举为 `{path, set}`,页面因此不必收到任何值就能渲染只写输入框。
@@ -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/bug-fix/2026-07-21-compaction-summary-prefix-cache-reuse.md
2026-07-21-compaction-summary-prefix-cache-reuse.md: fb6524846f837b783423b3ed0e043cd4e1f7849d
2026-07-21-compaction-summary-prefix-cache-reuse.zh.md: e6072313b7089b9dda14c298fcb94db786220501
2026-07-21-compaction-summary-prefix-cache-reuse.zh.md: 96adc31d6d6c50b4e6bfee32a5b89388a33a6939
@@ -42,4 +42,4 @@ Status: implemented
- **单元:** `compact-basic.spec.ts` 断言辅助调用转发 `system`/`tools`/前导消息,并把压缩指令作为最后一条消息追加,且 `compactRegion` 回放最新的已路由 header 前缀。现有的内容断言通过回放的消息而非 transcript 字符串来读取摘要器输入。
- **循环:** `compact-loop-repro.spec.ts` 依据摘要请求尾部 user 消息中的压缩指令对其分类,溢出恢复测试则继续在真实循环中固定对话请求与摘要请求的数量。
- **快照:** 无密钥回放会从带标记的 `compact/summary` 重建一条规范成功流;[compaction-seam Agent Note](../feature/2026-06-18-compaction-capability-seam.md) 负责持久标记约。
- **快照:** 无密钥回放会从带标记的 `compact/summary` 重建一条规范成功流;[compaction-seam Agent Note](../feature/2026-06-18-compaction-capability-seam.md) 负责持久标记约
@@ -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/bug-fix/2026-07-21-semantic-session-checkpoints.md
2026-07-21-semantic-session-checkpoints.md: 697d878dccbfab1ded9f73e134d594ba6f0665e1
2026-07-21-semantic-session-checkpoints.zh.md: c6184b0b00db16f9e318f4cbd148b1f0525f03a5
2026-07-21-semantic-session-checkpoints.zh.md: e4d432cfa4655fcf6b010ab944dc12095eca009c
@@ -10,7 +10,7 @@ Status: implemented
## 决策
`dsh-session-checkpoint-policy` 以零配置插件的形式与持久化后端共同加载,并负责语义持久性屏障。在 `agent/pre-step` 时,该插件会在推导下一个请求前刷新待持久化的提示词输入或前一批响应/结果。该插件惰性包装 `llm/stream`,在记录 `request/header` 之后、构造适配器流之前,刷新活动会话。该插件还在有序的执行前策略之后包装顶层 `tools/execute`,在进入工具主体前刷新已记录的 `tool/call`;嵌套分发则复用外层模型可见调用。循环的最终 `turn/end` 检查点仍是轮次的收尾边界,并会在处理另一个已排队轮次或观察到空闲状态之前完成。
`dsh-session-checkpoint-policy` 以零配置插件的形式与持久化后端共同加载,并负责语义持久性屏障。在 `agent/pre-step` 时,该插件会在推导下一个请求前刷新待持久化的提示词输入或前一批响应/结果。该插件惰性包装 `llm/stream`,在记录 `request/header` 之后、构造适配器流之前,刷新当前会话。该插件还在有序的执行前策略之后包装顶层 `tools/execute`,在进入工具主体前刷新已记录的 `tool/call`;嵌套分发则复用外层模型可见调用。循环的最终 `turn/end` 检查点仍是轮次的收尾边界,并会在处理另一个已排队轮次或观察到空闲状态之前完成。
持久化与检查点调度仍是相互独立的 Cordis 插件。后端使请求的 `session/flush` 边界持久化,但不选择边界;只加载后端而不加载本策略仍是有效组合,并保留循环提供的较粗检查点。第一方持久化应用与运行时会显式加载两者,专用部署则可以有意省略或替换本策略。注册顺序决定其他 `agent/pre-step` 监听器追加的事件是否先于本检查点;提示词输入以及前一批由循环自身记录的助手消息与有序结果都已在日志中。
@@ -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/feature/2026-06-15-code-mode.md
2026-06-15-code-mode.md: 99bbed3edab32512f88ece9694d6519a1f89c2dd
2026-06-15-code-mode.zh.md: ca1bbe9ed3e412186763d1ed4fca9ed06669d4c3
2026-06-15-code-mode.zh.md: d6f0887f421296818b6bba7a26fc662c246cc28a
@@ -48,7 +48,7 @@ Cloudflare 的 [Code Mode](https://blog.cloudflare.com/code-mode/) 提出了一
**子调用上下文通过父调用延后。**`run_code` 内部注入会破坏父调用/结果的相邻性,因此 `ToolRunContext.deferContext()` 按分发顺序收集每个子结果的 `additionalContexts` 条目。即使程序后来抛出异常,注册表仍携带该数组;循环只在外层结果与步骤中所有兄弟结果之后追加每个条目。外层 post-execute 阻止会丢弃工具延后的条目,只暴露阻止 decision 显式附加的上下文。
**并发是有界的,而非被序列化。** 每次 run 拥有一个分发队列,严格按提交顺序启动调用,并通过 `registry.executionMode` 对每个调用分类——与原生循环所用的 fail-closed `isConcurrencySafe` 约相同。连续的 parallel 类调用最多重叠 `maxParallelSubCalls` 个(默认 10;设为 `1` 恢复串行分发);exclusive 类调用会排空池并单独运行。结算时放弃尚未开始的排队调用。本 note 交付的是被序列化的占位实现;取代它的调度器由[实时并行 Agent Note](2026-07-26-code-mode-live-parallel-dispatch.md) 负责。
**并发是有界的,而非被序列化。** 每次 run 拥有一个分发队列,严格按提交顺序启动调用,并通过 `registry.executionMode` 对每个调用分类——与原生循环所用的 fail-closed `isConcurrencySafe`相同。连续的 parallel 类调用最多重叠 `maxParallelSubCalls` 个(默认 10;设为 `1` 恢复串行分发);exclusive 类调用会排空池并单独运行。结算时放弃尚未开始的排队调用。本 note 交付的是被序列化的占位实现;取代它的调度器由[实时并行 Agent Note](2026-07-26-code-mode-live-parallel-dispatch.md) 负责。
**呈现。** `run_code` 的 render intent 按[呈现意图 Agent Note](../architecture/2026-07-02-tool-render-intent-union.md)在此决定:`presentCall` 创建一个 `generic` 卡片,`kind: 'execute'`,以程序文本作为标题,并将同一程序文本作为 `rawInput``run_code` 有意不声明 `presentResult`,因此 TUI 和宿主/客户端运行时(Web)会通过通用原始内容回退机制,使用最终持久化的 `tool/result.content` 补全该卡片,其中包括捕获的日志,以及返回值、失败信息或 post-policy 输出落盘预览。这不是 `terminal` 卡片:该卡片的语义是「工作目录中的 shell 命令」,程序不是。参见[结果卡片完整性说明](../../archived/bug-fix/2026-07-20-code-mode-result-card-completeness.md)。
@@ -85,7 +85,7 @@ worker 运行时只能约束程序的运行,而不构成安全边界:模型
### 模型看到的内容
SDK 指示模型编写一个所加载运行时语言的异步函数体(默认可擦除 TypeScriptPython 运行时下为 Python `async` 函数体——见[语言分发 note](2026-07-31-code-mode-language-dispatch.md)),通过 `await tools.name(args)` 调用工具,在需要时捕获被拒绝的工具调用,并仅 return 或 log 应重新进入上下文的输出。两种 flavor 用各自的原语陈述同一约:相互独立的只读调用可以在 `Promise.all`TypeScript)或 `asyncio.gather`(Python)下重叠,有副作用的调用按提交顺序单独运行,有依赖的工作用 `await` 排序。声明前缀可能与原生 schema 一样大,尤其在 `'both'` 下,但对提供方缓存保持稳定。
SDK 指示模型编写一个所加载运行时语言的异步函数体(默认可擦除 TypeScriptPython 运行时下为 Python `async` 函数体——见[语言分发 note](2026-07-31-code-mode-language-dispatch.md)),通过 `await tools.name(args)` 调用工具,在需要时捕获被拒绝的工具调用,并仅 return 或 log 应重新进入上下文的输出。两种 flavor 用各自的原语陈述同一约:相互独立的只读调用可以在 `Promise.all`TypeScript)或 `asyncio.gather`(Python)下重叠,有副作用的调用按提交顺序单独运行,有依赖的工作用 `await` 排序。声明前缀可能与原生 schema 一样大,尤其在 `'both'` 下,但对提供方缓存保持稳定。
## 后果
@@ -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/feature/2026-06-18-compaction-capability-seam.md
2026-06-18-compaction-capability-seam.md: 390d091fd6e6f7fa1694ba78b90522b70948fe92
2026-06-18-compaction-capability-seam.zh.md: d72793e6adde911a8623ac1c6b65df899381923e
2026-06-18-compaction-capability-seam.zh.md: 59eb899acae0352a019cfc5252365e0116f84597
@@ -33,7 +33,7 @@ Status: implemented
早期草案将完整算法(保留遍历、token 求和、文本提取)作为接口上的具体方法。这会将契约重新耦合到一种策略:想要不同保留策略或事件排序的后端必须与继承来的具体代码对抗。将三个操作都设为抽象,把所有*怎么做*的决策放在后端,并让接口保持为*做什么*的声明。token 测量根本不是压缩钩子;单例服务使多个消费方能够共享逐会话的回放折叠。
`compactIfNeeded(agent, trigger, signal)` 接受显式的 `'pressure' | 'context-overflow'` 触发原因与取消信号。它只读取最新的持久化已路由请求;没有 header 就不执行工作,任何已路由的提供方/模型目标都使用单例估算器。`compactNow(agent, signal)` 要求 agent 处于 idle,即使未达到压力也进行一次有效的平衡缩减;不存在这种范围时返回 `null`,且不写入任何内容。`compactRegion(start, end, agent, signal?)``agent.session` 作为唯一会话身份,并为显式调用方保留可选 signal。默认摘要器依次从显式配置、最新记录的已路由目标和 agent 选项解析目标,并在任何 `llm/stream` 路由后记录提供方/模型对。它回放已路由请求的前缀,并将压缩指令追加为尾部 user 消息,从而复用提供方的热 KV cache;见[摘要前缀缓存 Agent Note](../bug-fix/2026-07-21-compaction-summary-prefix-cache-reuse.md)。该结果携带 `llmStreamCall: true`,因为生成它时恰好通过此上下文的 LLM 服务发起了一次调用;只有满足相同条件时,子类才设置该标记,因为单有保留的 `rawOutput` 并不能判定调用路径。该调用将提供方无关的 `GenerateOptions.purpose` 设为 `compaction`;适配器可以将此用途映射为对模型隐藏的传输元数据,DeepSeek 适配器会发送 `x-deepseek-harness-compact: 1`
`compactIfNeeded(agent, trigger, signal)` 接受显式的 `'pressure' | 'context-overflow'` 触发原因与取消信号。它只读取最新的持久化已路由请求;没有 header 就不执行工作,任何已路由的提供方/模型目标都使用单例估算器。`compactNow(agent, signal)` 要求 agent 处于 idle,即使未达到压力也进行一次有效的平衡缩减;不存在这种范围时返回 `null`,且不写入任何内容。`compactRegion(start, end, agent, signal?)``agent.session` 作为唯一会话身份,并为显式调用方保留可选 signal。默认摘要器依次从显式配置、最新记录的已路由目标和 agent 选项解析目标,并在任何 `llm/stream` 路由后记录提供方/模型对。它回放已路由请求的前缀,并将压缩指令追加为尾部 user 消息,从而复用提供方的热 KV Cache;见[摘要前缀缓存 Agent Note](../bug-fix/2026-07-21-compaction-summary-prefix-cache-reuse.md)。该结果携带 `llmStreamCall: true`,因为生成它时恰好通过此上下文的 LLM 服务发起了一次调用;只有满足相同条件时,子类才设置该标记,因为单有保留的 `rawOutput` 并不能判定调用路径。该调用将提供方无关的 `GenerateOptions.purpose` 设为 `compaction`;适配器可以将此用途映射为对模型隐藏的传输元数据,DeepSeek 适配器会发送 `x-deepseek-harness-compact: 1`
### 成功的持久步骤工作完成后运行自动压力检查
@@ -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/feature/2026-06-24-workspace-context.md
2026-06-24-workspace-context.md: 53a580aee50752b7c6daeff5caa42ba6409c8885
2026-06-24-workspace-context.zh.md: dc1d6b5dc2c9f576e7298c891a3a1145f123a8db
2026-06-24-workspace-context.zh.md: 79e5b0014801878595d3a5d71b8c4b23c5aad996
@@ -14,7 +14,7 @@ Status: implemented
## 决策
该实现在 `packages/context/workspace-context` 中,包package名为 `@deepseek-ai/dsh-workspace-context`。它是请求上下文扩展,不是核心服务或文件系统后端。共享 demo 主干与 Host Runtime 根据显式的 `{ maxBytes } | false` 部署选择挂载它;`dsh web` 启用 65,536 字节预算,Host Runtime 的 headless 消费方则禁用它。该插件使用 `agent/pre-step`、不可变的 `tools/result` 结果、`session/event` 边界和可选的 `ctx.fs` 功能。
该实现在 `packages/context/workspace-context` 中,包名为 `@deepseek-ai/dsh-workspace-context`。它是请求上下文扩展,不是核心服务或文件系统后端。共享 demo 主干与 Host Runtime 根据显式的 `{ maxBytes } | false` 部署选择挂载它;`dsh web` 启用 65,536 字节预算,Host Runtime 的 headless 消费方则禁用它。该插件使用 `agent/pre-step`、不可变的 `tools/result` 结果、`session/event` 边界和可选的 `ctx.fs` 功能。
插件不会静态注入 `fs`。因此,不带提供方的产品树仍能正常启动;在文件系统提供方出现之前,插件保持无操作。所有生产读取都通过该提供方完成。候选项探测会解析每个路径并对结果执行 stat,因此会跟随最终路径组件的符号链接至其目标:指向普通文件的链接会被加载,缺失路径或非文件目标则确认为不存在。允许仓库拥有的链接跨越信任边界,是对最初不跟随探测方式的刻意反转;[跟随指令符号链接记录](2026-07-21-follow-instruction-symlinks.md)负责说明该决策及其残余风险。步骤信号与动态工具执行信号会贯穿解析、元数据探测和流式读取,因此取消不会等待无关的文件系统扫描。解析或 stat 异常归类为不可用:它只跳过该候选项,绝不被解释为已经加载的作用域被删除。
@@ -78,7 +78,7 @@ shell 命令不会触发发现。本地 bash 调用会启动全新的 shell,
## 后果
工作区指引按会话隔离,并由 demo 入口、Web Host 与每一种工具展示模式共享。初始、嵌套与变更指令都保持持久且可回放。通用的会话/agent 上下文约通过先在 inbox 中暂存、再持久进入的 user 消息携带带类型的来源数据,而不会把条目展平。
工作区指引按会话隔离,并由 demo 入口、Web Host 与每一种工具展示模式共享。初始、嵌套与变更指令都保持持久且可回放。通用的会话/agent 上下文约通过先在 inbox 中暂存、再持久进入的 user 消息携带带类型的来源数据,而不会把条目展平。
仓库文本仍是不受信任的输入。低权威 user 角色框架、显式优先级说明和分隔符转义可以降低风险,但无法消除提示词注入。跟随候选符号链接到目标,会把该攻击面扩大至树外内容;因此,把 `ctx.fs` 限制在可信根目录内的权限与沙箱层才是真正的边界,它们让系统把工作区文件当作数据而不是权威([跟随指令符号链接记录](2026-07-21-follow-instruction-symlinks.md)负责说明残余风险)。
@@ -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/feature/2026-06-29-todo-write-tool.md
2026-06-29-todo-write-tool.md: c4cf64b7876bd8b80df80fe6fc27005715f96b5e
2026-06-29-todo-write-tool.zh.md: 1f797c5517df8affd66addecf2a8db43c1da3332
2026-06-29-todo-write-tool.zh.md: 5efcaf1f6e7ea69b1dd8cd0fd31b9cbd931666db
@@ -6,7 +6,7 @@ Status: implemented
## 问题
harness 为模型提供了 bash 和 subagent 工具,却没有办法记录结构化的任务列表。todo 列表有两个同等重要的用途:引导模型规划多步骤工作并保持当前活跃任务明确;同时为交互式宿主提供实时进度清单。调研的所有参考编码 agent(智能体),包括 claude-code、opencode、codex、oh-my-pi 和 pi,都提供了某种形式的此功能;本 harness 此前没有。
harness 为模型提供了 bash 和 subagent 工具,却没有办法记录结构化的任务列表。todo 列表有两个同等重要的用途:引导模型规划多步骤工作并保持当前活跃工作明确;同时为交互式宿主提供实时进度清单。调研的所有参考编码 agent(智能体),包括 claude-code、opencode、codex、oh-my-pi 和 pi,都提供了某种形式的此功能;本 harness 此前没有。
## 决策
@@ -34,7 +34,7 @@ claude-code V1 的条目是 `{ content, status, activeForm }`;后来(V2
### 校验:低成本的中间路线
schema 强制 type/required/enum。在此之上,`execute` 拒绝为空或重复的 `content`,并在 `allowParallelInProgress``false` 时拒绝超过一个活跃任务。排序和保持列表最新仍通过工具描述交给模型。被拒绝的写入返回 `isError` 结果,使模型自行修正。必的部署策略及持久日志不变式独立于该策略这一约定,由[并行 in-progress Agent Note](2026-07-26-todo-parallel-in-progress.md) 负责。
schema 强制 type/required/enum。在此之上,`execute` 拒绝为空或重复的 `content`,并在 `allowParallelInProgress``false` 时拒绝超过一个活跃任务。排序和保持列表最新仍通过工具描述交给模型。被拒绝的写入返回 `isError` 结果,使模型自行修正。必须采用的部署策略,以及持久不变式独立于该策略这一点,均由[并行 in-progress Agent Note](2026-07-26-todo-parallel-in-progress.md)负责。
## 为何没有 cordis-catalog 条目 / 没有 `@mode`
@@ -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/feature/2026-06-30-hook-bridges.md
2026-06-30-hook-bridges.md: a31116818c57e18d04e656f371efd1101a63b6af
2026-06-30-hook-bridges.zh.md: 8827408b67e2dd19f867a8658309f00231ba06c5
2026-06-30-hook-bridges.zh.md: 0fa76c0cee0f74101e70f44d442daa2fb46e6649
@@ -37,7 +37,7 @@ CC 桥接的 `ask` 结果是一条真正的权限路径,而非终态桥接决
每个桥接的 `inject()` 和 additional-context 输入都显式传入 `{ kind: 'plugin', plugin: 'hooks-claude' | 'hooks-codex' }`。单元测试固定验证结果中的 `user/message.source` 为插件而非用户。
`UserPromptSubmit``turn/start` 之后的 pre-step 运行,因此每次调用都会写入轮次范围的 `hook/invoked` / `hook/result` 对。reject 会让已领取输入保持删除,将轮次关闭为 blocked 且不包含步骤,并保留该 hook 对作为持久决策证据。Codex payload 会收到这个已打开轮次的 `turn_id`
`UserPromptSubmit``turn/start` 之后的 pre-step 运行,因此每次调用都会写入轮次范围的 `hook/invoked` / `hook/result` 对。拒绝会使已领取输入维持移除状态,将轮次关闭为已阻止状态且不包含步骤,并保留该钩子对作为持久决策证据。Codex payload 会收到这个已打开轮次的 `turn_id`
### 添加上下文不是否决——先 delegate,再 prepend
@@ -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/feature/2026-06-30-hook-protocol-lib.md
2026-06-30-hook-protocol-lib.md: 2edff0d501cd7695873c68054eeb3a1e9942eced
2026-06-30-hook-protocol-lib.zh.md: 42e526fdf8073465d5f6faf99a6c92fbe09505f5
2026-06-30-hook-protocol-lib.zh.md: e9ae51f5d7697017ecc153866bbb8c3f6fd34eb6
@@ -21,7 +21,7 @@ Status: implemented
- **合并** — `mergeHookOutputs(outputs)`,将多个匹配钩子的输出折叠为一个最严格的 `MergedHookOutcome`:权限优先级 **deny > ask > allow**halt 在首个 `continue:false` 时粘滞,阻止原因以 `\n\n` 拼接,context/system-messages 按序累积。
- **`hook/*` 会话事件** — `hook/invoked` / `hook/result`,通过声明合并进入 `SessionEventMap`(仅日志,如 `compact/*`——不是 `SurfaceEventType`),配有 `appendHookInvoked`/`appendHookResult` 辅助函数,确保 invoked/result 配对与由所有者定义的执行关系在各桥接插件间保持一致。`appendHookResult` 还负责定义持久化记录的语义:decision 字符串(钩子解析出的 decision,否则 `continue:false` 时为 `'stop'`,否则为 `'pass'`)和 500 字符的 `stderrSummary` 截断均从本库的 `HookOutput` 派生,而非各桥接插件各自实现。
**方言专属(桥接插件):** 构建每个事件的 stdin payloadCC 的 base+per-event 字段集 vs Codex 的 snake_case 加 `turn_id`/`model` 额外字段)、方言的 env 与 `${CLAUDE_PLUGIN_ROOT}` 替换(CCvs 无替换Codex),以及将方言无关的 `HookOutput`/`MergedHookOutcome` 映射为 harness seam 专属的类型化 Decision`PreToolDecision``PreStepDecision``ContinuationDecision``PostToolDecision`)。
**方言专属(桥接插件):** 构建每个事件的 stdin payloadCC 的 base+per-event 字段集 vs Codex 的 snake_case 加 `turn_id`/`model` 额外字段)、方言的 env 与 `${CLAUDE_PLUGIN_ROOT}` 替换(CCvs 两者皆无(Codex),以及将方言无关的 `HookOutput`/`MergedHookOutcome` 映射为 harness seam 专属的类型化 Decision`PreToolDecision``PreStepDecision``ContinuationDecision``PostToolDecision`)。
## 曾考虑的替代方案
@@ -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/feature/2026-07-16-durable-per-step-time-context.md
2026-07-16-durable-per-step-time-context.md: e1a5c65894913ad93f46db8ae45e5ef5ead215f3
2026-07-16-durable-per-step-time-context.zh.md: 9cbf653f5d43060e99502b640eed7907ffbd3840
2026-07-16-durable-per-step-time-context.zh.md: d353d2b4535cd2f6153adad084b4ca056f68dc8c
@@ -12,9 +12,9 @@ Status: implemented
## 决策
`@deepseek-ai/dsh-time-context` 是位于 `packages/context/time-context/`、需要显式启用的函数插件。`context/` 分组容纳有界的请求上下文增强,这些增强既不定义工具也不定义服务;已交付示例不挂载此插件,因为时区披露与 token 成本属于部署策略。它注册一个前置的 `agent/pre-step` 监听器;需要读数且下游决策 enter 时,返回一条额外的 `UserMessage`。该消息携带来源 `{ kind: 'plugin', plugin: 'time-context' }`受间隔抑制、reject 或失败的尝试不会追加任何内容。
`@deepseek-ai/dsh-time-context` 是位于 `packages/context/time-context/`、需要显式启用的函数插件。`context/` 分组容纳有界的请求上下文增强,这些增强既不定义工具也不定义服务;已交付示例不挂载此插件,因为时区披露与 token 成本属于部署策略。它注册一个前置的 `agent/pre-step` 监听器;当应生成读数且下游决策为进入时,返回一条额外的 `UserMessage`。该消息携带来源 `{ kind: 'plugin', plugin: 'time-context' }`被抑制、被拒绝或失败的尝试不会追加任何内容。
监听器在 `step/start` 之前采样,并在最终 enter 决策中结算读数。AgentLoop 会在 `step/start` 之后、请求派生之前记录它。因此,下游 reject 或失败会阻止读数进入持久历史。
监听器在 `step/start` 之前采样,并在最终决定进入时确定该读数。AgentLoop 会在 `step/start` 之后、请求派生之前记录它。因此,下游拒绝或失败会阻止读数进入持久历史。
省略可选配置 `timeZone` 时,插件在加载时解析一次 Node 进程的 IANA 时区;显式值由 `Intl.DateTimeFormat` 校验。时间戳包含数字 UTC 偏移和解析后的 IANA 时区。
@@ -44,7 +44,7 @@ Elapsed since the preceding step context: <duration-or-unavailable>.
每个读数都作为普通表层节点保留,直至压缩将其隐藏;正数间隔调度绝不会移除已有读数。因此,后续请求会看到影响先前准备过程和步骤且尚未被隐藏的累计读数,而不是一个被原地改写的系统提示词值。
插件不向系统提示词组装贡献任何内容。`request/header` 不包含时间上下文文本;请求重建从每个 `step/start` 取得完整的持久表层前缀。读数与请求无需一一对应,因为间隔抑制可以让请求进入步骤而不追加读数,reject 或失败则两者都不追加。插件通过 agent 注册表使用生命周期监听器,运行时不需要系统提示词服务。
插件不向系统提示词组装贡献任何内容。`request/header` 不包含时间上下文文本;请求重建从每个 `step/start` 取得完整的持久表层前缀。读数与请求无需一一对应,因为间隔抑制可以让请求进入步骤而不追加读数,拒绝或失败则两者都不追加。插件通过 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/feature/2026-07-19-human-goal-command.md
2026-07-19-human-goal-command.md: 5fdd80f7423b80e84e58f7379130ee59a2e8a723
2026-07-19-human-goal-command.zh.md: 74dd0a2bec923687c91ddf4ce7efe4cc5b25bfc9
2026-07-19-human-goal-command.zh.md: 841e7195454b2e0cfc6f74fb28369022b84c5780
@@ -34,7 +34,7 @@ Status: implemented
预期的 `GoalError` 失败会变为一个稳定且不含品牌化 id 的 `CommandResult.error`,使领域诊断不会向面向人类的界面泄露比较并交换内部细节,非法操作也绝不会进入模型历史。当前状态负责提供针对具体状态且可执行的恢复路径。其他异常仍是适配器可见的命令失败;若把程序缺陷当成普通领域错误,就会隐藏问题。命令处理器只执行同步领域变更,因此请求取消会在变更开始前由命令注册表决定,不存在需要回滚的外逸异步副作用。
通用斜杠输入、状态文本与错误不会持久化。成功的 goal 变更会追加领域自有的 `goal/change` 事件,而且不会模型上下文排队。该命令不会引入可能与领域事件不一致的第二份审计记录。
通用斜杠输入、状态文本与错误不会持久化。成功的目标变更会追加领域自有的 `goal/change` 事件,而且不会模型上下文加入队列。该命令不会引入可能与领域事件不一致的第二份审计记录。
### 应用组合
@@ -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/feature/2026-07-19-persisted-same-session-goal-domain.md
2026-07-19-persisted-same-session-goal-domain.md: ce93a652f3910912fbf6afc28061de4d13eb2de2
2026-07-19-persisted-same-session-goal-domain.zh.md: 7fb8dc3a6c76b91e0abf495782d51b30d3cd5b53
2026-07-19-persisted-same-session-goal-domain.zh.md: ddb4fdedafbacbfec355a7a1c912a5f9545e208a
@@ -18,9 +18,9 @@ Status: implemented
### 持久记录与回放
每次变更都会追加带版本的 `goal/change` 会话事件,其中包含完整快照;clear 则包含带修订号的墓碑。会话日志是唯一的持久真源,因此持久化和 fork 会继承 goal 记录,而无需另设数据库或头字段。[Goal 自有持久事件决策](../architecture/2026-07-31-goal-owned-durable-events.md)负责 goal 状态与 inbox 状态模型上下文之间的职责分离。
每次变更都会追加带版本的 `goal/change` 会话事件,其中包含完整快照;清除时则包含带修订号的墓碑。会话日志是唯一的持久真源,因此持久化和 fork 会继承目标记录,而无需另设数据库或头字段。[目标自有持久事件决策](../architecture/2026-07-31-goal-owned-durable-events.md)负责将目标持久事件与收件箱状态模型上下文分离。
回放折叠只从 `goal/change` 派生生命周期变更,并校验 JSON 形状、新 id、修订连续性、生命周期转换、计数器以及单个 goal 内单调递增的时间戳。只有当前活跃修订上带正数且连续编号、已准入`user/message` 来源才会推进 Goal Round,且不能超过 `maxGoalRounds`;普通会话轮次不会影响该计数器。当前格式的畸形记录会使回放失败,而不会被忽略或修复。
回放折叠只从 `goal/change` 派生生命周期变更,并校验 JSON 形状、新 id、修订连续性、生命周期转换、计数器以及单个目标内单调递增的时间戳。只有当前活跃修订上带正数且连续编号、已接纳`user/message` 来源才会推进 Goal Round,且不能超过 `maxGoalRounds`;普通会话轮次不会影响该计数器。当前格式的畸形记录会使回放失败,而不会被忽略或修复。
增量回放会在每个有效事件后推进游标,并停留在首个损坏事件处,因此后续读取会报告同一个持久故障。重启后仍以持久日志为准。
@@ -36,12 +36,12 @@ Status: implemented
## 测试
单元测试固定创建默认值、精确实时 agent 校验、比较并交换拒绝、所有生命周期转换、阻塞原因校验与保留、恢复时的上限执行、清除与替换、种子回放和 `SessionStore.fork()` 继承、会话启动与生命周期所有者解除激活、活跃目标重新激活、持久事件折叠、inbox 独立性、损坏事件的稳定回放、服务与监听器销毁、监听器隔离、挂钟后退钳制、严格记录解码、生命周期连续性,以及连续 Goal Round 归属。无密钥 Loader/stdio 进程测试通过测试专用 `cordis.yml` 挂载服务与生命周期消费方,再从外部读取持久 JSONL,以验证 goal 记录以及不存在未经请求的 Goal Round。包源码受仓库逐文件 100% 覆盖率门禁约束。
单元测试固定创建默认值、精确实时 agent 校验、比较并交换拒绝、所有生命周期转换、阻塞原因校验与保留、恢复时的上限执行、清除与替换、种子回放和 `SessionStore.fork()` 继承、会话启动与生命周期所有者解除激活、活跃目标重新激活、持久事件折叠、收件箱独立性、损坏事件的稳定回放、服务与监听器销毁、监听器隔离、挂钟后退钳制、严格记录解码、生命周期连续性,以及连续 Goal Round 归属。无密钥 Loader/stdio 进程测试通过测试专用 `cordis.yml` 挂载服务与生命周期消费方,再从外部读取持久 JSONL,以验证目标记录以及不存在未经请求的 Goal Round。包源码受仓库逐文件 100% 覆盖率门禁约束。
## 考虑过的替代方案
- **把目标存入独立数据库或会话头**——不予采纳,因为会话日志已经提供顺序、持久化、fork 前缀与可重建性;第二份存储会引入原子性和谱系问题。
- **把每次持久变更与排队的模型上下文绑定。** 后续的 [Goal 自有持久事件决策](../architecture/2026-07-31-goal-owned-durable-events.md)不采用这一方案:goal 工具与已调度的继续执行提示词会在需要时暴露状态,而领域持久化不依赖队列结果。
- **把每次持久变更与排队的模型上下文绑定。** 后续的 [Goal 自有持久事件决策](../architecture/2026-07-31-goal-owned-durable-events.md)不采用这一方案:目标工具与已调度的继续执行提示词会在需要时暴露状态,而领域持久化不依赖队列结果。
- **持久化激活态并自动重启**——不予采纳,因为打开或恢复会话时必须等待人类输入;持久阶段记录状态,而不是再次消耗资源的授权。
- **把所有会话轮次都计为 Goal Round**——不予采纳,因为同一会话可以包含人类澄清、检查和无关工作;只有归属于目标的继续执行轮次才消耗该预算。
- **向 `dsh-agent-loop` 添加目标状态或通用循环抽象**——不予采纳,因为状态与继续执行策略可以通过现有插件、`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/feature/2026-07-19-same-session-goal-round-driver.md
2026-07-19-same-session-goal-round-driver.md: a989b9d3487ebbf8ff11a4e34cb08029f1e4ace4
2026-07-19-same-session-goal-round-driver.zh.md: b49a759c8f3577db30b8ab45449bb7faed85a7da
2026-07-19-same-session-goal-round-driver.zh.md: 309b5ce45c2edb516a329f9992cea5558a2aeb8c
@@ -20,15 +20,15 @@ Status: implemented
### 预留与接纳
当 agent 空闲、没有竞争中的排队工作,且当前目标为 `active``armed` 时,驱动器会先检查点持久化待处理的目标变更,并在等待后重新校验所有条件。若 `roundsStarted` 已等于 `maxGoalRounds`,它会记录代码为 `round-limit``blocked`;否则,它会先预留精确身份 `{ goalId, revision, round: roundsStarted + 1 }` 和完整渲染提示词,再以 `GoalMessageSource` 调用 `Agent.followup()`。提示词用 JSON 引号编码目标描述,使多行或类似标签的文本在熟悉框架中仍是无歧义的数据值。
当 agent 空闲、没有竞争中的排队工作,且当前目标为 `active``armed` 时,驱动器会先待处理的目标变更持久化到检查点,并在等待完成后重新校验所有条件。若 `roundsStarted` 已等于 `maxGoalRounds`,它会记录代码为 `round-limit``blocked`;否则,它会先预留精确身份 `{ goalId, revision, round: roundsStarted + 1 }` 和完整渲染提示词,再以 `GoalMessageSource` 调用 `Agent.followup()`。提示词用 JSON 引号编码目标描述,使多行或类似标签的文本在熟悉框架中仍是无歧义的数据值。
`agent/pre-step` 瀑布是进入栅栏。正数目标来源只有在完全匹配驱动器待处理的身份和内容、实时目标仍具有相同 id 与修订号、激活态仍为 armed,并且该回合仍是下一个编号时才会进入步骤。插件在委托下游监听器前检查一次,在下游返回后再检查一次。第二次检查防止异步监听器编辑或暂停目标后,旧提示词仍进入步骤
`agent/pre-step` waterfall(瀑布式事件)是进入栅栏。正数目标来源只有在完全匹配驱动器待处理的身份和内容、实时目标仍具有相同 id 与修订号、激活态仍为 armed,并且该回合仍是下一个编号时才会进入。插件在委托下游监听器前检查一次,在下游返回后再检查一次。第二次检查防止异步监听器编辑或暂停目标后,旧提示词仍进入。
只有最终产生的 `user/message` 才是进入步骤的目标回合,并推进目标折叠。陈旧预留会关闭一个 blocked 的无步骤轮次;驱动器会把它标记为陈旧,不消耗回合数。若下游策略拒绝并非由陈旧状态导致,目标会进入 blocked,而不会绕过该策略自动重试。
只有最终产生的 `user/message` 才是进入的目标回合,并推进目标折叠。陈旧预留会关闭一个被阻塞的零步骤轮次;驱动器会把它标记为陈旧,不消耗回合数。若下游策略拒绝并非由陈旧状态导致,目标会进入 blocked,而不会绕过该策略自动重试。
### 人类工作与修订竞争
预留的 `MessageId` 会区分驱动器自己的完整记录与其他所有提示词。预留之前已经排队的普通工作会阻止调度;自动提示词待处理时进入的普通工作会使该预留过期,因此混合的已领取批次会 reject 自动提案。目标回合已经进入步骤后到达的普通工作会保留在队列中,成为下一个独立轮次;只有 agent 再次空闲后才重新考虑继续执行。
预留的 `MessageId` 会区分驱动器自己的完整记录与其他所有提示词。预留之前已经排队的普通工作会阻止调度;自动提示词待处理时进入的普通工作会使该预留过期,因此混合的已领取批次会拒绝自动提案。目标回合进入后到达的普通工作会保留在队列中,成为下一个独立轮次;只有 agent 再次空闲后才重新考虑继续执行。
目标在回合内发生变更时会推进持久修订号。旧修订的结算不得覆盖该变更。驱动器会丢弃旧尝试的结果、读取新投影,并且只在新修订仍为 active 与 armed 时继续。因此,模型记录的完成、暂停、阻塞和编辑相对于物理轮次稍后的关闭原因具有最终权威。
@@ -53,9 +53,9 @@ Status: implemented
每次 `goal/changed` 通知都会产生一个检查点义务。驱动器在预留工作前等待 `ctx.sessions.flush(session)`,随后检查是否出现了更新的变更、agent 生命周期变化或竞争提示词。轮次结束时的 flush 失败会在 `turn/end` 之后通过现有 `agent/error` 通知报告;即使并发的一次性注入已追加后续轮次,驱动器仍会找到对应的同一个已关闭轮次,把失败关联到对应的同一次尝试,并在下一次空闲决策前解除激活。
广义取消会清除待处理 inbox 工作,并中止活跃 loop 阶段。目标驱动器通过 inbox 的领取/丢弃事件和持久的 aborted 轮次结束来跟踪预留消息。由于轮次现在会在首次领取前打开,取消可以关闭已领取的步骤尝试;驱动器会把该尝试标记为已取消,并让随后的 idle 边沿暂停目标,与已准入尝试的处理方式相同。没有匹配目标尝试的取消只会移除进程内激活态。若暂停变更抛错,驱动器会回退到解除激活,避免已取消的自动工作重新启动。
广义取消会清除待处理的收件箱工作,并中止活跃的循环阶段。目标驱动器通过收件箱领取/丢弃事件和持久的 aborted 轮次结束事件来跟踪预留消息。由于轮次现在会在首次领取前打开,取消可以关闭已领取的步骤尝试;驱动器会把该尝试标记为已取消,并让随后的空闲边沿暂停目标,与已接纳尝试的处理方式相同。没有匹配目标尝试的取消只会移除进程内激活态。若暂停变更抛错,驱动器会回退到解除激活,避免已取消的自动工作重新启动。
`Agent.cancel()` 仍是唯一的公共广义取消动词。若消费方依赖顺序,实现该接口的自定义 `Agent` 必须遵守 inbox、轮次结束、status 与完全停稳的顺序。
`Agent.cancel()` 仍是唯一的公共广义取消动词。若消费方依赖这些顺序,实现该接口的自定义 `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/feature/2026-07-21-cross-session-references.md
2026-07-21-cross-session-references.md: b2d428e0937fdd881720720401948a1c3e7ef1f6
2026-07-21-cross-session-references.zh.md: a59657bfcd46393a5cd1c5a5076a6eddd5d065ff
2026-07-21-cross-session-references.zh.md: e242e159329a5f1446b08547cd0f176b6c1af3f7
@@ -26,7 +26,7 @@ TUI 用户需要把另一场对话中的相关工作带入一条新消息,但
## 消息所有权
TUI 负责快照/直接消息事务,不扩展通用收件箱记录。agent 空闲时,它会在调用 `followup()` 前安装一次性的外层 `agent/pre-step` 监听器;enter 决策会把快照作为另一条消息接收,而 reject 或更早的普通丢弃会释放监听器,并且不写入任何消息。agent 运行时,TUI 会依次调用 `inject(snapshot)``steer(prompt)`,把两者放入 next-step inbox,等待后续同一次领取。pre-step reject 或失败会这对已领取消息保持删除;领取后插入的消息继续等待。这一通用交付边界由[上下文分离决策](../architecture/2026-07-24-separate-context-injection-from-turn-execution.md)规定。
TUI 负责快照/直接消息事务,不扩展通用收件箱记录。agent 空闲时,它会在调用 `followup()` 前安装一次性的外层 `agent/pre-step` 监听器;enter 决策会把快照作为另一条消息接收,而 reject 或更早的普通丢弃会释放监听器,并且不写入任何消息。agent 运行时,TUI 会依次调用 `inject(snapshot)``steer(prompt)`,把两者放入 next-step inbox,等待后续同一次领取。pre-step reject 或失败会使这对已领取消息维持已移除状态;领取后插入的消息继续等待。这一通用交付边界由[上下文分离决策](../architecture/2026-07-24-separate-context-injection-from-turn-execution.md)规定。
引用准备过程不是新的 steering 协议,本身也不会创建轮次。空闲交付使用 `followup()` 和 pre-step 进入决策;运行期间的交付使用共享 next-step inbox,并保持快照顺序。
@@ -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/feature/2026-07-24-web-session-model-selector.md
2026-07-24-web-session-model-selector.md: 05e923fb3b5df72485ffb8d6ff5aa0104b19a8a0
2026-07-24-web-session-model-selector.zh.md: bdfa2f856a799cc86600650829a2df62a7dfdc18
2026-07-24-web-session-model-selector.zh.md: c1fd59ada0340a984ddf4a9dcc4c357f9609acb1
@@ -40,4 +40,4 @@ Web Host 为每个新建或恢复的 agent(智能体)复用 `installAgentLlm
## 测试
Host 测试固定分组发现、目录与精确元数据失败隔离、已记录推理强度恢复且不注入陈旧行、建议性的未列出模型选择、不支持的推理强度拒绝、默认值具体化,以及切换仅影响下一次组装。客户端测试固定共享目录、重连恢复与完整目标提交。组件测试固定动态推理强度标签、说明、提供方默认值展示、推理强度提交,以及已删除模型行的 `Select model` 回退。无密钥 built-app fixture(测试前置数据)加载生产模型插件,选择 OpenAI 的 GPT-5 及其 Max 推理强度,发起一个轮次,并验证下一条生成的响应会报告两个 ID;DeepSeek 配置 fixture 会删除活动目录行,在选择替代模型之前固定该回退。
Host 测试固定分组发现、目录与精确元数据失败隔离、已记录推理强度恢复且不注入陈旧行、不受目录约束的未列出模型选择、不支持的推理强度拒绝、默认值具体化,以及切换仅影响下一次组装。客户端测试固定共享目录、重连恢复与完整目标提交。组件测试固定动态推理强度标签、说明、提供方默认值展示、推理强度提交,以及已删除模型行的 `Select model` 回退。无密钥 built-app fixture(测试前置数据)加载生产模型插件,选择 OpenAI 的 GPT-5 及其 Max 推理强度,发起一个轮次,并验证下一条生成的响应会报告两个 ID;DeepSeek 配置 fixture 会删除活动目录行,在选择替代模型之前固定该回退。
@@ -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/feature/2026-07-28-tool-call-file-open-in-os.md
2026-07-28-tool-call-file-open-in-os.md: 499d3caa11e1f2c17d96e43b7846df72a31c0eb1
2026-07-28-tool-call-file-open-in-os.zh.md: a658a7b55d2575ad1a5d8baccb80f0a8444d6db9
2026-07-28-tool-call-file-open-in-os.zh.md: f5fdad90041915171dceab33ea77b2187419dc31
@@ -12,7 +12,7 @@ Status: implemented
文件工具的路径摘要(`read``write``edit` 参数中的 `path``file_path`)渲染为静止状态下即带下划线的链接,并使用 pointer 光标。点击路径会经 `WorkspacesService.openPath` 调用 `host.openPath`,相对路径以会话 cwd 为基准解析。带文件链接的行关闭参数展开(左侧图标不可点);工具行(含 bash 与 todo 注册)去掉整行点击、整行悬停底色,以及点击打开 details 的手势。details 面板及其 inject 面仍保留供程序化选择;工具行不再驱动它们。
`host.openPath` 是特权一元 RPC,仅接受来自回环地址且同源的浏览器请求(与 `host.pickDirectory` 相同的载体守卫)。平台适配器不经 shell 打开:macOS 为 `open`Windows 为 PowerShell `Invoke-Item`,桌面 Linux 为 `xdg-open`;浏览器可渲染的文档会在 macOS 与桌面 Linux 上优先使用能够确定的默认浏览器。尽管 Node 将 WSL 报告为 `linux`,WSL 仍是一种独立的宿主形态:适配器根据其环境或 Microsoft 内核 release 识别它,用 `wslpath -w` 转换 Linux 路径,并将所得 Windows/UNC 路径交给同一 PowerShell 交接。打开器的平台信息和命令运行器可在测试中注入。仅含 URL 的 read 参数(`web_fetch`)不是文件链接。
`host.openPath` 是特权一元 RPC,仅接受来自回环地址且同源的浏览器请求(与 `host.pickDirectory` 相同的载体守卫)。平台适配器不经 shell 打开:macOS 为 `open`Windows 为 PowerShell `Invoke-Item`,桌面 Linux 为 `xdg-open`;浏览器可渲染的文档会在 macOS 与桌面 Linux 上优先使用定的默认浏览器。尽管 Node 将 WSL 报告为 `linux`,WSL 仍是一种独立的宿主形态:适配器根据其环境或 Microsoft 内核 release 识别它,用 `wslpath -w` 转换 Linux 路径,并将所得 Windows/UNC 路径交给同一 PowerShell 交接。打开器的平台信息和命令运行器可在测试中注入。仅含 URL 的 read 参数(`web_fetch`)不是文件链接。
## 考虑过的替代方案
@@ -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/feature/2026-07-29-persistent-bash-str-replace-editor.md
2026-07-29-persistent-bash-str-replace-editor.md: c4750e30370bfd253064c39cb1adc0f5b2baa60d
2026-07-29-persistent-bash-str-replace-editor.zh.md: 62073571ce2164d88897d4959caafd9cb1dbdd93
2026-07-29-persistent-bash-str-replace-editor.zh.md: 14c52fc90d8619d63ca3acab2d85447fab5eb735
@@ -18,7 +18,7 @@ Status: implemented
两个插件都进入 Python runtime 闭包。持久 Bash 的闭包还包含 PTY 服务/本地后端,以及该后端要求的沙箱服务。由于 `node-pty` 在 macOS 上会执行原生 `spawn-helper`,每个打包后的 macOS 运行时可执行文件都会携带一个 `-spawn-helper` 伴随文件;Linux 直接使用 `forkpty`。固定版本的 `node-pty` 补丁会先检查 `DSH_NODE_PTY_SPAWN_HELPER`,因此对当前提供非伴随 helper 的外部消费方而言,该变量仍是真正的覆盖项。未设置该覆盖时,补丁会在打包可执行文件的伴随文件存在时解析它,否则在普通 Node 运行中保留上游查找方式。若 helper 缺失或不可执行,macOS 构建器会在发布前失败。
已交付的 [`core-web.cordis.yml`](../../../../apps/cli/config/core-web.cordis.yml) overlay 会在常规 Web 界面之上组合这两个插件,以满足与 Claude SWE 兼容的 RL 约。它固定使用原生工具模式;完整的系统提示词在设置 `DSH_SYSTEM_PROMPT` 时采用其值,否则采用 `You are a helpful software engineer assistant.`,且不包含 harness 身份、源码 checkout 提示词段、Web 界面定位、Workspace 指令或工具模式指引。它会禁用其他所有面向模型的消费方,使模型恰好只收到持久 `bash``str_replace_editor` 两个 schema,同时保留 Web 宿主、浏览器、Workspace、持久化、沙箱与权限栈。本地 PTY 后端会在创建 shell 时解析会话的有效沙箱模式。只要该所有者仍有打开的 shell 或仍在进行中的 spawn,另一种权限模式就会在对应的会话事件提交前遭到拒绝;编辑器则继续经由 Web 文件系统沙箱运行。
已交付的 [`core-web.cordis.yml`](../../../../apps/cli/config/core-web.cordis.yml) overlay 会在常规 Web 界面之上组合这两个插件,以满足与 Claude SWE 兼容的 RL 约。它固定使用原生工具模式;完整的系统提示词在设置 `DSH_SYSTEM_PROMPT` 时采用其值,否则采用 `You are a helpful software engineer assistant.`,且不包含 harness 身份、源码 checkout 提示词段、Web 界面定位、Workspace 指令或工具模式指引。它会禁用其他所有面向模型的消费方,使模型恰好只收到持久 `bash``str_replace_editor` 两个 schema,同时保留 Web 宿主、浏览器、Workspace、持久化、沙箱与权限栈。本地 PTY 后端会在创建 shell 时解析会话的有效沙箱模式。只要该所有者仍有打开的 shell 或仍在进行中的 spawn,另一种权限模式就会在对应的会话事件提交前遭到拒绝;编辑器则继续经由 Web 文件系统沙箱运行。
## 考虑过的替代方案
@@ -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/process/2026-07-04-doc-tiers-and-budgets.md
2026-07-04-doc-tiers-and-budgets.md: e7b3421d09a1ae5ab9a9373e8040832c1b0d4b97
2026-07-04-doc-tiers-and-budgets.zh.md: 18bf98777c87512e6161a4529db66030ced47bfd
2026-07-04-doc-tiers-and-budgets.zh.md: 157ad13f521d2a497180e25b70e3537a4afbc099
@@ -6,15 +6,15 @@ Status: implemented
## 问题
尽管已有写作指导,常设文档仍不断累积重复规则、反复讲述的事、重复的包package映射,以及陈旧的 Agent Noteagent 决策记录)摘要。该指导也未明确文档在层级中的位置如何限定其内容范围,以及按顺序引导读者学习的内容与面向查阅的材料有何不同。仅靠评审无法阻止这种增长,因此仓库需要在文档分类之外再配一套机械预算。
尽管已有写作指导,常设文档仍不断累积重复规则、反复讲述的事、重复的包映射,以及陈旧的 Agent Note 摘要。该指导也未明确文档在层级中的位置如何限定其内容范围,以及按顺序引导读者学习的内容与面向查阅的材料有何不同。仅靠评审无法阻止这种增长,因此仓库需要在文档分类体系之外再配一套可自动执行的预算。
## 决策
- **结构遵循文档树。**[docs/AGENTS.md](../../../../docs/AGENTS.md) 是文档标准:文档负责承载其主题的详细内容,仅概述直接子项的目的、职责和高层行为,并链接到更深层内容的归属文档。[Agent Note](../../README.md) 仍不受这一结构约约束。每份面向人的文档要么是按顺序引导读者达成结果的教程(tutorial),要么是查阅范围明确的参考文档(reference);[事故复盘(postmortem](../../../../docs/postmortem/README.md) 是范围限定于单个事件的参考文档,其时间线记录证据。教程结合读者的起始知识,按前置依赖顺序介绍概念。
- **结构遵循文档树。**[docs/AGENTS.md](../../../../docs/AGENTS.md) 是文档标准:文档负责承载其主题的详细内容,仅概述直接子项的目的、职责和高层行为,并链接到更深层内容的归属文档。[Agent Note](../../README.md) 仍不受这一结构约约束。每份面向人的文档要么是按顺序引导读者达成结果的教程(tutorial),要么是查阅范围明确的参考文档(reference);[事故复盘(postmortem](../../../../docs/postmortem/README.md) 是范围限定于单起事故的参考文档,其时间线记录证据。教程结合读者的起始知识,按前置依赖顺序介绍概念。
- **每项事实只归属一处的层级分类。**文档标准为每种 Markdown 层级分配单一职责,禁止在事实归属层级之外重复陈述,并包含编写或评审任何文档时使用的赘余检查清单。
- **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都必须低于其数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使重命名无法悄然遗落其预算。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md``packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md``docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。
- **范围窄且严格的预算门禁。**[scripts/verify-doc-budgets.ts](../../../../scripts/verify-doc-budgets.ts) 接入 `doc-sync`[scripts/doc-budgets.manifest.json](../../../../scripts/doc-budgets.manifest.json) 列出的每份文档都必须低于其数上限(采用 `wc -w` 语义,统计整个文件);预算内文件缺失也会使门禁失败,使重命名无法悄然遗落其预算。范围刻意只涵盖容易膨胀的常设文档——根目录和子树中的 `AGENTS.md` 文件、`architecture.md``packages/README.md`,以及它们将内容移入的常设策略文档(`docs/testing.md``docs/defensive-patterns.md`)。参考文档、Agent Note 和包 README 不设预算:只要每一行都是事实,长度在这些位置就是合理的;评审和赘余检查清单负责约束它们。
- **上限是只进不退的执行红线。** 达到或低于目标的文档在上限逐步下调时保留至少 5% 的余量;高于目标的文档则维持冻结的上限,在达到目标之前不得增长(根 `AGENTS.md` ≤ 1,600 词;`architecture.md` ≤ 1,800;子树 `AGENTS.md` ≤ 600,但 `packages/AGENTS.md` ≤ 650、`docs/AGENTS.md` ≤ 1,250`packages/README.md` ≤ 600)。门禁变红时,迁移或压缩内容;只有在 PR(Pull Request)描述中给出明确理由时才提高上限。
- **精简的工作流 skill(技能),契约归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载放置/审计/红灯门禁工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 契约之间的分工相同。
- **精简的工作流 skill(技能),契约归文档。**[.agents/skills/dsh-doc-standards](../../../skills/dsh-doc-standards/SKILL.md) 承载文档放置审计和门禁失败处理工作流,并以文档标准为真源,与 [dsh-translate-docs](../../../skills/dsh-translate-docs/SKILL.md) 和 i18n 契约之间的分工相同。
## 曾考虑的替代方案
@@ -24,7 +24,7 @@ Status: implemented
## 后果
- 向受预算约束的文档添加内容需要置换:将新增内容迁移到其分类体系归属地并留下指针,或压缩现有行文来腾出空间。只增不减会导致 CI 失败。
- 向受预算约束的文档添加内容需要腾挪空间:将新增内容迁移到其分类体系归属地并留下链接,或压缩现有行文来腾出空间。只增不减会导致 CI 失败。
- 结构评审先检查归属关系和文档形式,再进行句子层面的编辑,使较低层级的细节迁移到其归属文档,而不是在错误的位置加以润色。
- 仍高于目标的受预算约束文档不得增长;达到目标后,将恢复 5% 的工作余量。
- 数是一个粗糙的代理指标,这是有意接受的:它无法判断质量,但它在内容被添加的那一刻强制触发迁移决策,而那正是作者拥有足够上下文来正确放置内容的时刻。
- 数是一个粗糙的代理指标,这是有意接受的:它无法判断质量,但它在内容被添加的那一刻强制触发迁移决策,而那正是作者拥有足够上下文来正确放置内容的时刻。
@@ -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/process/2026-07-22-fast-local-git-hooks.md
2026-07-22-fast-local-git-hooks.md: af8a9380fc3cbf4f672ca0b32faa7b187522cf11
2026-07-22-fast-local-git-hooks.zh.md: 04f3a407e421c4d585b8b78b16b9055fd19ac756
2026-07-22-fast-local-git-hooks.zh.md: 1b2c3bf42a65f35d96c266f5f3afb63f26039388
@@ -12,9 +12,9 @@ agent(智能体)已经会运行能够覆盖自身改动的测试和检查,
## 决策
[lefthook.yml](../../../../lefthook.yml) 将两个钩子都保留为有界的本地检查点。Pre-commit 按顺序运行:仅用于格式化的 ESLint 配置修复改动过的 JavaScript 和 TypeScript 文件并重新暂存,不加载项目的 [Oxlint](2026-07-29-oxlint-linter.md) 配置验证这些文件并应用原生安全修复,`git diff --cached --check` 拒绝暂存 diff 中的空白错误,vendor manifest(元数据清单)守卫检查 vendor 源码元数据。Pre-push 运行 `pnpm run typecheck`;该命令会先准备好生成的 Host TypeRT 约,再运行 Client 增量类型检查。
[lefthook.yml](../../../../lefthook.yml) 将两个钩子都保留为有界的本地检查点。Pre-commit 按顺序运行:仅用于格式化的 ESLint 配置修复改动过的 JavaScript 和 TypeScript 文件并重新暂存,不加载项目的 [Oxlint](2026-07-29-oxlint-linter.md) 配置验证这些文件并应用原生安全修复,`git diff --cached --check` 拒绝暂存 diff 中的空白错误,vendor manifest(元数据清单)守卫检查 vendor 源码元数据。Pre-push 运行 `pnpm run typecheck`;该命令会先准备好生成的 Host TypeRT 约,再运行 Client 增量类型检查。
Pre-commit 不运行类型分析、测试、快照、文档检查、构建、`hygiene` 或门禁调度器。Pre-push 只增加仓库类型检查所需的 Host 约构建。可选运行的 `check:all` 包脚本独立于这些钩子,从 [scripts/run-gates.ts](../../../../scripts/run-gates.ts) 中选择 `check-all` 调度器清单;它是贡献者命令,而非对 agent 的指令。
Pre-commit 不运行类型分析、测试、快照、文档检查、构建、`hygiene` 或门禁调度器。Pre-push 只增加仓库类型检查所需的 Host 约构建。可选运行的 `check:all` 包脚本独立于这些钩子,从 [scripts/run-gates.ts](../../../../scripts/run-gates.ts) 中选择 `check-all` 调度器清单;它是贡献者命令,而非对 agent 的指令。
agent 检查待推送的 diff,并仅运行一次能够覆盖其行为的最小范围测试和检查。CI 负责全量覆盖率门禁、构建产物检查与平台矩阵。只有在明确要求、诊断 CI,或涉及全仓库的改动无法由范围更窄的证据得到可信验证时,才完整运行一遍本地检查矩阵。
@@ -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/process/2026-07-27-wine-windows-gates-experiment.md
2026-07-27-wine-windows-gates-experiment.md: 1b01fe00dc1588482442a3cedc35eefa7fdb0975
2026-07-27-wine-windows-gates-experiment.zh.md: b50739f5b63c5836248c94927037e38900abc84f
2026-07-27-wine-windows-gates-experiment.zh.md: 3145a4aa6a06cacbab96dd37714617aeecdfdde0
@@ -14,7 +14,7 @@ Pull request 的 Windows 通道旨在验证两个阻断性 win32 表面,即 wo
[ci.yml](../../../../.github/workflows/ci.yml) 中必需的 pull request `windows` 作业(`windows node 24 / wine blocking`)在 `ubuntu-latest` 上通过 Wine 用真实 Windows 二进制运行阻断门禁命令:校验和验证过的 win-x64 Node.js 执行 `tsc -b``tsdown` 与 VitePress 生产构建,因此工具链的 win32 分支——反斜杠路径处理、`CreateProcess` 派生语义、`@esbuild/win32-x64` 的 PE 加载、以及 rolldown/rollup 的 MSVC `.node` 插件——都真正执行。master 的 `serial-windows` 作业原封不动:完整的原生内核清单,包括本通道不运行的观察性可移植性门禁,仍在每次 master push 时于真实 `windows-2025` 上执行。
依赖在 Linux 上原生安装,`supportedArchitectures` 扩展到 win32-x64,使 Windows 平台包物化进同一个 store;通过直接调用各工具的 JavaScript 入口绕开 cmd-shim 层,这正是 `run-gates` 最终派生的那些进程。`nodeLinker: hoisted` 是承重的,不是风格问题:一个独立原型保留了 pnpm 默认的 isolated 布局——包括在 Linux 预取的 store 上忠实地用 Windows pnpm 离线重装——而 Wine 下的 Windows Node 依然无法穿过 isolated 符号链接链解析 `@esbuild/win32-x64` 或加载 koffi 预编译产物,在任何仓库门禁运行前就失败了。扁平的真实文件布局才让门禁变得可达;本通道采纳了该原型的校验和固定,同时明确放弃其「Windows pnpm 安装依赖树」的目标(安装约在此仍由 Linux 侧验证)。
依赖在 Linux 上原生安装,`supportedArchitectures` 扩展到 win32-x64,使 Windows 平台包物化进同一个 store;通过直接调用各工具的 JavaScript 入口绕开 cmd-shim 层,这正是 `run-gates` 最终派生的那些进程。`nodeLinker: hoisted` 是承重的,不是风格问题:一个独立原型保留了 pnpm 默认的 isolated 布局——包括在 Linux 预取的 store 上忠实地用 Windows pnpm 离线重装——而 Wine 下的 Windows Node 依然无法穿过 isolated 符号链接链解析 `@esbuild/win32-x64` 或加载 koffi 预编译产物,在任何仓库门禁运行前就失败了。扁平的真实文件布局才让门禁变得可达;本通道采纳了该原型的校验和固定,同时明确放弃其「Windows pnpm 安装依赖树」的目标(安装约在此仍由 Linux 侧验证)。
该通道靠四个杠杆把墙钟时间保持在与 Linux CI 作业相当的水平:master 刷新的 pnpm store 缓存(只恢复,与 Linux 作业同键)、Wine 供给(apt 安装、Windows Node 下载、`wineboot`)与 `pnpm install` 并发运行、两个阻断表面并发运行——与 `run-gates` 在原生 Windows 上给它们的形状相同——以及按 runner 镜像为键的 apt 归档缓存,由 master 的 `wine apt cache` 作业播种,使每个 pull request 都能从默认分支作用域恢复。
@@ -32,7 +32,7 @@ Pull request 的 Windows 通道旨在验证两个阻断性 win32 表面,即 wo
**在 Linux runner 内用 QEMU/KVM 跑完整 Windows 客户机。** 真实 NT 内核,保真度完整,包括大小写不敏感的 NTFS 与 ConPTY——但首个门禁运行前要花数十分钟下载镜像并做无人值守安装(兄弟实验分支 `exp/kvm-windows-ci` 实测端到端 40 分 19 秒)。只有配上会挤压 Actions 缓存预算的磁盘镜像缓存才可投入使用。
**在 Wine 下由 Windows pnpm 执行安装。** 同一想法的更高保真度变体:把 MinGit 与 pnpm 放进 prefix,用 Linux 预取填充 store,再由 Windows Node 运行 `pnpm install --offline`,让安装约本身以 win32 身份执行。它到达了安装但没到达门禁——Wine 的网络无法直接访问 registry,且 isolated 的 `node_modules` 布局即便在干净的离线安装后也挫败了 Windows 平台包的解析。本通道牺牲这份保真度(hoisted 布局、Linux 侧安装)来换取门禁可达;两份记录是同一裁决互补的两半。
**在 Wine 下由 Windows pnpm 执行安装。** 同一想法的更高保真度变体:把 MinGit 与 pnpm 放进 prefix,用 Linux 预取填充 store,再由 Windows Node 运行 `pnpm install --offline`,让安装约本身以 win32 身份执行。它到达了安装但没到达门禁——Wine 的网络无法直接访问 registry,且 isolated 的 `node_modules` 布局即便在干净的离线安装后也挫败了 Windows 平台包的解析。本通道牺牲这份保真度(hoisted 布局、Linux 侧安装)来换取门禁可达;两份记录是同一裁决互补的两半。
**Linux 上的文件系统语义通道(casefold ext4、文件名 lint)。** 以近零成本捕获最高频的 Windows 故障类别,但对 win32 二进制什么也证明不了。作为兄弟实验分支 `exp/casefold-windows-ci` 探索;与本通道互补而非竞争。
@@ -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-06-20-public-agent-stop-surface.md
2026-06-20-public-agent-stop-surface.md: e9c713c7ba8c784d3d66fe8f391e1b197f7394d2
2026-06-20-public-agent-stop-surface.zh.md: 5c1eb0ec8245c60613d6d4d7c1d105b5243bc938
2026-06-20-public-agent-stop-surface.zh.md: 217c898eec6b9e232f2cf3982b2bec411e8d91ab
@@ -36,4 +36,4 @@ Status: implemented
## 相关
本 Agent Note 只移除冗余的停止动词。轮次中途 steering 仍是一条有意保留的消息路径;完全停稳观察仍通过 `whenIdle()` 完成。最终交付接口包括 `followup()``steer()``inject()`;停止与观察仍通过 `cancel()``whenIdle()` 完成。
本 Agent Note 只移除冗余的停止动词。轮次中途 steering 仍是一条有意保留的消息路径;完全停稳观察仍通过 `whenIdle()` 完成。最终的消息投递接口包括 `followup()``steer()``inject()`;停止与观察仍通过 `cancel()``whenIdle()` 完成。
@@ -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-17-one-send-one-turn.md
2026-07-17-one-send-one-turn.md: 834eb2b3aace0a0aa5f86f483e7b7d7aabbe467e
2026-07-17-one-send-one-turn.zh.md: d22893b912462cffffb0d6ecd81811e9cb5c523d
2026-07-17-one-send-one-turn.zh.md: d05ce63c404ce4d10ac098e3a3c9617443cc278b
@@ -16,11 +16,11 @@ Status: implemented
规则很简单:一次成功的 `send()` 创建一个独立的 FIFO 队列项。该队列项如果运行,就是所在轮次中唯一的普通消息。队列项可能在启动前被丢弃,因此精确保证是最多一个轮次,而不是必定一个轮次;两次 send 绝不会被悄悄合并。
消息插入之前,`send()` 会检查 agent 状态,并接受已有标识且经过深度冻结的值。持久 splice 与 `agent/inbox/inserted { message }` 会保留其 `MessageId`;在驱动器领取或丢弃该消息之前,可以通过 `Inbox.replace()``Inbox.remove()` 寻址。当前生命周期由[已领取 pre-step inbox 决策](../architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md)规定。
消息插入之前,`send()` 会检查 agent 状态,并接受已有标识且经过深度冻结的值。持久 splice 与 `agent/inbox/inserted { message }` 会保留其 `MessageId`;在驱动器领取或丢弃该消息之前,可以通过 `Inbox.replace()``Inbox.remove()` 寻址。当前生命周期由[已领取 pre-step inbox 决策](../architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md)规定。
如果消息 A、B 都进入处理,B 的轮次只能在 A 记录 `turn/end` 且 A 的持久性检查点处理结束后开始。因此,B 的请求能看到 A 在同一会话日志中留下的已关闭结果。检查点错误会照常报告,但处理结束只表示解除这道顺序屏障,不表示失败的写入已经持久化。面向整个 agent 的 `cancel()`、dispose(资源释放)或 `turn/start` 之前的失败也可能丢弃尚未启动的队列项,而不打开一个空轮次。
轮次边界上,循环会先打开轮次,再在待处理 next-step 输入之后领取一条 follow-up。`agent/pre-step` 要么拒绝提案,要么返回进入步骤的完整批次。被拒绝的 follow-up 保持已删除,并关闭一个 blocked 的无步骤轮次,不写入模型可见历史。实现中不存在混合普通 follow-up 分支。
轮次边界上,循环会先打开轮次,再在待处理 next-step 输入之后领取一条 follow-up。`agent/pre-step` 要么拒绝提案,要么返回进入步骤的完整批次。被拒绝的 follow-up 保持已删除,并关闭一个受阻的无步骤轮次,不写入模型可见历史。实现中不存在混合普通 follow-up 分支。
上述不合批规则只适用于普通 follow-up 输入。`steer()` 会把输入放入 next-step inbox 并唤醒驱动器。在轮次期间,循环可以在后续步骤边界领取它;agent 空闲时,这个会唤醒的 next-step 批次会启动一个新轮次。批次被领取后才到达的输入会等待后续边界,而取消或 dispose 可以将其丢弃。
@@ -35,11 +35,11 @@ Status: implemented
- 单元测试和基于属性的测试从同一调用栈、相邻微任务、不同生产方和重入回调提交 send;每条消息都会得到一个按 FIFO 排序的独立轮次。
- stdio 构建产物测试提交两行输入,并观察到两个模型请求和两个轮次边界。
- 延迟和拒绝第一个轮次的检查点,都能让下一个轮次保持等待,并证明其请求可以看到前一条助手结果。
- 失败路径测试覆盖 pre-step reject、监听器失败、广义取消、dispose 和 `turn/start` 之前的失败;首次 pre-step 的各种退出都会关闭边界平衡的无步骤轮次,消息不会合并,之后仍需处理的工作也能继续清空。
- 失败路径测试覆盖 pre-step 拒绝、监听器失败、面向整个 agent 的取消、dispose 和 `turn/start` 之前的失败;首次 pre-step 的各种退出都会关闭边界平衡的无步骤轮次,消息不会合并,之后仍需处理的工作也能继续清空。
- 其他测试分别覆盖轮次打开时、轮次失败后和空闲时的 `steer()`,以及待处理的 `inject()`、面向整个 agent 的状态和 `whenIdle()`
## 后果
普通轮次的边界可预测:消息 A、B 始终分开,B 只能在 A 关闭并到达检查点后运行。调用方仍然拿不到逐次 send 的完成句柄;待处理消息可通过其 `MessageId` 移除,广义取消可以丢弃整个尚未启动的队尾,而状态与完全停稳仍是面向整个 agent 的观察。
普通轮次的边界可预测:消息 A、B 始终分开,B 只能在 A 关闭并到达检查点后运行。调用方仍然拿不到逐次 send 的完成句柄;待处理消息可通过其 `MessageId` 移除,面向整个 agent 的取消可以丢弃整个尚未启动的队尾,而状态与完全停稳仍是面向整个 agent 的观察。
代价是模型请求和检查点都会增加。繁忙队列可能需要更长时间才能清空;如果生产方持续提交消息,队列也可能增长。只有建立显式且经过测量的契约后,才能重新引入普通 send 批处理。
@@ -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-22-plan-specific-collaboration-state.md
2026-07-22-plan-specific-collaboration-state.md: d139f3d861244c80acfb604d17172461bbf0cd57
2026-07-22-plan-specific-collaboration-state.zh.md: a11ed1f270f58fc49a41be0e475889f84fa7cd40
2026-07-22-plan-specific-collaboration-state.zh.md: dbf90357e4c4967d9665533c9abd2b4e1838da27
@@ -24,7 +24,7 @@ Plan mode 拥有一个 plan 专用产品包:位于 `packages/plan/plan-mode/`
### 边界与模型契约
`plan/mode` 仅记录到日志且不进入表层,因此恢复、fork 和压缩都能恢复该状态,无需实时镜像。spawn 出的 agent 初始处于未激活状态,因为创建时没有 plan 选项。待生效的用户选择会在初始或续步 pre-step、或请求恢复重试时,于受影响的请求组装前写入日志;持久追加失败会让意图保持待定,留到后续边界处理。
`plan/mode` 仅记录到日志且不进入表层,因此恢复、fork 和压缩compaction都能恢复该状态,无需实时镜像。spawn 出的 agent(智能体)初始处于未激活状态,因为创建时没有 plan 选项。待生效的用户选择会在初始或续步 pre-step 时,或在请求恢复重试时,于受影响的请求组装前写入日志;持久追加失败会让意图保持待定,留到后续边界处理。
激活状态在提示词顺序 50 处贡献部署提供的区段。未激活状态不贡献区段,但 `exit_plan_mode` 在两种状态下都保持注册,因此状态转换会改变已记录的请求头,却不改变原生工具 schema 或 Code Mode SDK。用户发起的转换只会在上一条请求头描述相反状态时追加一条来源为插件的通知;第一次请求前的选择或最终状态未变化的选择不会追加通知,经批准的工具退出则依赖其工具结果,不再追加第二条通知。
@@ -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-23-collapse-persistence-flush-state.md
2026-07-23-collapse-persistence-flush-state.md: 2801079e5b322d076eced3a0113e7958d9b4c3b9
2026-07-23-collapse-persistence-flush-state.zh.md: dfc8fc9576416b1f51f4f9c978f49aee4f39f938
2026-07-23-collapse-persistence-flush-state.zh.md: d5b02ebc545a32fbdb44feed04ec2afbbafeff4b
@@ -12,9 +12,9 @@ Status: implemented
## 决策
每个活跃的 `Session` 都有一个生命周期条目,其中包含初始化和一个包私有写入控制器。该控制器负责 `pending`、固定批处理计时器、可选的活跃写入、自动重试暂停和共享 flush 屏障。`session/event` 监听器将冻结的事件复制到 `pending`;第一个事件设定固定截止时间,后续事件加入但不会重置。一次写入会取出一个稳定的待处理前缀;写入期间接纳的事件仍留在待处理队列中,并形成另一个独立有界的后续批次。
每个活跃的 `Session` 都有一个生命周期条目,其中包含初始化和一个包私有写入控制器。该控制器负责 `pending`、固定批处理计时器、可选的活跃写入、自动重试暂停和共享刷新屏障。`session/event` 监听器将冻结的事件复制到 `pending`;第一个事件设定固定截止时间,后续事件加入但不会重置。一次写入会取出一个稳定的待处理前缀;写入期间接纳的事件仍留在待处理队列中,并形成另一个独立有界的后续批次。
`session/flush` 是即时完全停稳屏障。它等待初始化完成、取消批处理计时器、等待任何活跃尝试完成,并排空待处理事件,包括屏障运行期间接纳的事件。后台写入失败会被记录,但不会拒绝同步事件生产方;系统会恢复完整且顺序不变的批次,并暂停自动重试。新事件会开启新的固定窗口;显式 flush、退役或后端资源销毁会立即重试,并在失败再次发生时向调用方暴露失败。
`session/flush` 是即时完全停稳屏障。它等待初始化完成、取消批处理计时器、等待任何活跃尝试完成,并排空待处理事件,包括屏障运行期间接纳的事件。后台写入失败会被记录,但不会拒绝同步事件生产方;系统会恢复完整且顺序不变的批次,并暂停自动重试。新事件会开启新的固定窗口;显式刷新、退役或后端资源销毁会立即重试,并在失败再次发生时向调用方暴露失败。
初始化只进入现有的按 id 操作链一次,并在占有该轮执行权时调用未串行化的核心操作。该操作链与活跃控制器保持分离,因为公共 `create``append``load` 调用即使没有 `Session` 对象仍可能发生竞态,依然需要按标识串行执行。
@@ -44,6 +44,6 @@ Status: implemented
## 后果
活跃会话条目把初始化与一个控制器放在一起;该控制器负责待处理事件、计时器、活跃写入、重试暂停和 flush 屏障。协调器仍以独立容器保存已持久化的标识状态、准备好的冷态 Session、标识退役等待方和按 id 操作链,因为即使不存在可写的活跃 Session,这些生命周期仍然存在。有界写入缩短了通常情况下因崩溃而丢失数据的窗口,相比即时调度产生更少的后端批次,同时不改变强制屏障。
活跃会话条目把初始化与一个控制器放在一起;该控制器负责待处理事件、计时器、活跃写入、重试暂停和刷新屏障。协调器仍以独立容器保存已持久化的标识状态、准备好的冷态 Session、标识退役等待方和按 id 操作链,因为即使不存在可写的活跃 Session,这些生命周期仍然存在。有界写入缩短了通常情况下因崩溃而丢失数据的窗口,相比即时调度产生更少的后端批次,同时不改变强制屏障。
`session/flush` 不再决定普通持久化何时开始。它仍是循环和检查点策略使用的顺序与错误观测边界,因此检查点成功仍表示在其完成前接纳的每个事件都已持久化。
@@ -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-24-agent-loop-observable-state-machine.md
2026-07-24-agent-loop-observable-state-machine.md: b1773fdf7fd3bcbea9c6b9a4a0ce37c715bce4ca
2026-07-24-agent-loop-observable-state-machine.zh.md: a4ed98117b965b445ec0888c55a1b1b27887532a
2026-07-24-agent-loop-observable-state-machine.zh.md: f47e5901ec97a44efa00cf79ef8ce52fdcac9878
@@ -21,7 +21,7 @@ agent 生命周期、agent 整体活动状态、收件箱条目的进度以及
- 待处理消息插入时会发出 `agent/inbox/inserted`,随后要么在原子纯删除领取后发出 `agent/inbox/claimed`,要么在普通删除后发出 `agent/inbox/discarded``MessageId` 关联确切消息;持久 splice 坐标保留 placement 与取消信息。inbox 事件描述插入、领取和丢弃,而不是轮次完成。
- 已领取的轮次经过 pre-step 进入决策和零个或多个请求步骤。自动重试会关闭失败轮次并立即开启另一个轮次;`agent/settled` 只报告该重试链的终态轮次,且仍不同于 agent 整体转换到 `status === 'idle'`
循环保留四个状态机扩展事件。`agent/pre-step` 对独占的已领取批次执行 reject 或 enter 决策,并在每个拟议步骤前运行。`agent/request` 是冻结调用配置所用的 waterfall;配置只能来自 `await next()`,不再通过重复的位置参数提供。`agent/request-error` 串行确定需要等待的模型请求恢复由谁负责。当轮次原本已经没有剩余工作时,`agent/turn-stopping` 运行;需要再执行一个步骤的监听器使用 `agent.steer()` 记录真实的 steering,循环在所有监听器完成后根据这份数据作出决定。
循环保留四个状态机扩展事件。`agent/pre-step` 对独占的已领取批次执行 reject 或 enter 决策,并在每个拟议步骤前运行。`agent/request` 是冻结调用配置所用的 waterfall;配置只能来自 `await next()`,不再通过重复的位置参数提供。`agent/request-error` 串行确定需要等待的模型请求恢复由谁负责。当轮次原本已经没有剩余工作时,`agent/turn-stopping` 运行;需要再执行一个步骤的监听器使用 `agent.steer()` 记录真实的 steering(中途引导),循环在所有监听器完成后根据这份数据作出决定。
是否继续和终止执行由数据表达,不再由返回的控制枚举表达。工具调用和已接受的 steering 要求再执行一个步骤。携带 `concludesTurn` 的工具结果会在其所属步骤终止工具循环。循环不再暴露通用的 `ContinuationDecision` 或终止返回通道。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/proposed/architecture/2026-07-25-client-settings-locale-theme.md
2026-07-25-client-settings-locale-theme.md: fdcdcff92fa324803d5f2343f3fc3d9dcca069c7
2026-07-25-client-settings-locale-theme.zh.md: b1536a1515fa7022320934dfdabf94c815e8ba04
2026-07-25-client-settings-locale-theme.zh.md: 9cf973aee29cc1072c2fdaf1ce67e0b8395f1d90
@@ -55,11 +55,11 @@ root
└─ models (order 10) ui-models 注册
```
section/item contribution 使用 `ctx.slots.inject()`,不依赖 client manifest 的 apply 顺序;本地化 label 走 [全量接入 Note](../../implemented/architecture/2026-07-30-client-locale-full-rollout.md) 的 label thunk。SlotMap 类型分家:trigger/header/close/section 正家在 ui-settings contract(消费 general/models 均依赖壳,无环);`settings.general.item` 正家在 locale 包——它是全部 item 注册方的最低公共依赖(设置行必带文案),而声明方 general 的 contract 对 locale/ui-theme 不可达(会成环);ui-theme 经 re-export seam 消费。
section/item contribution 使用 `ctx.slots.inject()`,不依赖 client manifest(元数据清单)的 apply 顺序;本地化 label 走 [全量接入 Note](../../implemented/architecture/2026-07-30-client-locale-full-rollout.md) 的 label thunk。SlotMap 类型分家:trigger/header/close/section 正家在 ui-settings 约定(消费 general/models 均依赖壳,无环);`settings.general.item` 正家在 locale 包——它是全部 item 注册方的最低公共依赖(设置行必带文案),而声明方 general 的约定对 locale/ui-theme 不可达(会成环);ui-theme 经 re-export seam 消费。
### slot 声明是一等可注入等待对象
`SlotsService.inject()` 直接等待有类型约束的 ledger key;它不会将声明桥接为合成的 `slot:<name>` Cordis 服务。回调会跟随声明折叠与重新声明,而其控制器仍归贡献方插件 fiber 所有;直接向未声明 slot 注册仍会大声失败。这除了陈旧 disposer 在位机器和容易因拼写错误出错的平行服务命名空间。完整的生命周期与失败约见 [slot 声明注入决策](../../implemented/architecture/2026-08-05-slot-declaration-injection.md)。
`SlotsService.inject()` 直接等待有类型约束的 ledger key;它不会将声明桥接为合成的 `slot:<name>` Cordis 服务。回调会跟随声明撤销与重新声明,而其控制器仍归贡献方插件 fiber 所有;直接向未声明 slot 注册仍会显式报错。这除了陈旧 disposer 在位判定机制和容易因拼写错误出错的平行服务命名空间。完整的生命周期与失败约见 [slot 声明注入决策](../../implemented/architecture/2026-08-05-slot-declaration-injection.md)。
### 服务契约
@@ -127,4 +127,4 @@ Locale 内置中文和 English`setLocale`/`setTheme` 是唯一写入口,未
## 风险
slot 声明与 contribution 的 apply 顺序不固定,所有 section/item 注册方必须使用 `ctx.slots.inject()`,而不能以服务或本地 disposer 作为在位信号。service event 可能早于行首次渲染,功能行 store 的 init 与 inject attach 都必须从 getter 对齐当前 snapshot。`settings.general.item` 的重复合并副本(locale、ui-theme)与 ui-settings 正家必须逐字一致,漂移即三处一起改。Layout 卸载时必须清理自己设置的全局属性,ThemeService dispose 时必须移除 matchMedia 监听,避免 HMR 后残留。
slot 声明与 contribution 的 apply 顺序不固定,所有 section/item 注册方必须使用 `ctx.slots.inject()`,而不能以服务或本地 disposer 作为在位信号。服务事件可能早于行首次渲染,功能行 store 的 init 与 inject attach 都必须从 getter 对齐当前 snapshot。`settings.general.item` 的重复合并副本(locale、ui-theme)与 ui-settings 正家必须逐字一致,漂移即三处一起改。Layout 卸载时必须清理自己设置的全局属性,ThemeService dispose 时必须移除 matchMedia 监听,避免 HMR(热模块替换)后残留。
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/cookbook/adding-an-llm-adapter.md
adding-an-llm-adapter.md: 4fcc646ed2eea8a6170027d01761887aa28b0045
adding-an-llm-adapter.zh.md: 35a671416f8160a6187a06f3dbd614dfe4faa778
adding-an-llm-adapter.zh.md: eeeb84583fb9951745083b60385547d14f461266
+1 -1
View File
@@ -36,7 +36,7 @@ export function apply(ctx: Context, config: Config) {
## 实现结构
让协议类型、请求序列化、传输解析、分片转换和适配器类分别承担独立职责;[`llm-deepseek`](../../packages/llm/llm-deepseek/README.md) 是参考布局。
让协议格式(wire format类型、请求序列化、传输解析、分片转换和适配器类分别承担独立职责;[`llm-deepseek`](../../packages/llm/llm-deepseek/README.md) 是参考布局。
## 验证
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/defensive-patterns.md
defensive-patterns.md: 6dc1708f0b9bbcb00ad4774006d6d60059accf6a
defensive-patterns.zh.md: 4c376f1a5f2f6d5fbe85124ce473bc7af5ad4c8d
defensive-patterns.zh.md: 2ff5cba2a94c5149f2b92b37845d23211ee7989d
+2 -2
View File
@@ -10,11 +10,11 @@
## 跨 seam 契约两侧都要遵守
当一个实现边界接收到同一结果的多种表示时,应在跨越公共 seam 前将其规范化。`LlmAdapter.stream()` 的实现可以抛出异常或发出 `finish {kind:'error'|'aborted'}`,但 `LlmService.stream()` 只会通过终止 finish chunk 暴露模型请求失败;middleware 与消费方缺陷仍会抛出。这使消费方不必猜测捕获的异常究竟来自提供方、包装层、chunk 日志记录还是自身组装逻辑。请在类型定义处记录规范化契约;通过真实消费方覆盖每种来源形式。
当一个实现边界接收到同一结果的多种表示时,应在跨越公共 seam 前将其规范化。`LlmAdapter.stream()` 的实现可以抛出异常或发出 `finish {kind:'error'|'aborted'}`,但 `LlmService.stream()` 只会通过终止 finish 分片暴露模型请求失败;middleware 缺陷与消费方缺陷仍会以异常形式抛出。这使消费方不必猜测捕获的异常究竟来自提供方、包装层、chunk 日志记录还是自身组装逻辑。请在类型定义处记录规范化后的约定;通过真实消费方覆盖每种来源形式。
## 异步状态不是同步状态
`agent.followup()` 没有逐消息的完成状态或结果;后台任务的完成与轮次边界存在竞争;`reader.close()` 在 EOF 和 dispose(资源释放)两种情况下都会触发。切勿把 `agent/status``whenIdle()` 当作某次 `followup()` 的结果:多条已排队的后续消息、steering(中途引导)和注入工作可能共用同一个 `running` 区间,而取消或资源释放可能丢弃尚未启动的项。真正拥有一次运行的自动化调用方必须显式定义其区间——例如从消息的持久 inbox 回执到整个 agent 下一次进入 `idle`——并将选取的任何输出描述为整个区间的输出,而不是把因果关系归于该消息。这条守则是双向的:如果等待的转换永远不会发生,等待就会挂起,因此应显式处理「无需等待」的分支。
`agent.followup()` 没有逐消息的完成状态或结果;后台任务的完成与轮次边界存在竞争;`reader.close()` 在 EOF 和 dispose(资源释放)两种情况下都会触发。切勿把 `agent/status``whenIdle()` 当作某次 `followup()` 的结果:多条已排队的后续消息、steering(中途引导)和注入工作可能共用同一个 `running` 区间,而取消或资源释放可能丢弃尚未启动的项。真正拥有一次运行的自动化调用方必须显式定义其区间——例如从消息的持久 inbox 回执到整个 agent(智能体)下一次进入 `idle`——并将选取的任何输出描述为整个区间的输出,而不是把因果关系归于该消息。这条守则是双向的:如果等待的转换永远不会发生,等待就会挂起,因此应显式处理「无需等待」的分支。
## Dispose 必须达到完全停稳,而不仅仅是请求停止
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/user/develop/basic/config.md
config.md: 02998c32415b5ba7acf82700034cabc1f7314f33
config.zh.md: 42af432b36d8f82871aa7d7b6a3a2eaf7427cdae
config.zh.md: 161af5d6703b4cc77d63443a5a80846593d1c7fd
+1 -1
View File
@@ -93,7 +93,7 @@ export interface Config {
### 配置错误要响亮
在 schema 中表达自身完备的约束,使无效配置在插件加载时失败。对服务或已注册资源的引用需要依赖注入;[服务教程](../framework/service.md)会介绍这项约。
在 schema 中表达自身完备的约束,使无效配置在插件加载时失败。对服务或已注册资源的引用需要依赖注入;[服务教程](../framework/service.md) 会介绍这项约
## 配合 HMR
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/user/develop/basic/tool.md
tool.md: 8505bdaf6fcece3235b0302d54e82ee8aed3cbab
tool.zh.md: 1831afa9be74756cb9bb1e96515fefd3685ee4c2
tool.zh.md: c6c579c5da9dc545c2795e65331810528c37219f
+1 -1
View File
@@ -1,4 +1,4 @@
# 构建工具
# 开发一个工具
[English](tool.md) | 中文
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write examples/acp-agent/README.md
README.md: 61c6efafe9dde4f91385beebdfd426c57006187b
README.zh.md: 343e55722a4dda7c5cc5e0de6b5fecf250b9ab13
README.zh.md: 6eaf9fb5a91c737254e785d8601961b547e4e9ca
+1 -1
View File
@@ -9,7 +9,7 @@ pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
pnpm run demo:code-mode # same protocol with the Code Mode tool transport
```
该叶节点加载 ACP 应用、DeepSeek 适配器、受沙箱限制的 bash 与文件系统栈、一次性批准策略、压缩(compaction)、subagent、工作流、钩子、派生会话查询索引和重复守卫。应用为每次 `session/new` 创建一个新 agent,将会话持久化到 JSONL,并保持 stdout 只含协议内容。可选 overlay 可添加会话查询、文件系统溢出存储、Code Mode 或 Web 抓取。
该叶节点加载 ACP 应用、DeepSeek 适配器、受沙箱限制的 bash 与文件系统栈、一次性批准策略、压缩(compaction)、subagent、工作流、钩子、派生会话查询索引和重复守卫。应用为每次 `session/new` 创建一个新 agent,将会话持久化到 JSONL,并保持 stdout 只含协议内容。可选 overlay 可添加会话查询、文件系统 spill 存储、Code Mode 或 Web 抓取。
## 协议通道
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write examples/jsonrpc-agent/README.md
README.md: bcc1027d2edb30ab374dfa2ed13ad8e6360d923b
README.zh.md: 197c25d7b4f5645aeb7e92d8c36ef4a423beb2e8
README.zh.md: ce255e4dd70bf8c5c6edc51afbe03bb4c66560a0
+3 -3
View File
@@ -26,11 +26,11 @@
通过 Python SDK 的 `cordis` 选项或 `DSH_CORDIS_CONFIG` 传入配置路径。内置可执行文件已携带此文件中指定的每个插件;目标机器无需 Node.js。
## 持久工具变体
## 持久工具变体
[`persistent-tools.cordis.yml`](persistent-tools.cordis.yml) 是一个最小可运行变体,面向模型的能力严格只有:
- agent 独占、状态持久的 `bash`
- 所有者作用域内持久`bash`
- 提供 `view``create``str_replace``insert``str_replace_editor`
它组合本地 PTY、文件系统 intent 策略与会话沙箱策略。
它组合本地 PTY、文件系统意图策略与会话沙箱策略。
+1 -1
View File
@@ -3,4 +3,4 @@
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write native/README.md
README.md: 51c8da7b57df15e65b8e431ee18ce6cebb89d54b
README.zh.md: baf530157c6731893a66aea301c6dd962592defc
README.zh.md: 55dd5eb0a197bd970b33f7943877fb3826858128
+2 -2
View File
@@ -2,10 +2,10 @@
[English](README.md) | 中文
与 DeepSeek Harness 一同维护的原生源码和公开包。[`landlock-run/` workspace](landlock-run/README.md)负责 harness 使用的 Landlock 自限后执行启动器,包括其架构、由三个包组成的 npm 包家族、平台支持、开发工作流和[发布流程](landlock-run/docs/release.md)。
与 DeepSeek Harness 一同维护的原生源码和公开包。[`landlock-run/` workspace](landlock-run/README.md) 负责 harness 使用的 Landlock 自限后执行启动器,包括其架构、由三个包组成的 npm 包家族、平台支持、开发工作流和[发布流程](landlock-run/docs/release.md)。
## Workspace 与发布边界
`landlock-run/` 及其包属于仓库根 pnpm workspace,并共用根锁文件。开发和 CI 中的 harness 消费方直接使用当前 workspace 的入口包,因此启动器约变更与消费方更新可以在同一个改动中落地并一起测试。
`landlock-run/` 及其包属于仓库根 pnpm workspace,并共用根锁文件。开发和 CI 中的 harness 消费方直接使用当前 workspace 的入口包,因此启动器约变更与消费方更新可以在同一个改动中落地并一起测试。
主仓库的 `Landlock Run` 工作流为每个受支持架构构建并测试。`Landlock Run Release` 汇集这些原生产物,打包并验证三个 npm tarball,随后可选择以同一个启动器版本发布。入口包继续将平台包声明为 npm 可选依赖,因此 npm 仍然只会安装与用户操作系统和 CPU 匹配的包。

Some files were not shown because too many files have changed in this diff Show More