Sfoglia il codice sorgente

fix(bundle): enable projection cache in base-backed profiles

_Kerman 1 mese fa
parent
commit
2c17b3048e

+ 2 - 2
.agents/notes/implemented/architecture/2026-08-19-projection-cache-per-session-files.i18n.yaml

@@ -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-08-19-projection-cache-per-session-files.md
-2026-08-19-projection-cache-per-session-files.md: 0a44ca96384bc4935a95a868da941f43fee70c46
-2026-08-19-projection-cache-per-session-files.zh.md: fa30c03e88015b78996e7cae5224d2260670542b
+2026-08-19-projection-cache-per-session-files.md: e62107cd0cf8f27f68aab8f15d5a66941434a9ed
+2026-08-19-projection-cache-per-session-files.zh.md: 33198d02464c7b30d672f617fff9e9f4bc91a483

+ 2 - 1
.agents/notes/implemented/architecture/2026-08-19-projection-cache-per-session-files.md

@@ -10,7 +10,7 @@ The persisted projection cache was one global `session_projcache.json` — a `se
 
 ## Decision
 
-The cache opens the `session_projcache` storage domain in the new `per-record` layout, added to the json backend: one version-stamped document per session at `<root>/session_projcache/sessions/<id>.json`, owned by the storage stack — `storage` / `storage-json` / `storage-domain` live in the shared base bundle alongside the cache, and the cache itself is a plain domain consumer again. The cache never consults the persistence layer: no `locate`, no dependency on which backend is mounted.
+The cache opens the `session_projcache` storage domain in the new `per-record` layout, added to the json backend: one version-stamped document per session at `<root>/session_projcache/sessions/<id>.json`, owned by the storage stack — `storage` / `storage-json` / `storage-domain` live in the shared base bundle alongside the cache, and the cache itself is a plain domain consumer again. Every shipped base-backed profile keeps the cache enabled, so the session producer records checkpoints independently of whether its current application exposes a listing interface; `sdk-minimal`, which does not use the base bundle, remains outside this composition. The cache never consults the persistence layer: no `locate`, no dependency on which backend is mounted.
 
 Reads and writes share ONE coherent state: every read (`cachedSnapshot`) is a synchronous lookup in the domain's in-memory tables (zero I/O), and every write queues on the domain's per-unit write chain, mutating memory only after durability — no direct disk reads that could lag the throttled writes. The cache keeps every other responsibility: checkpoint fold, write policy (turn/end + disposal mandatory, count/interval throttle), fail-soft durability, and the listing read. `cachedSnapshot(meta)` is synchronous. The cache runs no cold-refold ladder (that would require reading the session log, which belongs to the persistence layer); a consumer that needs a guaranteed cold snapshot refolds from the log itself. The json backend creates its tree owner-only (`0o700`).
 
@@ -18,6 +18,7 @@ Reads and writes share ONE coherent state: every read (`cachedSnapshot`) is a sy
 
 - Per-session write isolation: each throttled write replaces only that session's small document, removing the global write amplification. The domain write chain serializes writes, so a newer cut never lands before an older one; domain close drains in-flight writes.
 - Listing is a synchronous in-memory read; a session without a record document simply lacks the projection column.
+- ACP, headless, SDK, and Web sessions publish cache rows for later consumers. The log-leading durability barrier may flush a covered prefix at the cache cadence and split otherwise coalesced JSONL runs; recorded profile snapshots preserve that assembled persistence behavior.
 - The per-record contract scopes failure: a malformed or stale-version document reads as an absent record at open, so one bad file never bricks the cache, and a checkpoint schema bump discards stale sessions per record instead of rejecting the whole domain.
 - No migration: the cache is derived data, never an authority. An obsolete cache (any earlier format) is never read — the first cold read refolds from the log and writes the current format.
 - The cache record is bound to the same log lifecycle as before: the stored `{createdAt, cwd}` identity guards against a recreated id.

+ 2 - 1
.agents/notes/implemented/architecture/2026-08-19-projection-cache-per-session-files.zh.md

@@ -10,7 +10,7 @@ Status: implemented
 
 ## Decision
 
-缓存打开采用新增 `per-record` 布局的 `session_projcache` 存储域:每个会话一个带版本戳的文档,位于 `<root>/session_projcache/sessions/<id>.json`,介质归存储栈所有——`storage` / `storage-json` / `storage-domain` 与缓存一起落在共享 base 装配里,缓存重新变回纯粹的域消费方。缓存绝不咨询持久化层:没有 `locate`、不依赖挂载的是哪个后端。
+缓存打开采用新增 `per-record` 布局的 `session_projcache` 存储域:每个会话一个带版本戳的文档,位于 `<root>/session_projcache/sessions/<id>.json`,介质归存储栈所有——`storage` / `storage-json` / `storage-domain` 与缓存一起落在共享 base 装配里,缓存重新变回纯粹的域消费方。所有随附且基于 base 的 profile 都保持启用缓存,因此会话生产方会记录检查点,不取决于当前应用是否提供列表接口;不使用 base 组合包的 `sdk-minimal` 不在此装配范围内。缓存绝不咨询持久化层:没有 `locate`、不依赖挂载的是哪个后端。
 
 读写共享同一份一致状态:每次读取(`cachedSnapshot`)都是对域内存表的同步查找(零 I/O);每次写入排进该域的单条写链,先落盘成功才改内存——不再有落后于节流写入的直读磁盘。缓存保留其余全部职责:检查点折叠、写策略(turn/end + dispose 强制点、count/interval 节流)、fail-soft 持久化与列表读。`cachedSnapshot(meta)` 是同步的。缓存不运行冷重折叠阶梯(那需要读取会话日志,属于持久化层的职责);需要保证冷快照的消费方自行从日志重折叠。json 后端以仅属主权限(`0o700`)创建自己的目录树。
 
@@ -18,6 +18,7 @@ Status: implemented
 
 - 每会话写入隔离:每次节流写入只替换该会话的小文档,消除全局写放大。域写链将写入串行化,新切面绝不会先于旧切面落盘;域关闭时会排空在途写入。
 - 列表读取是同步内存读;没有记录文档的会话只是缺少投影列。
+- ACP、headless、SDK 与 Web 会话都会发布缓存行,供后续消费方使用。确保日志领先的持久性屏障可能按缓存节奏 flush 已覆盖的前缀,并拆分原本会合并的 JSONL 行;各 profile 的录制快照保留这一完整装配下的持久化行为。
 - per-record 契约把故障范围缩小到单记录:畸形或过期版本的文档在打开时读作"无此记录",单个坏文件不会拖垮整个缓存;检查点 schema 升级按会话丢弃过期行,而不是拒绝整个域。
 - 无需迁移:缓存是派生数据,绝非权威。过时的缓存(任何更早格式)从不被读取——首次冷读从日志重折叠并写出当前格式。
 - 缓存记录仍绑定同一日志生命周期:存储的 `{createdAt, cwd}` 身份防止被重建的 id 误导。

+ 2 - 2
packages/bundle/acp-app/README.i18n.yaml

@@ -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/bundle/acp-app/README.md
-README.md: 491d5b211fb8d010ba41485465093360eae7b598
-README.zh.md: ee26b13c0e66dbb8ed14f5c620ee1089f7120570
+README.md: 5cb1e33319c5da04de19d3c8eb1c24cb72ec2312
+README.zh.md: 13debf94c4fe209a74f0174fda7195c383bf590a

+ 1 - 1
packages/bundle/acp-app/README.md

@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
 
 The automation-only ACP stdio application as a `dsh` profile bundle over [`dsh-base`](../base/README.md). It inherits the base's disabled module-HMR policy; its patch sets the coding-agent persona and default model route, mounts an app-owned zero-option command provider, and starts [`dsh-acp`](../../acp/acp/README.md) only after that provider accepts the invocation. `dsh --profile acp --help` therefore writes help and exits without claiming stdin or stdout.
 
-The startup provider binds stdin EOF to the launcher's bounded successful shutdown. ACP connection close, SIGINT, and SIGTERM drain the bridge-owned agents and the root profile tree before exit. Stdout is reserved for newline-delimited ACP JSON-RPC frames. The bundle disables model-generated session titles because ACP exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. It also keeps the base projection cache disabled: ACP exposes no session-listing surface, and the cache's write-behind would force session-log flushes at cache-chosen times, changing the durable JSONL batching this profile's keyless transcripts pin. A deployment selects a different complete composition through profile bundles and patch files, not another app bin.
+The startup provider binds stdin EOF to the launcher's bounded successful shutdown. ACP connection close, SIGINT, and SIGTERM drain the bridge-owned agents and the root profile tree before exit. Stdout is reserved for newline-delimited ACP JSON-RPC frames. The bundle disables model-generated session titles because ACP exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. The inherited projection cache checkpoints ACP-created sessions for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. A deployment selects a different complete composition through profile bundles and patch files, not another app bin.
 
 The shipped row creates sessions with `deepseek-official` and `deepseek-v4-flash`; a later patch can replace that row's complete config. The base profile owns adapters, tools, persistence, policy, settings, credentials, and the per-session workspace supplied by the ACP client.
 

+ 1 - 1
packages/bundle/acp-app/README.zh.md

@@ -4,7 +4,7 @@
 
 以 [`dsh-base`](../base/README.zh.md) 为基础的 automation-only ACP stdio 应用 `dsh` profile 组合包。它继承 base 默认禁用模块 HMR(热模块替换)的策略;其 patch 设置 coding agent(编程智能体)persona 与默认模型路由、挂载应用自有的零选项命令提供方,并且只在该提供方接受调用后启动 [`dsh-acp`](../../acp/acp/README.zh.md)。因此,`dsh --profile acp --help` 会写出 help 并退出,不会占用 stdin 或 stdout。
 
-启动提供方把 stdin EOF 绑定到启动器的有界成功关闭。ACP 连接关闭、SIGINT 与 SIGTERM 会在退出前排空 bridge 自有 agent 以及根 profile 树。Stdout 仅保留给换行分隔的 ACP JSON-RPC frame。ACP 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。本组合包同样禁用 base 的投影缓存:ACP 不提供 session 列表表层,而缓存的写回会在缓存选定的时机强制 session 日志 flush,从而改变本 profile 免密钥转录所固定的持久 JSONL 批处理格式。部署方通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个 app bin。
+启动提供方把 stdin EOF 绑定到启动器的有界成功关闭。ACP 连接关闭、SIGINT 与 SIGTERM 会在退出前排空 bridge 自有 agent 以及根 profile 树。Stdout 仅保留给换行分隔的 ACP JSON-RPC frame。ACP 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。继承的投影缓存会为 ACP 创建的会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。部署方通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个 app bin。
 
 随附配置项使用 `deepseek-official` 与 `deepseek-v4-flash` 创建 session;后续 patch 可以替换该配置项的完整 config。base profile 负责适配器、工具、持久化、策略、settings 与 credentials;ACP client 为每个 session 提供工作区。
 

+ 0 - 8
packages/bundle/acp-app/cordis.patch.yml

@@ -8,14 +8,6 @@
 - id: session-title-llm
   disabled: true
 
-# The base projection cache serves session-listing columns for web surfaces.
-# ACP exposes no listing surface, and the cache's write-behind would force
-# session-log flushes at cache-chosen times — changing the durable JSONL
-# batching this profile's keyless transcripts pin — so the automation profile
-# disables the base row.
-- id: session-projection-cache
-  disabled: true
-
 - insert:
     - id: acp-app-startup
       name: '@deepseek-ai/dsh-acp-app'

+ 2 - 2
packages/bundle/headless/README.i18n.yaml

@@ -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/bundle/headless/README.md
-README.md: eff2569a32f9ff21b8b3af40cc699ce49915a88c
-README.zh.md: 4e614587b8c0cd468eaa3a6192bb7fb8594df5b4
+README.md: cafc27f9e8267593cf58ee063d47d23aaccb3017
+README.zh.md: ab288e0cd0bd8f07c3e7ae760acbea4b9056807b

+ 1 - 1
packages/bundle/headless/README.md

@@ -2,7 +2,7 @@
 
 English | [中文](README.zh.md)
 
-The dsh one-shot bundle. [`cordis.patch.yml`](cordis.patch.yml) rides directly over [`dsh-base`](../base/README.md): it inherits the base's disabled module-HMR policy, supplies the coding persona and tool mode, mounts Code Mode's worker as a core execution capability, and inserts this package's `headless-runner` plugin (config `{task}`, resolved from the injected `headlessStartup` provider). It mounts no Host, HTTP server, Web runtime, or browser plugin. It also keeps the base projection cache disabled: the one-shot runner exposes no session-listing surface, and the cache's write-behind would force session-log flushes at cache-chosen times, changing the durable JSONL batching this profile's keyless replay fixtures pin.
+The dsh one-shot bundle. [`cordis.patch.yml`](cordis.patch.yml) rides directly over [`dsh-base`](../base/README.md): it inherits the base's disabled module-HMR policy and projection cache, supplies the coding persona and tool mode, mounts Code Mode's worker as a core execution capability, and inserts this package's `headless-runner` plugin (config `{task}`, resolved from the injected `headlessStartup` provider). It mounts no Host, HTTP server, Web runtime, or browser plugin. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs.
 
 After the Loader settles, the runner reads the shared [`ctx.agentDefaultModel`](../../core/agent-default-model/README.md), creates one fresh persisted Agent through `ctx.agents`, submits the task as an ordinary user message, and waits for quiescence. It flushes the Session before folding the owned durable event interval, writes the last non-empty assistant text to stdout, and requests exit through the launcher-provided `ctx.appExit` host hook ([`dsh-cmdline`](../../boot/cmdline/README.md)) (final `turn/end` completed → 0, otherwise 1). A terminal `error` reason also writes its code and message to stderr; successful runs keep stderr empty. The process opens no listening port. The task text is this app's command line: the ordinary `headless-startup` provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the positional argument of `dsh --profile headless "task"`, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config. A missing or whitespace-only task is rejected before the runner activates.
 

+ 1 - 1
packages/bundle/headless/README.zh.md

@@ -2,7 +2,7 @@
 
 [English](README.md) | 中文
 
-dsh 一次性任务组合包。[`cordis.patch.yml`](cordis.patch.yml) 直接叠加在 [`dsh-base`](../base/README.zh.md) 之上:继承 base 默认禁用模块 HMR(热模块替换)的策略,提供编码 persona 和工具模式,将 Code Mode 的 worker 作为核心执行能力挂载,并插入本包的 `headless-runner` 插件(配置为 `{task}`,从注入的 `headlessStartup` 提供方解析)。它不挂载任何 Host、HTTP server、Web runtime 或浏览器插件。本组合包同样禁用 base 的投影缓存:一次性 runner 不提供 session 列表表层,而缓存的写回会在缓存选定的时机强制 session 日志 flush,从而改变本 profile 免密钥回放 fixtures 所固定的持久 JSONL 批处理格式。
+dsh 一次性任务组合包。[`cordis.patch.yml`](cordis.patch.yml) 直接叠加在 [`dsh-base`](../base/README.zh.md) 之上:继承 base 默认禁用模块 HMR(热模块替换)的策略与投影缓存,提供编码 persona 和工具模式,将 Code Mode 的 worker 作为核心执行能力挂载,并插入本包的 `headless-runner` 插件(配置为 `{task}`,从注入的 `headlessStartup` 提供方解析)。它不挂载任何 Host、HTTP server、Web runtime 或浏览器插件。缓存会为每个持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。
 
 Loader 结算后,runner 读取共享的 [`ctx.agentDefaultModel`](../../core/agent-default-model/README.zh.md),通过 `ctx.agents` 创建一个全新的持久化 Agent(智能体),将任务作为普通用户消息提交,并等待完全停稳。它对 Session 执行 flush 后再汇总自身持有的持久化事件区间,将最后一条非空 assistant 文本写入 stdout,再经启动器提供的 `ctx.appExit` 宿主钩子([`dsh-cmdline`](../../boot/cmdline/README.zh.md))请求退出(最终 `turn/end` 完成 → 0,否则为 1)。最终结束原因为 `error` 时,还会将 code 与 message 写入 stderr;成功运行时 stderr 保持为空。进程不会打开监听端口。任务文本就是这个应用的命令行:普通 `headless-startup` 提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取 `dsh --profile headless "task"` 的位置参数、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 注入该服务,再从惰性配置中读取任务。缺失或只有空白的任务会在 runner 激活前被拒绝。
 

+ 0 - 8
packages/bundle/headless/cordis.patch.yml

@@ -14,14 +14,6 @@
     # Keep the same temporary process-wide Code Mode opt-in as the Web surface.
     mode: !!js process.env.DSH_TOOLS_MODE
 
-# The base projection cache serves session-listing columns for web surfaces.
-# The one-shot runner exposes no listing surface, and the cache's write-behind
-# would force session-log flushes at cache-chosen times — changing the durable
-# JSONL batching this profile's keyless replay fixtures pin — so this bundle
-# disables the base row.
-- id: session-projection-cache
-  disabled: true
-
 - insert:
     # Code Mode is a core execution capability, not a Web component.
     - id: code-runtime

+ 2 - 2
packages/bundle/sdk-app/README.i18n.yaml

@@ -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/bundle/sdk-app/README.md
-README.md: 05abcd99c74bbaec9c27b554738e94fd4251a619
-README.zh.md: 4ca614bb0e8a5318950d59882d86f032118e1254
+README.md: 5c65c4a318683d51e930473a9715d290e331e60a
+README.zh.md: 935e2d88ce7730c6f6cef39dc9ff3d1ddb644bb4

+ 1 - 1
packages/bundle/sdk-app/README.md

@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
 
 The SDK stdio application as a `dsh` profile bundle over [`dsh-base`](../base/README.md). It inherits the base's disabled module-HMR policy; its patch sets the coding-agent persona, mounts an app-owned zero-option command provider, and starts [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.md) only after that provider accepts the invocation. `dsh --profile sdk --help` therefore writes help and exits without claiming stdin or stdout. The standalone [`sdk-minimal`](../sdk-minimal/README.md) bundle reuses the same startup provider and supplies its own profile name.
 
-The startup provider binds stdin EOF to the launcher's bounded successful shutdown. SDK protocol `shutdown`, SIGINT, and SIGTERM retain their owning server or launcher paths; disposal drains the root profile tree and persistence. Stdout is reserved for newline-delimited JSON-RPC frames. The bundle disables model-generated session titles because the SDK exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. It also keeps the base projection cache disabled: the SDK protocol exposes no session-listing surface, and the cache's write-behind would force session-log flushes at cache-chosen times, changing the durable JSONL batching this profile's keyless replay fixtures pin. A deployment selects a different complete composition through profile bundles and patch files, not another app bin.
+The startup provider binds stdin EOF to the launcher's bounded successful shutdown. SDK protocol `shutdown`, SIGINT, and SIGTERM retain their owning server or launcher paths; disposal drains the root profile tree and persistence. Stdout is reserved for newline-delimited JSON-RPC frames. The bundle disables model-generated session titles because the SDK exposes no title surface; deterministic fallback titles remain durable without an auxiliary model request. The inherited projection cache checkpoints SDK-created sessions for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. A deployment selects a different complete composition through profile bundles and patch files, not another app bin.
 
 | Config | Default | Behavior |
 |---|---|---|

+ 1 - 1
packages/bundle/sdk-app/README.zh.md

@@ -4,7 +4,7 @@
 
 以 [`dsh-base`](../base/README.zh.md) 为基础的 SDK stdio 应用 `dsh` profile 组合包。它继承 base 默认禁用模块 HMR(热模块替换)的策略;其 patch 设置 coding agent(编程智能体)persona、挂载应用自有的零选项命令提供方,并且只在该提供方接受调用后启动 [`dsh-sdk-jsonrpc-server`](../../sdk/server/README.zh.md)。因此,`dsh --profile sdk --help` 会写出 help 并退出,不会占用 stdin 或 stdout。独立的 [`sdk-minimal`](../sdk-minimal/README.zh.md) 组合包复用同一个启动提供方,并提供自己的 profile 名称。
 
-启动提供方把 stdin EOF 接到启动器的有界成功关闭流程。SDK 协议 `shutdown`、SIGINT 与 SIGTERM 继续使用各自所属的 server 或启动器路径;dispose(资源释放)会排空根 profile 配置树与持久化。stdout 专用于按换行分隔的 JSON-RPC 帧。SDK 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。本组合包同样禁用 base 的投影缓存:SDK 协议不提供 session 列表表层,而缓存的写回会在缓存选定的时机强制 session 日志 flush,从而改变本 profile 免密钥回放 fixtures 所固定的持久 JSONL 批处理格式。部署通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个应用 bin。
+启动提供方把 stdin EOF 接到启动器的有界成功关闭流程。SDK 协议 `shutdown`、SIGINT 与 SIGTERM 继续使用各自所属的 server 或启动器路径;dispose(资源释放)会排空根 profile 配置树与持久化。stdout 专用于按换行分隔的 JSON-RPC 帧。SDK 不提供 title 表层,因此本组合包禁用模型生成的 session title;确定性的 fallback title 仍会持久化,但不发起辅助模型请求。继承的投影缓存会为 SDK 创建的会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。部署通过 profile 组合包与 patch 文件选择另一套完整组合,而不是使用另一个应用 bin。
 
 | 配置 | 默认值 | 行为 |
 |---|---|---|

+ 0 - 8
packages/bundle/sdk-app/cordis.patch.yml

@@ -8,14 +8,6 @@
 - id: session-title-llm
   disabled: true
 
-# The base projection cache serves session-listing columns for web surfaces.
-# The SDK protocol exposes no listing surface, and the cache's write-behind
-# would force session-log flushes at cache-chosen times — changing the durable
-# JSONL batching this profile's keyless replay fixtures pin — so this bundle
-# disables the base row.
-- id: session-projection-cache
-  disabled: true
-
 - insert:
     - id: sdk-app-startup
       name: '@deepseek-ai/dsh-sdk-app'

+ 2 - 1
snapshots/acp/escalation-rejected/session.jsonl

@@ -29,7 +29,8 @@
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
 {"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," rejected"," the"," escalation","."," As"," instructed",","," I"," will"," not"," ret","ry"," and"," will"," not"," work"," around"," it"," —"," just"," explain"," in"," one"," short"," sentence"," and"," stop","."]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
-{"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," rejected"," the"," permission"," escalation",","," so"," this"," command"," cannot"," be"," run","."]}}
+{"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0],"texts":["The"," user"," rejected"," the"," permission"]}}
+{"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0,0,0,0,0],"texts":[" escalation",","," so"," this"," command"," cannot"," be"," run","."]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user rejected the escalation. As instructed, I will not retry and will not work around it — just explain in one short sentence and stop."}}}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"The user rejected the permission escalation, so this command cannot be run."}}}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":69,"outputTokens":45,"cacheReadTokens":1664,"reasoningTokens":30}}}}

+ 2 - 1
snapshots/sdk/subagent-mixed/session.jsonl

@@ -54,7 +54,8 @@
 {"type":"step/end","data":{"turn":2,"step":2}}
 {"type":"step/start","data":{"turn":2,"step":3}}
 {"type":"assistant/chunk","data":{"turn":2,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
-{"type":"reasoning-chunks","data":{"turn":2,"step":3,"index":0,"dt":[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],"texts":["Both"," sub","agents"," returned",":\n","1","."," First"," (","fresh"," child","):"," \"","AL","P","HA","\"\n","2","."," Second"," (","fork","ed"," child","):"," \"","SA","FF","RON","\""," -"," correctly"," inherited"," the"," conversation"," context"," where"," I"," was"," asked"," to"," remember"," the"," cod","ew","ord"," \"","SA","FF","RON","\".\n\n","Now"," I"," reply"," with"," \"","PAR","ENT","_D","ONE","\""," as"," instructed","."]}}
+{"type":"reasoning-chunks","data":{"turn":2,"step":3,"index":0,"dt":[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],"texts":["Both"," sub","agents"," returned",":\n","1","."," First"," (","fresh"," child","):"," \"","AL","P","HA","\"\n","2"]}}
+{"type":"reasoning-chunks","data":{"turn":2,"step":3,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],"texts":["."," Second"," (","fork","ed"," child","):"," \"","SA","FF","RON","\""," -"," correctly"," inherited"," the"," conversation"," context"," where"," I"," was"," asked"," to"," remember"," the"," cod","ew","ord"," \"","SA","FF","RON","\".\n\n","Now"," I"," reply"," with"," \"","PAR","ENT","_D","ONE","\""," as"," instructed","."]}}
 {"type":"assistant/chunk","data":{"turn":2,"step":3,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
 {"type":"text-chunks","data":{"turn":2,"step":3,"index":1,"dt":[0,0,0],"texts":["PAR","ENT","_D","ONE"]}}
 {"type":"assistant/chunk","data":{"turn":2,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"Both subagents returned:\n1. First (fresh child): \"ALPHA\"\n2. Second (forked child): \"SAFFRON\" - correctly inherited the conversation context where I was asked to remember the codeword \"SAFFRON\".\n\nNow I reply with \"PARENT_DONE\" as instructed."}}}}

+ 3 - 1
snapshots/session/code-mode-turn/session.jsonl

@@ -29,7 +29,9 @@
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"step/start","data":{"turn":1,"step":2}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
-{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," program"," ran"," successfully","."," The"," console",".log"," output"," \"","capt","ured"," output","\""," appeared",","," and"," the"," return"," value"," is"," \"","CODE","_","ONE","+","CODE","_T","WO","\"."," The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only","."]}}
+{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
+{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" program"}}}
+{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":[" ran"," successfully","."," The"," console",".log"," output"," \"","capt","ured"," output","\""," appeared",","," and"," the"," return"," value"," is"," \"","CODE","_","ONE","+","CODE","_T","WO","\"."," The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only","."]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
 {"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0,0,0],"texts":["CODE","_","ONE","+","CODE","_T","WO"]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}}}

+ 2 - 2
snapshots/session/cordis-inspect-jsdoc/system-prompt.expected.md

@@ -23,6 +23,8 @@ Use the web_search tool to discover current information on the web. The required
 
 Use goal tools for one long-running completion objective in the current session. create_goal may infer goal intent from a direct human request in any language; do not create a goal for routine single-turn work. Call get_goal before update_goal and copy its exact goal_id and revision. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least 3 consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.
 
+Use the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
+
 # Dynamic Cordis Plugins
 
 Dynamic Cordis plugins temporarily extend the current DSH process. A Plugin uses apply(ctx) to consume Services, listen to Events, provide Services, register model Tools, or register browser UI in Slots.
@@ -129,8 +131,6 @@ return {
 - After a technical failure, use cordis_inspect_self to read the exact Package source and its message/stack. Define a corrected Package under the same Plugin and retry autonomously.
 - Use the cordis-plugin-development Skill for other failure causes, repair procedures, and complete extension patterns.
 
-Use the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
-
 Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.
 
 Use subagent in the background by default. Start independent delegations together in one assistant message and continue useful work while they run. Set `run_in_background: false` only when your next action depends on that subagent's result. When a background run settles, the runtime sends you a notice containing its outcome and any final assistant message.

+ 2 - 1
snapshots/session/workflow-run/session.jsonl

@@ -29,7 +29,8 @@
 {"type":"step/end","data":{"turn":1,"step":1}}
 {"type":"step/start","data":{"turn":1,"step":2}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
-{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0],"texts":["The"," workflow"," returned"," successfully"," with"," the"," reply"," \"","WF","_CH","ILD","_OK","\"."," Now"," I"," need"," to"," reply"," with"," exactly"," \"","WORK","FL","OW","_D","ONE","\""," and"," stop","."]}}
+{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," workflow"," returned"," successfully"," with"," the"," reply"," \"","WF","_CH","ILD","_OK","\"."," Now"," I"," need"," to"," reply"," with"," exactly"," \"","WORK","FL","OW","_D","ONE"]}}
+{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[0,1,0],"texts":["\""," and"," stop","."]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
 {"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0],"texts":["WORK","FL","OW","_D","ONE"]}}
 {"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The workflow returned successfully with the reply \"WF_CHILD_OK\". Now I need to reply with exactly \"WORKFLOW_DONE\" and stop."}}}}