Merge remote-tracking branch 'origin/codex/fix-compact-agents-reinjection' into codex/fix-resume-baseline-dedup

# Conflicts:
#	.agents/notes/implemented/feature/2026-06-24-workspace-context.i18n.yaml
#	.agents/notes/implemented/feature/2026-06-24-workspace-context.md
#	.agents/notes/implemented/feature/2026-06-24-workspace-context.zh.md
#	docs/event-producer-consumer.md
#	examples/acp-agent/tests/snapshots/code-mode-workspace-context/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-context/session.jsonl
#	packages/context/workspace-context/README.i18n.yaml
#	packages/context/workspace-context/README.md
#	packages/context/workspace-context/README.zh.md
#	packages/context/workspace-context/src/index.ts
#	packages/context/workspace-context/src/state.ts
#	packages/context/workspace-context/tests/workspace-context.spec.ts
This commit is contained in:
fz
2026-08-06 10:37:43 +08:00
829 changed files with 18643 additions and 15293 deletions
@@ -6,7 +6,7 @@
## 生命周期
完整基线会在全新会话第一 `agent/step` 注入。它先读取 `$DSH_HOME/AGENTS.md`,随后针对项目根目录到 `agent.session.header.cwd` 的每个目录,先读取每个现有基础候选文件,再读取每个现有本地 overlay 候选文件。同一目录中,如果候选文件在去除首尾空白后字节完全一致,就会按已配置顺序折叠到最早候选文件,因此 `CLAUDE.md` 若只是复制同级 `AGENTS.md`,只会渲染一次。这条持久的带来源 `user/message` 与被认领的提示词进入同一个请求。恢复的 loop 会在该基线仍可见,且其发现、优先顺序和预算标识与当前配置匹配时保留它,然后只追加由当前基线预算选中的转换。不兼容的可见基线会被一条重新组合的完整基线取代,其面向模型的引言会明确说明替换关系;如果当前候选集为空,这条基线会显式清除先前的指令。如果后续表层替换(例如压缩(compaction))遮蔽了该基线,面向模型请求的 `system-prompt/assemble` 会在 loop 对该请求创建快照之前,重新组合并注入当前指令链;仅检查组装不会改变会话
每个实时会话第一次符合条件的 `agent/pre-step` 会组合基线。当下游决策让非空的第一步批次进入时,插件会将基线折入最终批次、紧随已领取的直接提示词之后,使直接提示词与持久基线一同进入步骤 1,并共同抵达第一次请求。reject 或空的第一步决策会将基线留在 agent 的 `next-step` inbox,等待后续唤醒。loader 先读取 `$DSH_HOME/AGENTS.md`,随后针对项目根目录到 `agent.session.header.cwd` 的每个目录,先读取每个现有基础候选文件,再读取每个现有本地 overlay 候选文件。同一目录中,如果候选文件在去除首尾空白后字节完全一致,就会按已配置顺序折叠到最早候选文件,因此 `CLAUDE.md` 若只是复制同级 `AGENTS.md`,只会渲染一次。若之前排队的 workspace 上下文仍在等待,插件会删除并替换该确切 inbox 条目,而不会不断累积副本。恢复后的会话会保留一条兼容的可见基线,并只追加当前文件的转换;如果发现、优先级、项目根目录或预算标识发生变化,则会将一条明确取代旧基线的完整基线折入进入步骤的批次
该插件还会监听 `tools/post-execute` 中成功的第一方 `read``write``edit` 调用。每次 touch 都会检查新达到的后代 scope 以及之前加载的每个 scope。每个已配置候选名称都是所在目录中的独立 scope:新出现的文件通过结果的 `additionalContexts` 附加;已改变文件追加替换;文件消失或成为同一目录中较早候选文件的重复项时,追加移除通知。原生调用与 Code Mode 子分派共享该路径:`run_code` 将每个嵌套上下文延迟到外层结果,因此 loop 仍会在工具调用/结果相邻关系完成后追加更新。这种发现跟随结构化文件系统活动,而不是 shell `cd`,因为每次本地 bash 调用都启动新 shell,解析任意 shell 语法也不可靠。
@@ -48,11 +48,11 @@ These instructions apply to work under `packages/app`. Use them as guidance when
## 状态与刷新
模型可见文本不含隐藏状态标记。每个基线或动态上下文事件改为携带带类型的 `workspace-instructions` 来源,其中包含 `{ action, scope, path, digest? }` 变更列表;完整基线还会携带 `baseline: true`,以及根据规范化的发现、优先顺序和预算配置派生的 `baselineIdentity`。每次相关工具 touch 时,插件会从可见会话事件重建已加载状态,并叠加一个短暂内存 pending 窗口,用于不可变顶层 `tools/result` 上存在但 loop 尚未追加的上下文。匹配的持久 `user/message` 会确认 pending 转换。如果所属 `step/end` 在匹配上下文进入日志之前到达,插件会清除 pending 转换及其版本快速路径,使下一次成功 touch 可以重新加载。嵌套 Code Mode 结果会在外层执行 token 下暂存 pending 变更,用于抑制同次运行中的重复项;外层结果会回滚该状态,再只重新提交经过外层策略的上下文。
模型可见文本不含隐藏状态标记。每个基线或动态上下文事件改为携带带类型的 `workspace-instructions` 来源,其中包含 `{ action, scope, path, digest? }` 变更列表;完整基线还会携带 `baseline: true`,以及规范化的发现、优先级、项目根目录和预算配置派生的 `baselineIdentity`匹配的持久 `user/message` 会确认已排队基线及其候选版本。进入步骤的 pre-step 会把新组合的上下文折入最终批次,位置紧随已领取的消息,并移除 inbox 中仍待处理的副本;reject 则让当前上下文继续排队。若监听器改写掉已领取的 workspace 消息,又没有让替代消息进入,后续边界会重新组合当前上下文。每次相关工具 touch 时,插件会从可见会话事件重建已加载状态,并叠加一个短暂内存 pending 窗口,用于不可变顶层 `tools/result` 上存在但 loop 尚未追加的上下文。如果所属 `step/end` 在匹配的动态上下文进入日志之前到达,插件会清除 pending 转换及其版本快速路径,使下一次成功 touch 可以重新加载。嵌套 Code Mode 结果会在外层执行 token 下暂存 pending 变更,用于抑制同次运行中的重复项;外层结果会回滚该状态,再只重新提交经过外层策略的上下文。
路径与 SHA-1 内容 digest 都未变时,不会重复注入。每会话、每 scope 提供方 cache 只存储 `{ path, version, digest, trimmedDigest }`:当提供方的不透明 `FsVersion` 与有效可见状态都匹配时,对账会跳过内容读取;版本改变会在任何模型可见更新之前触发有界读取与 SHA-1 确认。`trimmedDigest` 是针对去除空白后内容的 SHA-1,也是每目录重复 key,因此较早候选文件与某个未更改文件的内容收敛后,后者仍可被移除。恢复可行,因为 SHA-1 状态持久化在带类型的来源中,而空的内存版本 cache 只会导致一次确认读取。压缩会在 scope 的上下文事件离开可见表层后重新启用它,即使缓存版本未变。移除是 tombstone,因此候选文件之后重新出现时会重新加载。只有在字节预算内实际渲染的模型可见变更才会进入来源、pending 状态和版本 cache;已省略变更仍可在后续 touch 处理,而相同 digest 的版本刷新只更新提供方 cache。
路径与 SHA-1 内容 digest 都未变时,不会重复注入。每会话、每 scope 提供方 cache 只存储 `{ path, version, digest, trimmedDigest }`:当提供方的不透明 `FsVersion` 与有效可见状态都匹配时,对账会跳过内容读取;版本改变会在任何模型可见更新之前触发有界读取与 SHA-1 确认。`trimmedDigest` 是针对去除空白后内容的 SHA-1,也是每目录重复 key,因此较早候选文件与某个未更改文件的内容收敛后,后者仍可被移除。恢复可行,因为 SHA-1 状态持久化在带类型的来源中,而空的内存版本 cache 只会导致一次确认读取。压缩compaction会在 scope 的上下文事件离开可见表层后重新启用它,即使缓存版本未变。移除是 tombstone,因此候选文件之后重新出现时会重新加载。只有在字节预算内实际渲染的模型可见变更才会进入来源、pending 状态和版本 cache;已省略变更仍可在后续 touch 处理,而相同 digest 的版本刷新只更新提供方 cache。
初始基线事件自身不会被改写。其带类型的变更仅在该事件仍位于可见会话表层时才是权威状态。恢复的 loop 或插件热重挂会保留一条兼容的可见基线,并在第一个请求前对账其动态 scope 以及当前完整渲染所保留的基线 scope:未变文件不追加任何内容,而 agent 离线期间新增、编辑移除的文件会追加带类型的 `set``replace``remove` 转换。当前基线预算省略的文件不会因恢复对账而进入历史;先前可见但已不在保留集内的文件会收到移除转换。如果可见基线标识不兼容,一条完整的当前基线会明确取代此前的基线。如果已无带类型的基线可见(例如表层替换后),面向模型请求的提示词组装会重新组合完整的当前基线,并在注入前立即重新检查取消状态、可见性和当前替换代次。并发准备可以并行读取,但只有第一次提交会将一条基线排入队列;仅检查组装绝不会恢复基线。内存中的 scope 标记和提供方版本 cache 只负责选择探测对象并加速探测。没有文件 watcher,因此磁盘变更会在下一次成功 `read``write``edit` touch 时可见,也会在模型请求恢复被遮蔽的基线时或恢复 loop 准备基线时可见。
初始基线事件自身不会被改写。其带类型的变更仅在该事件仍位于可见会话表层时才是权威状态。当压缩遮蔽该事件时,下一次进入步骤的 pre-step 会组合当前基线,并在同一请求中记录它;也可以改由一次成功的文件系统 touch 重新添加未变的基线 scope,或追加其替换或移除。内存中的 scope 标记和提供方版本 cache 只负责选择探测对象并加速探测。恢复或插件热重挂后的第一次 pre-step 会保留兼容的可见基线,并将它与当前完整渲染所保留的文件进行比较。未变化和被预算省略的文件不追加任何内容agent 离线期间新增、编辑移除或不再属于预算保留集的文件会追加 `set``replace``remove` 转换。不兼容的可见基线会被一条完整的当前基线取代;如果没有候选文件,这条当前基线会是显式空基线。没有文件 watcher,因此磁盘变更会在下一次成功 `read``write``edit` touch 时可见,也会在恢复后的会话对账其基线时,或进入步骤的 pre-step 恢复被遮蔽的基线时可见。
## 配置
@@ -83,7 +83,7 @@ export interface Config {
#### 模型看到的内容
全新会话的第一请求包含一条持久 user 角色消息,其中按从宽泛到具体的顺序包含有界用户全局指令与项目指令链。恢复后的请求会在该消息仍可见且兼容时保留它,然后只追加预算选中的转换。不兼容的可见基线之后会跟随一条按当前优先顺序排列的完整替换基线;表层替换将基线遮蔽后的第一个请求会收到一条重新组合的完整基线
第一请求的派生历史中包含一条持久 user 角色消息,其中按从宽泛到具体的顺序包含有界用户全局指令与项目指令链。可见基线兼容时,恢复会复用该消息
##### 基线指令模板
@@ -103,11 +103,11 @@ Instructions from: AGENTS.md
#### Token 影响
渲染后基线保留在派生历史中直到表层替换将其遮蔽;随后会在下一个请求前追加一条重新组合的基线`maxBytes` 会限制每条完整消息,较宽泛文件在最具体文件截断之前被省略,空指令链不产生 token。
渲染后基线只追加一次,并保留在派生历史中直到压缩`maxBytes` 会限制完整消息,较宽泛文件在最具体文件截断之前被省略,空指令链不产生 token。
#### KV Cache 影响
仅追加,位于现有可复用前缀之后。全新请求或压缩后的请求可能追加完整基线;恢复后的请求保留其可见基线,只追加检测到的转换,因此指令、优先级、cwd、候选文件或字节预算变更会从该历史位置起影响缓存复用。
仅追加,位于现有可复用前缀之后。可见基线标识兼容时,恢复会保持复用;不兼容的标识会追加一条完整的替代基线,因此发现、优先级、项目根目录或预算变更会从该历史位置起影响复用。
### 新发现的 scope 上下文
@@ -162,7 +162,7 @@ The previously loaded instructions from this file no longer apply.
## 已知限制与暂缓事项
- **发现跟随结构化 fs 工具,而非 shell 导航**:更改目录的 `bash` 命令不会触发嵌套指令发现,因为 shell 语法与每次调用 shell 状态不是可靠的文件系统 seam。
- **刷新由事件驱动**:没有 watcher;外部编辑会在下一次成功的第一方 `read``write``edit`可见,也会在面向模型请求的提示词组装恢复被遮蔽的基线时或恢复 loop 准备基线时可见。
- **刷新由 touch 驱动**:没有 watcher;外部编辑会在下一次成功的第一方 `read``write``edit`、恢复过程对账可见基线时,或进入步骤的 pre-step 恢复被遮蔽的基线时可见。
- **候选语义有意保持简单**:不解释小写名称、`.claude/rules/``@path` import;项目 scope 默认加载 `AGENTS.local.md``CLAUDE.local.md` overlay,但用户全局 `$DSH_HOME` scope 没有本地 overlay,其他自定义名称需要显式候选配置。
- **每目录去重基于内容**:只有在去除首尾空白后字节完全一致时,才折叠同级候选文件。`CLAUDE.md` 若 symlink 到同级 `AGENTS.md`,会解析为相同内容,并像任何重复项一样折叠;从 `AGENTS.md` 漂移的独立实体副本则会与它一起完整加载。
- **Symlink 指令文件会跨越信任边界跟随**:最终组件是 symlink 的候选文件会被解析并加载其目标,因此克隆仓库可以将树外文件内容呈现为较低优先级的工作区指引(它绝不会覆盖 system、developer 或用户直接下达的指令)。加载不受信任仓库时,请用文件系统策略门禁或 OS 沙箱限制 `ctx.fs`