refactor(workspace-context): fold context into the entering pre-step batch

This commit is contained in:
_Kerman
2026-08-05 14:46:48 +08:00
parent b76543cc1c
commit 757adac62d
19 changed files with 179 additions and 124 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-07-31-claimed-pre-step-inbox-lifecycle.md
2026-07-31-claimed-pre-step-inbox-lifecycle.md: ed56723ab8dcaaba9fbcd8e4c52ecc264141df2e
2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md: aecfece99c49a31d429fc3229432911dc4b83f82
2026-07-31-claimed-pre-step-inbox-lifecycle.md: 6aa829ee8489ee0d19c4af16e527774f8751eea8
2026-07-31-claimed-pre-step-inbox-lifecycle.zh.md: 03dc58fed3ecc3842faab3727286c6ceda9399a5

View File

@@ -20,7 +20,7 @@ The durable inbox remains two `UserMessage[]` lists addressed by `MessageId`. `a
The two event surfaces have separate consumers. Observers following one message use `agent/inbox/inserted`, `claimed`, and `discarded`. Whole-queue consumers, including the Web queue projection and reconnect baseline, use the durable `agent/inbox/spliced` stream; UI edits and removals route through `Inbox.splice()` or another Inbox mutation method so the same projection records every change.
Plugins that need current-step atomic rewriting return messages from `agent/pre-step`. Plugins that only need later context may mutate `agent.inbox` directly. Workspace context uses that weaker timing deliberately: it prepends its current sourced context to `next-step`, replaces an exact still-pending predecessor, and does not alter the current pre-step decision.
Plugins that need current-step atomic rewriting return messages from `agent/pre-step`. Plugins that only need later context may mutate `agent.inbox` directly. Workspace context uses both paths: asynchronous filesystem projections stage one replaceable `next-step` item, while the next entering pre-step folds that item or a newly composed baseline into its final batch and removes the pending copy. Rejection keeps the item queued.
The archived [addressable queue occurrence decision](../../archived/feature/2026-07-29-addressable-queue-operations.md) describes the superseded occurrence-wrapper design. `MessageId` now owns addressability, while the retained Host queue mirror derives its snapshots from the durable splice projection.
@@ -34,7 +34,7 @@ The archived [addressable queue occurrence decision](../../archived/feature/2026
## Verification
Agent-loop coverage pins turn-start-before-claim-before-pre-step ordering, exact live event payloads, balanced no-step rejection, final-batch rewriting, input inserted after a claim, listener failure, and cancellation. Inbox and consumer tests pin pure claim deletions, canceled ordinary removals, workspace-context delayed insertion and replacement, plan/goal/hook behavior, UI cleanup, compaction, checkpointing, and resumed durable projection. Generated event and type catalogs expose only the new seam and payloads.
Agent-loop coverage pins turn-start-before-claim-before-pre-step ordering, exact live event payloads, balanced no-step rejection, final-batch rewriting, input inserted after a claim, listener failure, and cancellation. Inbox and consumer tests pin pure claim deletions, canceled ordinary removals, workspace-context staging, replacement, and same-step entry, plan/goal/hook behavior, UI cleanup, compaction, checkpointing, and resumed durable projection. Generated event and type catalogs expose only the new seam and payloads.
## Consequences

View File

@@ -20,7 +20,7 @@ Status: implemented
两类事件表面服务不同消费方。跟踪单条消息的观察方使用 `agent/inbox/inserted``claimed``discarded`。包括 Web 队列投影和重连基线在内的整体队列消费方使用持久 `agent/inbox/spliced`UI 编辑与移除经 `Inbox.splice()` 或其他 Inbox 变更方法进入,从而让同一投影记录所有变化。
必须对当前步骤进行原子改写的插件从 `agent/pre-step` 返回消息。只需要稍后上下文的插件可以直接修改 `agent.inbox`。Workspace context 刻意采用较弱的时序:它把当前带来源上下文 prepend 到 `next-step`,替换仍在等待的确切前序消息,并且不修改当前 pre-step 决策
必须对当前步骤进行原子改写的插件从 `agent/pre-step` 返回消息。只需要稍后上下文的插件可以直接修改 `agent.inbox`。Workspace context 同时使用两条路径:异步文件系统投影会暂存一条可替换的 `next-step` 消息,而下一次进入步骤的 pre-step 会把该消息或新组合的基线折入最终批次并移除仍待处理的副本。reject 会让该条目继续排队
已归档的[可寻址队列项决策](../../archived/feature/2026-07-29-addressable-queue-operations.md)描述了已被取代的单次出现 wrapper 设计。现在由 `MessageId` 负责寻址,而保留的 Host 队列镜像根据持久 splice 投影派生快照。
@@ -34,7 +34,7 @@ Status: implemented
## 验证
Agent-loop 覆盖固定先 `turn/start`、再领取、后 pre-step 的顺序、实时事件的确切载荷、边界平衡的无步骤 reject、最终批次改写、领取后插入的输入、监听器失败与取消。Inbox 和消费方测试固定纯领取删除、普通删除的 canceled 结果、workspace-context 的延后插入与替换、plan/goal/hook 行为、UI 清理、压缩、检查点以及恢复后的持久投影。生成的事件与类型目录只公开新的 seam 与载荷。
Agent-loop 覆盖固定先 `turn/start`、再领取、后 pre-step 的顺序、实时事件的确切载荷、边界平衡的无步骤 reject、最终批次改写、领取后插入的输入、监听器失败与取消。Inbox 和消费方测试固定纯领取删除、普通删除的 canceled 结果、workspace-context 的暂存、替换与同一步骤进入、plan/goal/hook 行为、UI 清理、压缩、检查点以及恢复后的持久投影。生成的事件与类型目录只公开新的 seam 与载荷。
## 后果

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# 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: 5b55f9173d11ad248c29fc736df092cb3600d7ef
2026-06-24-workspace-context.zh.md: c6f3ce9d52e537f04765123124a1d3e5cd81249d
2026-06-24-workspace-context.md: df7ae58f8a42a8c1aac8113a429ef0f50b2fb795
2026-06-24-workspace-context.zh.md: aecc57a5c63b4c6282260863f4183944c14b5cf8

View File

@@ -28,9 +28,9 @@ The user-global file is fixed at `$DSH_HOME/AGENTS.md`, is not affected by eithe
### Baseline Injection
At the first `agent/pre-step` of an agent-loop instance, the plugin composes one sourced user-role baseline and prepends it directly to the agent's next-step inbox without changing the current decision. It loads the user-global file first, then finds the project root by walking upward from `agent.session.header.cwd` to a configured root marker (default `.git`), then loads the configured candidates from each directory from the root to the cwd. A `.git` file and a `.git` directory are both valid markers, covering linked worktrees and submodules. Without a marker, the cwd itself is the root. Because the loop claims before pre-step, the baseline may enter a later request.
At the first `agent/pre-step` of an agent-loop instance, the plugin composes one sourced user-role baseline. When the downstream decision enters a nonempty first-step batch, the plugin folds the baseline into that final batch right after the claimed prompt, so it becomes durable with the direct prompt and reaches the first request. Rejection or an empty first-step decision leaves the baseline in the next-step inbox for a later wakeup. The plugin loads the user-global file first, then finds the project root by walking upward from `agent.session.header.cwd` to a configured root marker (default `.git`), then loads the configured candidates from each directory from the root to the cwd. A `.git` file and a `.git` directory are both valid markers, covering linked worktrees and submodules. Without a marker, the cwd itself is the root.
When a later pre-step claims and enters it, the baseline becomes a durable `user/message` with a typed `workspace-instructions` source. Its `baseline: true` marker distinguishes the complete startup or resume baseline from later deltas, and its change list persists the included scopes and content digests. If a previously queued workspace baseline is still pending, the plugin removes that exact message and prepends its replacement instead of accumulating duplicates.
The baseline becomes a durable `user/message` with a typed `workspace-instructions` source. Its `baseline: true` marker distinguishes the complete startup or resume baseline from later deltas, and its change list persists the included scopes and content digests. If a previously queued workspace baseline is still pending, the plugin removes that exact message and prepends its replacement instead of accumulating duplicates.
A resumed agent creates a new loop instance and injects a baseline composed from current files before its first request. This permits current baseline content on resume without mutating an earlier history event. A resume and a hot plugin remount both face a log that may already hold a baseline; they are told apart by `agent/session-start`, which a startup or resume emits before the first step while a remount attaches to an already-live session and never sees it. A remount retains the existing baseline only when its typed event remains in the current visible surface, and still rebuilds scope and provider-version tracking from current files. If compaction has shadowed that event, the remount injects a current baseline. A resume always re-composes.
@@ -68,7 +68,7 @@ There is intentionally no watcher. Detection occurs at the next successful struc
**Use a global `ctx.systemPrompt.section()`.** Rejected because one Cordis context can host sessions with different cwd values, while repository-owned text is lower-authority context rather than top-authority provider system content.
**Return the baseline in every `agent/pre-step` decision.** Rejected because workspace context needs only eventual next-step delivery. Direct inbox replacement keeps one pending baseline without coupling it to the request already claimed before the listener ran.
**Always leave prepared workspace context in the inbox.** Rejected because context prepared during pre-step would then survive the current request and start a second model step by itself. The inbox remains the staging and rejection fallback, while an entering pre-step owns atomic delivery with its final batch.
**Load both `AGENTS.md` and `CLAUDE.md` in one directory.** Rejected because repositories in transition commonly duplicate guidance across both files. Ordered candidates make precedence explicit and configurable.

View File

@@ -28,9 +28,9 @@ Status: implemented
### 基线注入
在 agent loop智能体循环实例的第一个 `agent/pre-step`,插件会组合一条带来源的 user 角色基线,将它直接 prepend 到 agent 的 next-step inbox且不修改当前决策。它先加载用户全局文件,再从 `agent.session.header.cwd` 向上遍历至配置的根标记(默认为 `.git`)以确定项目根目录,随后从根目录至 cwd 的每级目录加载已配置候选项。`.git` 文件与 `.git` 目录都是有效标记,因而能覆盖链接 worktree 和 submodule。找不到标记时cwd 本身就是根目录。由于循环先领取、后运行 pre-step该基线可能进入后续请求。
在 agent loop智能体循环实例的第一个 `agent/pre-step`,插件会组合一条带来源的 user 角色基线。当下游决策让非空的第一步批次进入时插件会将基线折入最终批次、紧随已领取的直接提示词之后使其与直接提示词一同成为持久记录并抵达第一次请求。reject 或空的第一步决策会将基线留在 next-step inbox等待后续唤醒。插件先加载用户全局文件,再从 `agent.session.header.cwd` 向上遍历至配置的根标记(默认为 `.git`)以确定项目根目录,随后从根目录至 cwd 的每级目录加载已配置候选项。`.git` 文件与 `.git` 目录都是有效标记,因而能覆盖链接 worktree 和 submodule。找不到标记时cwd 本身就是根目录。
后续 pre-step 领取并让它进入步骤时,该基线成为一条持久 `user/message`,并携带带类型的 `workspace-instructions` 来源。其 `baseline: true` 标记将完整的启动或恢复基线与后续增量区分开来,变更列表则持久保存已纳入的作用域和内容 digest。若先前排队的 workspace 基线仍在等待,插件会删除该确切消息并 prepend 替代值,而不会累积副本。
该基线成为一条持久 `user/message`,并携带带类型的 `workspace-instructions` 来源。其 `baseline: true` 标记将完整的启动或恢复基线与后续增量区分开来,变更列表则持久保存已纳入的作用域和内容 digest。若先前排队的 workspace 基线仍在等待,插件会删除该确切消息并 prepend 替代值,而不会累积副本。
恢复 agent 会创建新的循环实例,并在其第一次请求前注入由当前文件组合的基线。这样,恢复时可以使用当前基线内容,而无需修改先前的历史事件。恢复与插件热重挂都会面对日志中可能已存在基线的情况;二者通过 `agent/session-start` 区分:启动或恢复会在第一步前发出该事件,而热重挂附着到一个已存活的会话、永远不会看到它。只有当基线的类型化事件仍在当前可见表层中时,热重挂才保留既有基线,同时仍会根据当前文件重建 scope 与提供方版本跟踪。如果压缩compaction已遮蔽该事件热重挂会注入当前基线。恢复则始终重新组合。
@@ -68,7 +68,7 @@ shell 命令不会触发发现。本地 bash 调用会启动全新的 shell
**使用全局 `ctx.systemPrompt.section()`。** 不予采纳,因为同一个 Cordis 上下文可以承载 cwd 不同的多个会话,而仓库自身拥有的文本属于低权威用户上下文,不是最高权威的提供方系统内容。
**在每次 `agent/pre-step` 决策中返回基线。** 不予采纳,因为 workspace context 只需要最终进入后续步骤。直接替换 inbox 可以只保留一条待处理基线,也不会把它耦合到监听器运行前已经领取的请求
**始终把准备好的 workspace 上下文留在 inbox 中。** 不予采纳,因为在 pre-step 中准备的上下文会因此在当前请求结束后继续留存并自行启动第二个模型步骤。inbox 仍作为暂存区以及 reject 时的后备,而进入步骤的 pre-step 负责随最终批次原子投递
**在一个目录中同时加载 `AGENTS.md` 和 `CLAUDE.md`。** 不予采纳,因为正在迁移的仓库通常会在两个文件中重复指引。按顺序排列的候选项让优先级显式且可配置。

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/architecture.md
architecture.md: e852506d94fa67da92819879dba4dc546fce540b
architecture.zh.md: b4b34b29c75996dc79188943dc1999db71e1710c
architecture.md: bbf13606861ad6995fd5a8b2c64e323eabc869c6
architecture.zh.md: bea98a9d5936af9e11be6dffcb4d911e07665419

View File

@@ -158,7 +158,7 @@ A swappable capability usually has **interface / implementation / consumer** lay
Exceptions combine LLM interface/consumer, filesystem policy, web registries, and named skill/subagent providers. Subagents spawn fresh, fork a completed-turn prefix, or use ACP children ([subagent.md](core-data-structures/subagent.md)).
`dsh-workspace-context` prepends its baseline to the next-step inbox on first `agent/pre-step`, replacing a pending predecessor, and adds `ctx.fs` changes after tools. Because claim precedes pre-step, the baseline may miss the current request ([decision](../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)). `dsh-paths` owns shared paths.
`dsh-workspace-context` composes its baseline on the first `agent/pre-step` and folds it into the final entering batch right after the claimed prompt, so it reaches the first request with the direct prompt; rejection keeps it in the next-step inbox. Filesystem changes projected after tools are likewise folded into the next entering pre-step instead of creating a later context-only step ([decision](../.agents/notes/implemented/feature/2026-06-24-workspace-context.md)). `dsh-paths` owns shared paths.
### Bundles And Apps

View File

@@ -158,7 +158,7 @@ idle inject:
例外情况包括 LLM大语言模型合并接口和消费方、文件系统整合策略、web 使用注册表、skill 和 subagent 使用具名提供方。subagent 可以通过 spawn 创建全新实例、fork 一个已完成轮次的前缀,或使用 ACPAgent Client Protocol子 agent[subagent.md](core-data-structures/subagent.md))。
`dsh-workspace-context` 在第一次 `agent/pre-step` 将基线 prepend 到 next-step inbox替换仍在等待的前序消息并在工具执行后添加 `ctx.fs` 变更。由于领取发生在 pre-step 之前,该基线可能赶不上当前请求[决策](../.agents/notes/implemented/feature/2026-06-24-workspace-context.md))。`dsh-paths` 负责共享路径。
`dsh-workspace-context` 在第一次 `agent/pre-step` 组合基线并将它折入最终进入的批次、紧随已领取的直接提示词之后使其与直接提示词一同抵达第一次请求reject 则将它留在 next-step inbox。工具执行后投影的文件系统变更也会折入下一次进入步骤的 pre-step而不会另外创建稍后的纯上下文步骤[决策](../.agents/notes/implemented/feature/2026-06-24-workspace-context.md))。`dsh-paths` 负责共享路径。
### 组合包与应用

View File

@@ -9,13 +9,6 @@
{ "type": "finish", "reason": { "kind": "tool-calls" } }
]
},
{
"kind": "chunks",
"chunks": [
{ "type": "usage", "usage": { "inputTokens": 10, "outputTokens": 1 } },
{ "type": "finish", "reason": { "kind": "stop" } }
]
},
{
"kind": "chunks",
"chunks": [

View File

@@ -1,12 +1,12 @@
{"type":"session","version":0,"id":"b1e35a14-a592-44e6-bf23-b2496ad2bf7b","createdAt":1785014475001,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"agent/inbox/spliced","seq":0,"time":1785498825884,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Using ONE run_code program, call tools.read on nested/task.txt. After the program finishes, answer the workspace handshake question using the newly discovered instructions: What is the Code Mode workspace handshake?"}],"source":{"kind":"user"},"role":"user","id":"433d493a-0b85-4b92-8063-cfa19b8f7d10"}]}}
{"type":"agent/inbox/spliced","seq":0,"time":1785498825884,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Using ONE run_code program, call tools.read on nested/task.txt. After the program finishes, answer the workspace handshake question using the newly discovered instructions: What is the Code Mode workspace handshake?"}],"source":{"kind":"user"},"role":"user","id":"3b04578e-7b22-4b44-b4cd-ef9d4d26fe8b"}]}}
{"type":"turn/start","seq":1,"time":1785821441771,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":2,"time":1785498825916,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"agent/inbox/spliced","seq":3,"time":1785821441803,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nWorkspace snapshot root instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2119a7072358cc727f8d9c4cb7388e905b075fe6"}]},"role":"user","id":"5578ed99-ced0-42af-b8f6-651e8aaf72e5"}]}}
{"type":"step/start","seq":4,"time":1785122256264,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":5,"time":1785498825917,"data":{"content":[{"type":"text","text":"Using ONE run_code program, call tools.read on nested/task.txt. After the program finishes, answer the workspace handshake question using the newly discovered instructions: What is the Code Mode workspace handshake?"}],"source":{"kind":"user"},"role":"user","id":"433d493a-0b85-4b92-8063-cfa19b8f7d10"},"surfaceOp":"append"}
{"type":"user/message","seq":6,"time":1785730478198,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"adca0c36-3b6e-4d58-9076-c0b4e6681030"},"surfaceOp":"append"}
{"type":"session/title","seq":7,"time":1785730478198,"data":{"title":"Using ONE run_code program, call","messageSeqs":[5],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785901435161,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":4,"time":1785498825917,"data":{"content":[{"type":"text","text":"Using ONE run_code program, call tools.read on nested/task.txt. After the program finishes, answer the workspace handshake question using the newly discovered instructions: What is the Code Mode workspace handshake?"}],"source":{"kind":"user"},"role":"user","id":"3b04578e-7b22-4b44-b4cd-ef9d4d26fe8b"},"surfaceOp":"append"}
{"type":"user/message","seq":5,"time":1785901435161,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nWorkspace snapshot root instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2119a7072358cc727f8d9c4cb7388e905b075fe6"}]},"role":"user","id":"ac92e76e-4861-47a6-87f8-4e9ca904eb24"},"surfaceOp":"append"}
{"type":"user/message","seq":6,"time":1785730478198,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"d6d78330-05c0-4ebd-9e29-595df6440250"},"surfaceOp":"append"}
{"type":"session/title","seq":7,"time":1785730478198,"data":{"title":"Using ONE run_code program, call","messageSeqs":[4],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":8,"time":1785498825920,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":9,"time":1785730478199,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":10,"time":1785014475638,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
@@ -14,28 +14,21 @@
{"type":"assistant/chunk","seq":12,"time":1785014475639,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}}}}
{"type":"assistant/chunk","seq":13,"time":1785014475639,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":14,"time":1785014475639,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":15,"time":1785733131056,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"b6030d5a-27fb-42a4-b1a4-5e2893e4dfe2"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
{"type":"assistant/message","seq":15,"time":1785733131056,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"b9402d85-58bd-4881-b890-0b186f661671"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
{"type":"tool/call","seq":16,"time":1785733131056,"data":{"turn":1,"step":1,"callId":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}}
{"type":"tool/code-dispatch-start","seq":17,"time":1785733131109,"data":{"parentCallId":"call_workspace_read","subCallId":"call_workspace_read:code:1","name":"read","arguments":{"file_path":"nested/task.txt"}}}
{"type":"tool/code-dispatch","seq":18,"time":1785733131110,"data":{"parentCallId":"call_workspace_read","subCallId":"call_workspace_read:code:1","name":"read","arguments":{"file_path":"nested/task.txt"},"isError":false,"content":[{"type":"text","text":"<path>{{cwd}}/nested/task.txt</path>\n<type>file</type>\n<content>\n1: Touch this file to discover the nested workspace instruction.\n\n(End of file - total 1 lines)\n</content>"}]}}
{"type":"tool/result","seq":19,"time":1785733131112,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_workspace_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_read","content":[{"type":"text","text":"{\n \"path\": \"{{cwd}}/nested/task.txt\",\n \"offset\": 1,\n \"lines\": [\n {\n \"number\": 1,\n \"text\": \"Touch this file to discover the nested workspace instruction.\"\n }\n ],\n \"totalLines\": 1\n}"}],"isError":false}],"role":"user","id":"10d0cae5-8ed4-41a3-9696-04069be40100"}},"sourceEventSeqs":[16],"surfaceOp":"append"}
{"type":"tool/result","seq":19,"time":1785733131112,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_workspace_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_read","content":[{"type":"text","text":"{\n \"path\": \"{{cwd}}/nested/task.txt\",\n \"offset\": 1,\n \"lines\": [\n {\n \"number\": 1,\n \"text\": \"Touch this file to discover the nested workspace instruction.\"\n }\n ],\n \"totalLines\": 1\n}"}],"isError":false}],"role":"user","id":"bde1c12e-44d1-44f7-ba7e-868349ed2b05"}},"sourceEventSeqs":[16],"surfaceOp":"append"}
{"type":"step/end","seq":20,"time":1785733131112,"data":{"turn":1,"step":1}}
{"type":"agent/inbox/spliced","seq":21,"time":1785733131112,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
{"type":"agent/inbox/spliced","seq":22,"time":1785733131116,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nWorkspace snapshot root instruction.\n\n</system-reminder>"},{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2119a7072358cc727f8d9c4cb7388e905b075fe6"},{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]},"role":"user","id":"9324febc-9fee-483f-b70e-d0d52e90ac20"}]}}
{"type":"agent/inbox/spliced","seq":21,"time":1785733131112,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]},"role":"user","id":"29b0eb87-92d5-4915-ba64-7bd8133ed011"}]}}
{"type":"agent/inbox/spliced","seq":22,"time":1785733131116,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[],"outcome":"canceled"}}
{"type":"step/start","seq":23,"time":1785733131123,"data":{"turn":1,"step":2}}
{"type":"user/message","seq":24,"time":1785733131123,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nWorkspace snapshot root instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2119a7072358cc727f8d9c4cb7388e905b075fe6"}]},"role":"user","id":"5578ed99-ced0-42af-b8f6-651e8aaf72e5"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":25,"time":1785014475805,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":1}}}}
{"type":"assistant/chunk","seq":26,"time":1785014475806,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":27,"time":1785733131123,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"4e3a19b8-1c6f-4005-a336-272245160fa3"},"usage":{"inputTokens":10,"outputTokens":1}},"sourceEventSeqs":[25,26],"surfaceOp":"append"}
{"type":"step/end","seq":28,"time":1785733131123,"data":{"turn":1,"step":2}}
{"type":"agent/inbox/spliced","seq":29,"time":1785733131123,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":30,"time":1785733131134,"data":{"turn":1,"step":3}}
{"type":"user/message","seq":31,"time":1785733131134,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nWorkspace snapshot root instruction.\n\n</system-reminder>"},{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2119a7072358cc727f8d9c4cb7388e905b075fe6"},{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]},"role":"user","id":"9324febc-9fee-483f-b70e-d0d52e90ac20"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":32,"time":1785014475887,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":33,"time":1785014475887,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":0,"text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}}}
{"type":"assistant/chunk","seq":34,"time":1785014475887,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}}}}
{"type":"assistant/chunk","seq":35,"time":1785014475888,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":36,"time":1785014475930,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":37,"time":1785733131134,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"2cf342f3-fa45-4472-8304-f8c947e825f8"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[32,33,34,35,36],"surfaceOp":"append"}
{"type":"step/end","seq":38,"time":1785733131135,"data":{"turn":1,"step":3}}
{"type":"turn/end","seq":39,"time":1785733131135,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"user/message","seq":24,"time":1785733131123,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]},"role":"user","id":"29b0eb87-92d5-4915-ba64-7bd8133ed011"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":25,"time":1785014475805,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":26,"time":1785014475806,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}}}
{"type":"assistant/chunk","seq":27,"time":1785901435233,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}}}}
{"type":"assistant/chunk","seq":28,"time":1785901435233,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":29,"time":1785901435233,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":30,"time":1785901435233,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"**Code Mode workspace handshake:** `CODE_MODE_CONTEXT_OK`"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"add632ac-e646-4e50-84d3-96a084427a01"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[25,26,27,28,29],"surfaceOp":"append"}
{"type":"step/end","seq":31,"time":1785901435233,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":32,"time":1785901435234,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -19,16 +19,6 @@
{ "type": "finish", "reason": { "kind": "tool-calls" } }
]
},
{
"kind": "chunks",
"chunks": [
{ "type": "block-start", "index": 0, "blockType": "text" },
{ "type": "text-delta", "index": 0, "text": "DONE" },
{ "type": "block-end", "index": 0, "block": { "type": "text", "text": "DONE" } },
{ "type": "usage", "usage": { "inputTokens": 10, "outputTokens": 2 } },
{ "type": "finish", "reason": { "kind": "stop" } }
]
},
{
"kind": "chunks",
"chunks": [

View File

@@ -1,12 +1,12 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"agent/inbox/spliced","seq":0,"time":1785498790330,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Read nested/task.txt, then read scope</system-reminder>/task.txt with the read tool, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"c692a4e6-491f-49e2-8e50-9019b7c34ee7"}]}}
{"type":"agent/inbox/spliced","seq":0,"time":1785498790330,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Read nested/task.txt, then read scope</system-reminder>/task.txt with the read tool, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"22938d3b-c065-46c8-acb7-18f758285842"}]}}
{"type":"turn/start","seq":1,"time":1785821400350,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":2,"time":1785498790356,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"agent/inbox/spliced","seq":3,"time":1785821400378,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nRoot snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2e18766c26603608f321508caae00ea8f4434d59"}]},"role":"user","id":"ee54ddf0-5cfd-46f6-b7ab-465184a4a43b"}]}}
{"type":"step/start","seq":4,"time":1784903339799,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":5,"time":1785498790356,"data":{"content":[{"type":"text","text":"Read nested/task.txt, then read scope</system-reminder>/task.txt with the read tool, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"c692a4e6-491f-49e2-8e50-9019b7c34ee7"},"surfaceOp":"append"}
{"type":"user/message","seq":6,"time":1785730689193,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"0b6bc3b0-57bc-4a60-bc46-992b6a29b361"},"surfaceOp":"append"}
{"type":"session/title","seq":7,"time":1785730689193,"data":{"title":"Read nested/task.txt, then read scope</s","messageSeqs":[5],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785901433981,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":4,"time":1785498790356,"data":{"content":[{"type":"text","text":"Read nested/task.txt, then read scope</system-reminder>/task.txt with the read tool, then reply DONE."}],"source":{"kind":"user"},"role":"user","id":"22938d3b-c065-46c8-acb7-18f758285842"},"surfaceOp":"append"}
{"type":"user/message","seq":5,"time":1785901433982,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nRoot snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2e18766c26603608f321508caae00ea8f4434d59"}]},"role":"user","id":"ec039e95-6864-49ef-ad23-4f65b331dc29"},"surfaceOp":"append"}
{"type":"user/message","seq":6,"time":1785730689193,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt"},"role":"user","id":"cbea9bd9-3e08-48bf-951f-fe3e5aa4b5d9"},"surfaceOp":"append"}
{"type":"session/title","seq":7,"time":1785730689193,"data":{"title":"Read nested/task.txt, then read scope</s","messageSeqs":[4],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":8,"time":1785498790358,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":9,"time":1785730689194,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"assistant/chunk","seq":10,"time":1783778297070,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
@@ -14,42 +14,32 @@
{"type":"assistant/chunk","seq":12,"time":1784903339801,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}}}}
{"type":"assistant/chunk","seq":13,"time":1785498790358,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":14,"time":1785730689194,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":15,"time":1785730689194,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"942bc3f0-d564-4c12-bbf6-1a838295bc88"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
{"type":"assistant/message","seq":15,"time":1785730689194,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"cb229812-91a1-4aa6-8bce-d3b8d032f6dd"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
{"type":"tool/call","seq":16,"time":1785730689195,"data":{"turn":1,"step":1,"callId":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}}
{"type":"tool/result","seq":17,"time":1785730689204,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_workspace_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_read","content":[{"type":"text","text":"<path>{{cwd}}/nested/task.txt</path>\n<type>file</type>\n<content>\n1: snapshot task\n\n(End of file - total 1 lines)\n</content>"}],"isError":false}],"role":"user","id":"c03ad3ab-6cf8-404a-bf96-c676ae92401b"},"meta":{"path":"{{cwd}}/nested/task.txt","offset":1,"lines":[{"number":1,"text":"snapshot task"}],"totalLines":1}},"sourceEventSeqs":[16],"surfaceOp":"append"}
{"type":"tool/result","seq":17,"time":1785730689204,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_workspace_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_read","content":[{"type":"text","text":"<path>{{cwd}}/nested/task.txt</path>\n<type>file</type>\n<content>\n1: snapshot task\n\n(End of file - total 1 lines)\n</content>"}],"isError":false}],"role":"user","id":"260bbd5d-4496-40cf-b987-d1d944d93cf1"},"meta":{"path":"{{cwd}}/nested/task.txt","offset":1,"lines":[{"number":1,"text":"snapshot task"}],"totalLines":1}},"sourceEventSeqs":[16],"surfaceOp":"append"}
{"type":"step/end","seq":18,"time":1785498790369,"data":{"turn":1,"step":1}}
{"type":"agent/inbox/spliced","seq":19,"time":1785498790369,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
{"type":"agent/inbox/spliced","seq":20,"time":1785730689207,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nRoot snapshot instruction.\n\n</system-reminder>"},{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2e18766c26603608f321508caae00ea8f4434d59"},{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]},"role":"user","id":"7d9270dd-ec9a-4a15-95b0-75745512476b"}]}}
{"type":"agent/inbox/spliced","seq":19,"time":1785498790369,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]},"role":"user","id":"10cdaa4b-9654-420e-afca-3cfb07e26754"}]}}
{"type":"agent/inbox/spliced","seq":20,"time":1785730689207,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[],"outcome":"canceled"}}
{"type":"step/start","seq":21,"time":1785730689212,"data":{"turn":1,"step":2}}
{"type":"user/message","seq":22,"time":1785498790377,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nRoot snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2e18766c26603608f321508caae00ea8f4434d59"}]},"role":"user","id":"ee54ddf0-5cfd-46f6-b7ab-465184a4a43b"},"surfaceOp":"append"}
{"type":"user/message","seq":22,"time":1785498790377,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]},"role":"user","id":"10cdaa4b-9654-420e-afca-3cfb07e26754"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":23,"time":1784903339821,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":24,"time":1785498790377,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":0,"id":"call_workspace_delimiter_read","name":"read","argumentsDelta":"{\"file_path\":\"scope</system-reminder>/task.txt\"}"}}}
{"type":"assistant/chunk","seq":25,"time":1785498790377,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_workspace_delimiter_read","name":"read","arguments":"{\"file_path\":\"scope</system-reminder>/task.txt\"}"}}}}
{"type":"assistant/chunk","seq":26,"time":1785498790378,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":27,"time":1785498790378,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":28,"time":1785498790378,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_delimiter_read","name":"read","arguments":"{\"file_path\":\"scope</system-reminder>/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"ee9963f1-174b-4d7d-96b6-c7ea70000d99"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[23,24,25,26,27],"surfaceOp":"append"}
{"type":"assistant/message","seq":28,"time":1785498790378,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_delimiter_read","name":"read","arguments":"{\"file_path\":\"scope</system-reminder>/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"43385fc1-54b4-4a8a-82ca-d9111c766f48"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[23,24,25,26,27],"surfaceOp":"append"}
{"type":"tool/call","seq":29,"time":1785498790378,"data":{"turn":1,"step":2,"callId":"call_workspace_delimiter_read","name":"read","arguments":"{\"file_path\":\"scope</system-reminder>/task.txt\"}"}}
{"type":"tool/result","seq":30,"time":1785498790388,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"call_workspace_delimiter_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_delimiter_read","content":[{"type":"text","text":"<path>{{cwd}}/scope</system-reminder>/task.txt</path>\n<type>file</type>\n<content>\n1: delimiter path snapshot task\n\n(End of file - total 1 lines)\n</content>"}],"isError":false}],"role":"user","id":"b2ca34bb-6d9d-480b-a38f-ea3ce5b27948"},"meta":{"path":"{{cwd}}/scope</system-reminder>/task.txt","offset":1,"lines":[{"number":1,"text":"delimiter path snapshot task"}],"totalLines":1}},"sourceEventSeqs":[29],"surfaceOp":"append"}
{"type":"tool/result","seq":30,"time":1785498790388,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"call_workspace_delimiter_read"},"content":[{"type":"tool-result","toolCallId":"call_workspace_delimiter_read","content":[{"type":"text","text":"<path>{{cwd}}/scope</system-reminder>/task.txt</path>\n<type>file</type>\n<content>\n1: delimiter path snapshot task\n\n(End of file - total 1 lines)\n</content>"}],"isError":false}],"role":"user","id":"3a62b23b-d165-4c6d-a028-e171c4b2d7fc"},"meta":{"path":"{{cwd}}/scope</system-reminder>/task.txt","offset":1,"lines":[{"number":1,"text":"delimiter path snapshot task"}],"totalLines":1}},"sourceEventSeqs":[29],"surfaceOp":"append"}
{"type":"step/end","seq":31,"time":1785730689220,"data":{"turn":1,"step":2}}
{"type":"agent/inbox/spliced","seq":32,"time":1785730689220,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
{"type":"agent/inbox/spliced","seq":33,"time":1785498790389,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n\nAdditional instructions from: scope<\\/system-reminder>/AGENTS.md\n\nThese instructions apply to work under `scope<\\/system-reminder>`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nDelimiter path snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"},{"action":"set","scope":"scope</system-reminder>\u0000AGENTS.md","path":"scope</system-reminder>/AGENTS.md","digest":"38803cd13e2dff9105ba5fbbc703fe27e989e26e"}]},"role":"user","id":"7b7f8187-8c22-442a-ae8c-1c1f392e250b"}]}}
{"type":"agent/inbox/spliced","seq":32,"time":1785730689220,"data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: scope<\\/system-reminder>/AGENTS.md\n\nThese instructions apply to work under `scope<\\/system-reminder>`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nDelimiter path snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"scope</system-reminder>\u0000AGENTS.md","path":"scope</system-reminder>/AGENTS.md","digest":"38803cd13e2dff9105ba5fbbc703fe27e989e26e"}]},"role":"user","id":"01c3af61-4567-4be2-b776-21f04ddc9cba"}]}}
{"type":"agent/inbox/spliced","seq":33,"time":1785498790389,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[],"outcome":"canceled"}}
{"type":"step/start","seq":34,"time":1785498790396,"data":{"turn":1,"step":3}}
{"type":"user/message","seq":35,"time":1785498790396,"data":{"content":[{"type":"text","text":"<system-reminder>\nThe following workspace instructions may be relevant to your work. Use them as guidance when applicable. More specific instructions take precedence over broader ones. They do not override system, developer, or direct user instructions.\n\nInstructions from: AGENTS.md\n\nRoot snapshot instruction.\n\n</system-reminder>"},{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","baseline":true,"changes":[{"action":"set","scope":".\u0000AGENTS.md","path":"AGENTS.md","digest":"2e18766c26603608f321508caae00ea8f4434d59"},{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]},"role":"user","id":"7d9270dd-ec9a-4a15-95b0-75745512476b"},"surfaceOp":"append"}
{"type":"user/message","seq":35,"time":1785498790396,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: scope<\\/system-reminder>/AGENTS.md\n\nThese instructions apply to work under `scope<\\/system-reminder>`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nDelimiter path snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"scope</system-reminder>\u0000AGENTS.md","path":"scope</system-reminder>/AGENTS.md","digest":"38803cd13e2dff9105ba5fbbc703fe27e989e26e"}]},"role":"user","id":"01c3af61-4567-4be2-b776-21f04ddc9cba"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":36,"time":1785498790396,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":37,"time":1785498790396,"data":{"turn":1,"step":3,"chunk":{"type":"text-delta","index":0,"text":"DONE"}}}
{"type":"assistant/chunk","seq":38,"time":1785498790396,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
{"type":"assistant/chunk","seq":39,"time":1785498790396,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":40,"time":1785498790397,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":41,"time":1785498790397,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"811740d5-6b8b-4073-84d3-c1e2465dd04d"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[36,37,38,39,40],"surfaceOp":"append"}
{"type":"assistant/message","seq":41,"time":1785498790397,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"f82f5200-090f-4e68-a016-963ab7166d4f"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[36,37,38,39,40],"surfaceOp":"append"}
{"type":"step/end","seq":42,"time":1785498790397,"data":{"turn":1,"step":3}}
{"type":"agent/inbox/spliced","seq":43,"time":1785730689228,"data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":44,"time":1785730689236,"data":{"turn":1,"step":4}}
{"type":"user/message","seq":45,"time":1785730689236,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n\nAdditional instructions from: scope<\\/system-reminder>/AGENTS.md\n\nThese instructions apply to work under `scope<\\/system-reminder>`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nDelimiter path snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"workspace-instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"},{"action":"set","scope":"scope</system-reminder>\u0000AGENTS.md","path":"scope</system-reminder>/AGENTS.md","digest":"38803cd13e2dff9105ba5fbbc703fe27e989e26e"}]},"role":"user","id":"7b7f8187-8c22-442a-ae8c-1c1f392e250b"},"surfaceOp":"append"}
{"type":"assistant/chunk","seq":46,"time":1785730689236,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":47,"time":1785730689236,"data":{"turn":1,"step":4,"chunk":{"type":"text-delta","index":0,"text":"DONE"}}}
{"type":"assistant/chunk","seq":48,"time":1785730689236,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
{"type":"assistant/chunk","seq":49,"time":1785730689236,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}}}
{"type":"assistant/chunk","seq":50,"time":1785730689236,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":51,"time":1785730689236,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"4082d4e8-9244-45e6-b71d-cd6728f372a3"},"usage":{"inputTokens":10,"outputTokens":2}},"sourceEventSeqs":[46,47,48,49,50],"surfaceOp":"append"}
{"type":"step/end","seq":52,"time":1785730689237,"data":{"turn":1,"step":4}}
{"type":"turn/end","seq":53,"time":1785730689237,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/end","seq":43,"time":1785901434023,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,5 +1,4 @@
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"DONE"}}}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"DONE"}}}}
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/context/workspace-context/README.md
README.md: 954a032e600579dda7f11ce3d11224aff7f7666e
README.zh.md: ba945cfbf83398466c7888c5e95300dd8570870d
README.md: 7add269c7a1b38e9624cf6d0368151661bc72a6b
README.zh.md: 3456c5c36275e8e521b312b1b0b7e1dd102ca23c

View File

@@ -6,7 +6,7 @@ Per-session workspace instruction loading for `AGENTS.md`-compatible files. The
## Lifecycle
The first eligible `agent/pre-step` of each live session composes the baseline and prepends it to the agent's `next-step` inbox without changing the current `PreStepDecision`. It reads `$DSH_HOME/AGENTS.md` followed by, in each directory from the project root to `agent.session.header.cwd`, every existing base candidate and then every existing local-overlay candidate. Within one directory, candidates whose content is byte-identical after trimming leading and trailing whitespace collapse to the earliest candidate in configured order, so a `CLAUDE.md` that merely duplicates its sibling `AGENTS.md` is rendered once. Because the loop claims before running pre-step, the new context may enter a later request. If a previously queued workspace context is still pending, the plugin removes and replaces that exact inbox item instead of accumulating duplicates.
The first eligible `agent/pre-step` of each live session composes the baseline. When the downstream decision enters a nonempty first-step batch, the plugin folds the baseline into that final batch right after the claimed prompt, so the direct prompt and the durable baseline enter step 1 and reach the first request together. A rejected or empty first-step decision leaves the baseline in the agent's `next-step` inbox for a later wakeup. The loader reads `$DSH_HOME/AGENTS.md` followed by, in each directory from the project root to `agent.session.header.cwd`, every existing base candidate and then every existing local-overlay candidate. Within one directory, candidates whose content is byte-identical after trimming leading and trailing whitespace collapse to the earliest candidate in configured order, so a `CLAUDE.md` that merely duplicates its sibling `AGENTS.md` is rendered once. If a previously queued workspace context is still pending, the plugin removes and replaces that exact inbox item instead of accumulating duplicates.
The plugin also listens on `tools/post-execute` for successful first-party `read`, `write`, and `edit` calls. Each touch checks newly reached descendant scopes and every previously loaded scope. Each configured candidate name is an independent scope in its directory: a newly present file is attached through the result's `additionalContexts`; a changed file appends a replacement; a file that disappears or becomes a per-directory duplicate of an earlier candidate appends a removal notice. Native calls and Code Mode sub-dispatches share this path: `run_code` defers each nested context until its outer result, so the loop still appends updates after tool-call/result adjacency is complete. This follows structured filesystem activity rather than shell `cd`, because each local bash call starts a fresh shell and parsing arbitrary shell syntax would be unreliable.
@@ -48,11 +48,11 @@ The plugin owns the complete `<system-reminder>` framing, and every injected `us
## State And Refresh
Model-visible text contains no hidden state markers. Each baseline or dynamic context event instead carries a typed `workspace-instructions` source with a list of `{ action, scope, path, digest? }` changes; the complete startup or resume baseline also carries `baseline: true`. A matching durable `user/message` confirms a queued baseline and its candidate versions. If pre-step rejects or rewrites away a claimed workspace message, a later boundary recomposes and queues the current context again. On every relevant tool touch, the plugin reconstructs loaded state from its visible session events and overlays a short in-memory pending window for context present on the immutable top-level `tools/result` but not yet appended by the loop. If the owning `step/end` arrives before a matching dynamic context reaches the log, the plugin clears that pending transition and its version fast path so the next successful touch can load it again. Nested Code Mode results stage pending changes under the outer execution token for same-run duplicate suppression; the outer result rolls that state back and recommits only contexts that survived outer policy.
Model-visible text contains no hidden state markers. Each baseline or dynamic context event instead carries a typed `workspace-instructions` source with a list of `{ action, scope, path, digest? }` changes; the complete startup or resume baseline also carries `baseline: true`. A matching durable `user/message` confirms a queued baseline and its candidate versions. An entering pre-step folds newly composed context into its final batch immediately after the claimed messages and removes the pending inbox copy; rejection keeps the current context queued. If a listener rewrites away a claimed workspace message without entering its replacement, a later boundary recomposes the current context. On every relevant tool touch, the plugin reconstructs loaded state from its visible session events and overlays a short in-memory pending window for context present on the immutable top-level `tools/result` but not yet appended by the loop. If the owning `step/end` arrives before a matching dynamic context reaches the log, the plugin clears that pending transition and its version fast path so the next successful touch can load it again. Nested Code Mode results stage pending changes under the outer execution token for same-run duplicate suppression; the outer result rolls that state back and recommits only contexts that survived outer policy.
An unchanged path and SHA-1 content digest is not injected again. A per-session, per-scope provider cache stores only `{ path, version, digest, trimmedDigest }`: when the provider's opaque `FsVersion` and the effective visible state both match, reconciliation skips the content read; a changed version triggers a bounded read and SHA-1 confirmation before any model-visible update. The `trimmedDigest` — SHA-1 over the whitespace-trimmed content — is the per-directory duplicate key, so an unchanged file can still be removed when an earlier candidate converges on its content. Resume works because SHA-1 state is persisted in the typed source, while an empty in-memory version cache merely causes one confirming read. Compaction re-arms a scope after its context event leaves the visible surface even when the cached version is unchanged. A removal is a tombstone, so a later candidate reappearance is loaded again. Only model-visible changes actually rendered within the byte budget enter the source, pending state, and version cache; an omitted change remains eligible for a later touch, while a same-digest version refresh updates only the provider cache.
The initial baseline event itself is not rewritten. Its typed changes remain authoritative only while that event is in the visible session surface; the next successful filesystem touch re-adds an unchanged baseline scope after compaction, or appends its replacement or removal. The in-memory scope marker and provider-version cache only select and accelerate probes. A hot plugin remount retains a baseline only when its typed event remains visible, while rebuilding current scope and version tracking; otherwise it queues a current baseline. A resumed loop always recomposes the current baseline and also reconciles still-visible dynamic scopes at its first pre-step, but the resulting inbox context need not enter that same request. There is no file watcher, so an on-disk change becomes visible at the next successful `read`, `write`, or `edit` touch, or when a resumed loop prepares its baseline.
The initial baseline event itself is not rewritten. Its typed changes remain authoritative only while that event is in the visible session surface; the next successful filesystem touch re-adds an unchanged baseline scope after compaction, or appends its replacement or removal. The in-memory scope marker and provider-version cache only select and accelerate probes. A hot plugin remount retains a baseline only when its typed event remains visible, while rebuilding current scope and version tracking; otherwise it queues a current baseline. A resumed loop always recomposes the current baseline and also reconciles still-visible dynamic scopes at its first pre-step; an entering first request records that context in the same step. There is no file watcher, so an on-disk change becomes visible at the next successful `read`, `write`, or `edit` touch, or when a resumed loop prepares its baseline.
## Configuration

View File

@@ -6,7 +6,7 @@
## 生命周期
每个实时会话第一次符合条件的 `agent/pre-step` 会组合基线,将它 prepend 到 agent 的 `next-step` inbox但不改变当前 `PreStepDecision`。它先读取 `$DSH_HOME/AGENTS.md`,随后针对项目根目录到 `agent.session.header.cwd` 的每个目录,先读取每个现有基础候选文件,再读取每个现有本地 overlay 候选文件。同一目录中,如果候选文件在去除首尾空白后字节完全一致,就会按已配置顺序折叠到最早候选文件,因此 `CLAUDE.md` 若只是复制同级 `AGENTS.md`,只会渲染一次。因为循环在运行 pre-step 前已经领取消息,新上下文可能要到后续请求才会进入。若之前排队的 workspace 上下文仍在等待,插件会删除并替换该确切 inbox 条目,而不会不断累积副本。
每个实时会话第一次符合条件的 `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`。匹配的持久 `user/message` 会确认已排队基线及其候选版本。 pre-step reject 或改写掉已领取的 workspace 消息,后续边界会重新组合并排队当前上下文。每次相关工具 touch 时,插件会从可见会话事件重建已加载状态,并叠加一个短暂内存 pending 窗口,用于不可变顶层 `tools/result` 上存在但 loop 尚未追加的上下文。如果所属 `step/end` 在匹配的动态上下文进入日志之前到达,插件会清除该 pending 转换及其版本快速路径,使下一次成功 touch 可以重新加载。嵌套 Code Mode 结果会在外层执行 token 下暂存 pending 变更,用于抑制同次运行中的重复项;外层结果会回滚该状态,再只重新提交经过外层策略的上下文。
模型可见文本不含隐藏状态标记。每个基线或动态上下文事件改为携带带类型的 `workspace-instructions` 来源,其中包含 `{ action, scope, path, digest? }` 变更列表;完整的启动或恢复基线还会携带 `baseline: true`。匹配的持久 `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 只会导致一次确认读取。压缩compaction会在 scope 的上下文事件离开可见表层后重新启用它,即使缓存版本未变。移除是 tombstone因此候选文件之后重新出现时会重新加载。只有在字节预算内实际渲染的模型可见变更才会进入来源、pending 状态和版本 cache已省略变更仍可在后续 touch 处理,而相同 digest 的版本刷新只更新提供方 cache。
初始基线事件自身不会被改写。其带类型的变更仅在该事件仍位于可见会话表层时才是权威状态;下一次成功的文件系统 touch 会在压缩后重新添加未变的基线 scope或追加其替换或移除。内存中的 scope 标记和提供方版本 cache 只负责选择探测对象并加速探测。插件热重挂只有在其带类型的事件仍然可见时才保留基线,同时会重建当前 scope 与版本跟踪状态;否则会排队当前基线。恢复的 loop 始终在第一次 pre-step 重新组合当前基线,并对账仍可见的动态 scope,但产生的 inbox 上下文不保证进入同一次请求。没有文件 watcher因此磁盘变更会在下一次成功 `read``write``edit` touch 时可见,也会在恢复 loop 准备基线时可见。
初始基线事件自身不会被改写。其带类型的变更仅在该事件仍位于可见会话表层时才是权威状态;下一次成功的文件系统 touch 会在压缩后重新添加未变的基线 scope或追加其替换或移除。内存中的 scope 标记和提供方版本 cache 只负责选择探测对象并加速探测。插件热重挂只有在其带类型的事件仍然可见时才保留基线,同时会重建当前 scope 与版本跟踪状态;否则会排队当前基线。恢复的 loop 始终在第一次 pre-step 重新组合当前基线,并对账仍可见的动态 scope;首次请求若进入步骤,就会在同一步骤记录该上下文。没有文件 watcher因此磁盘变更会在下一次成功 `read``write``edit` touch 时可见,也会在恢复 loop 准备基线时可见。
## 配置

View File

@@ -214,13 +214,31 @@ export function apply(ctx: Context, config: Config): void {
ctx.on('agent/pre-step', async (
agent: Agent,
messages,
{ signal },
{ step, signal },
next,
): Promise<PreStepDecision> => {
const decision = await next()
await waitForProjections(agent)
await composeAndSync(agent, signal, messages)
return decision
const pending = agent.inbox.nextStep.filter(isWorkspaceContext)
const desired = await compose(agent, signal, messages, pending)
signal.throwIfAborted()
// An empty first entry owns a no-step turn; keep context pending instead
// of turning it into a standalone request. Later entries may be tool continuations.
if (decision.kind === 'reject' || (step === 1 && decision.messages.length === 0)) {
syncInbox(agent, messages, desired)
return decision
}
// A proceeding step settles the pending context: it either enters below as
// `desired`, or its payload is already covered by the batch, so nothing stays pending.
for (const message of pending) agent.inbox.remove(message.id)
if (desired === undefined || decision.messages.some(message => sameContextPayload(message, desired))) {
return decision
}
// Fold the context right after the claimed batch, so the direct prompt
// precedes it and the driver-appended runtime context follows it.
const lastClaimedIndex = decision.messages.findLastIndex(message => messages.includes(message))
const entered = decision.messages.toSpliced(lastClaimedIndex + 1, 0, desired)
return { kind: 'enter', messages: entered }
})
ctx.on('tools/result', (exec: ToolExecution, result: ToolExecutionResult) => {

View File

@@ -40,7 +40,7 @@ import {
type InstructionVersionCache,
} from '../src/state.ts'
import { resolveConfig } from '../src/config.ts'
import { candidateScopeKey, renderInstructionChanges } from '../src/render.ts'
import { candidateScopeKey, renderInstructionChanges, USER_GLOBAL_DIRECTORY, USER_GLOBAL_FILE } from '../src/render.ts'
import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
/** Per-candidate reconciliation scope key: directory paired with the file name. */
@@ -1035,26 +1035,20 @@ describe('workspace context request injection', () => {
() => Promise.resolve({ kind: 'enter' as const, messages: staleClaim }),
)
expect(staleDecision).toEqual({ kind: 'enter', messages: staleClaim })
expect(resumed.inbox.nextStep).toHaveLength(1)
const replacement = resumed.inbox.nextStep[0]
if (staleDecision.kind !== 'enter') throw new Error('recovered baseline was rejected')
expect(staleDecision.messages).toHaveLength(2)
expect(staleDecision.messages[0]).toBe(staleClaim[0])
const replacement = staleDecision.messages[1]
expect(replacement?.id).not.toBe(stale?.id)
expect(blocksText(replacement?.content)).toContain('new repo rule')
expect(blocksText(replacement?.content)).not.toContain('old repo rule')
expect(resumed.inbox.nextStep).toHaveLength(0)
const replacementClaim = resumed.inbox.claim('next-step')
const replacementDecision = await agentEvents(ctx, resumed).waterfall(
'agent/pre-step',
replacementClaim,
{ turn: 1, step: 2, signal: AbortSignal.timeout(1000) },
() => Promise.resolve({ kind: 'enter' as const, messages: replacementClaim }),
)
if (replacementDecision.kind !== 'enter') throw new Error('replacement baseline was rejected')
for (const message of replacementDecision.messages) {
for (const message of staleDecision.messages) {
const event = resumed.session.append('user/message', message, { surfaceOp: 'append' })
ctx.emit('session/event', resumed.session, event)
}
expect(baselineEvents(resumed)).toHaveLength(0)
expect(baselineEvents(resumed)).toHaveLength(1)
} finally {
await rm(root, { recursive: true, force: true })
await rm(home, { recursive: true, force: true })
@@ -1177,7 +1171,7 @@ describe('workspace context request injection', () => {
}
})
it('leaves the current pre-step batch unchanged while queuing the baseline', async () => {
it('enters the baseline right after the claimed prompt in the first pre-step without queuing another step', async () => {
const root = await tempRepo()
const home = await tempRepo()
try {
@@ -1199,9 +1193,40 @@ describe('workspace context request injection', () => {
() => Promise.resolve(downstream),
)
expect(decision).toBe(downstream)
expect(agent.inbox.nextStep).toHaveLength(1)
expect(blocksText(agent.inbox.nextStep[0]?.content)).toContain('Instructions from: AGENTS.md')
expect(decision).toMatchObject({ kind: 'enter' })
if (decision.kind !== 'enter') throw new Error('workspace baseline was rejected')
expect(decision.messages).toHaveLength(2)
expect(decision.messages[0]).toBe(prompt)
expect(decision.messages[1]?.source).toMatchObject({ kind: 'workspace-instructions', baseline: true })
expect(blocksText(decision.messages[1]?.content)).toContain('Instructions from: AGENTS.md')
expect(agent.inbox.nextStep).toHaveLength(0)
} finally {
await rm(root, { recursive: true, force: true })
await rm(home, { recursive: true, force: true })
}
})
it('reports a changed user-global instruction through the visible-scope reconcile path', async () => {
const root = await tempRepo()
const home = await tempRepo()
try {
await mkdir(join(root, '.git'), { recursive: true })
await write(join(root, 'AGENTS.md'), 'repo rule')
await write(join(home, 'AGENTS.md'), 'global rule')
const ctx = new Context()
await mountFileToolsAndWorkspaceContext(ctx, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
await composeBaselinePrefix(ctx, agent)
await write(join(home, 'AGENTS.md'), 'updated global rule')
await syncWorkspaceContext(ctx, agent)
const pending = await workspaceContextOf(agent)
expect(pending?.source).toMatchObject({
kind: 'workspace-instructions',
changes: [{ action: 'replace', scope: sk(USER_GLOBAL_DIRECTORY, USER_GLOBAL_FILE) }],
})
expect(blocksText(pending?.content)).toContain('updated global rule')
} finally {
await rm(root, { recursive: true, force: true })
await rm(home, { recursive: true, force: true })
@@ -2693,6 +2718,51 @@ describe('dynamic nested workspace context injection', () => {
},
)
it('skips visible baseline scopes when baseline scopes are excluded from reconciliation', async () => {
const root = join(await tempRepo(), 'virtual-repo')
const home = join(await tempRepo(), 'virtual-home')
const ctx = new Context()
try {
await ctx.plugin(RecordingFileSystem)
const fs = ctx.fs as RecordingFileSystem
fs.entries.set(join(root, '.git'), { type: 'directory' })
fs.entries.set(join(root, 'AGENTS.md'), { type: 'file', content: 'repo rule' })
const agent = stubAgent(root)
const rootScope = sk('.', 'AGENTS.md')
const loaded = baselineInstructionState([{
absolutePath: join(root, 'AGENTS.md'),
displayPath: 'AGENTS.md',
content: 'repo rule',
version: FsVersion('loaded-agents'),
}])
const previous = loaded.changes.get(rootScope)
if (previous === undefined) throw new Error('missing AGENTS.md baseline state')
const authoritative = createUserMessage({
content: [{ type: 'text', text: 'repo rule' }],
source: { kind: 'workspace-instructions', changes: [previous] },
})
agent.session.append('user/message', authoritative, { surfaceOp: 'append' })
const resolved = resolveConfig({ dshHome: home, maxBytes: 65536, localInstructionFileCandidates: [] })
const cache: InstructionVersionCache = new WeakMap()
cache.set(agent.session, new Map(loaded.versions))
const options = {
authorityMessages: [],
scopeMessages: [],
touchedPaths: [],
includeBaselineScopes: false,
signal: testToolSignal,
}
const result = await reconcileInstructionContext(agent, resolved, cache, fs, options)
expect(result).toBeUndefined()
} finally {
await ctx.fiber.dispose()
await rm(dirname(root), { recursive: true, force: true })
await rm(dirname(home), { recursive: true, force: true })
}
})
it('removes a previously rendered sibling once its content becomes a duplicate of an earlier candidate', async () => {
const root = await tempRepo()
const home = await tempRepo()
@@ -3885,7 +3955,7 @@ describe('workspace context inbox synchronization', () => {
}
})
it('keeps a claimed batch unchanged and queues an offline correction', async () => {
it('enters an offline correction immediately after its claimed stale context', async () => {
const root = await tempRepo()
const home = await tempRepo()
try {
@@ -3906,10 +3976,12 @@ describe('workspace context inbox synchronization', () => {
async () => downstream,
)
expect(decision).toBe(downstream)
expect(blocksText(decision.kind === 'enter' ? decision.messages[0]?.content : [])).toContain('old claimed rule')
expect(agent.inbox.nextStep).toHaveLength(1)
expect(blocksText(agent.inbox.nextStep[0]?.content)).toContain('new claimed rule with more detail')
if (decision.kind !== 'enter') throw new Error('offline correction was rejected')
expect(decision.messages).toHaveLength(2)
expect(decision.messages[0]).toBe(claimed[0])
expect(blocksText(decision.messages[0]?.content)).toContain('old claimed rule')
expect(blocksText(decision.messages[1]?.content)).toContain('new claimed rule with more detail')
expect(agent.inbox.nextStep).toHaveLength(0)
} finally {
await rm(root, { recursive: true, force: true })
await rm(home, { recursive: true, force: true })