Merge remote-tracking branch 'origin/master' into feat/web-presenter

# Conflicts:
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
This commit is contained in:
Chinesezjc
2026-07-30 18:59:46 +08:00
447 changed files with 10444 additions and 4264 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 examples/README.md
README.md: 7f12178d1b67f1ebfac6f4f0e31403c54106e98f
README.zh.md: c7c1bf76593661616464558e554d57340d7c03b1
README.md: a502f34128da497586d593f64d0ce1c05f68a067
README.zh.md: e3c111bb6a8f67899b6f434345baa3b47640b7ee

View File

@@ -2,7 +2,7 @@
English | [中文](README.zh.md)
Runnable demos (not workspaces) that showcase how the harness is wired. Each example is a **thin leaf**: a `cordis.yml` that picks swappable backends, loads one app package, and may add optional product tools. The composition and boot glue live in [`@deepseek-ai/dsh-tui-demo`](../packages/examples/tui-demo), [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo), [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo), and their shared [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) bundle. There is no `start.ts`; the terminal `demo:*` scripts boot through the [`dsh`](../apps/cli/README.md) CLI (which mounts the `tui-demo` bundle), and the headless/ACP scripts invoke the `cli-demo`/`acp-demo` bins.
Runnable demos (not workspaces) that showcase how the harness is wired. Each example is a **thin leaf**: either a `cordis.yml` tree that picks swappable backends and loads one app package, or an **overlay** — a patch list `dsh --config` applies over the shipped composition ([`apps/cli/config/base.cordis.yml`](../apps/cli/config/base.cordis.yml) plus a surface overlay). Bundled compositions live in [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo), [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo), and their shared [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) bundle; the `dsh` surfaces use flat config trees instead. There is no `start.ts`; the terminal `demo:*` scripts boot through the [`dsh`](../apps/cli/README.md) CLI, and the headless/ACP scripts invoke the `cli-demo`/`acp-demo` bins.
## headless-agent
@@ -10,26 +10,20 @@ A non-interactive agent demo that accepts one positional task, runs one complete
Run with: `pnpm run demo:headless "task"` (needs `DEEPSEEK_API_KEY`). See [headless-agent/README.md](headless-agent/README.md) for the output contract, safety boundaries, and snapshot suite.
## tui-agent
The interactive coding agent: DeepSeek V4, filesystem and bash tools, subagents, workflows, `todo_write`, compaction, and the full-screen TUI. It is also the home of TUI PTY and snapshot scenarios.
Run with: `pnpm run demo:tui` (needs `DEEPSEEK_API_KEY`). Run its Code Mode overlay with `pnpm run demo:code-mode`. See [tui-agent/README.md](tui-agent/README.md) for controls and composition.
## jsonrpc-agent
An unattended coding agent driven through the Python SDK: JSON-RPC stdio, foreground-only `bash`, `read` / `write` / `edit`, one foreground `subagent`, `todo_write`, JSONL persistence, and compaction. It excludes terminal UI, stdout logging, approvals, skills, and background task controls. See [jsonrpc-agent/README.md](jsonrpc-agent/README.md).
## cordis-agent
## web-cordis
The **self-referential** demo: the coding spine plus [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis), whose three tools (`cordis_inspect` / `cordis_mount` / `cordis_unmount`) let the agent inspect the current DSH process, mount model-written temporary Plugins (an event listener, a brand-new tool, or a service another temporary Plugin injects), and unmount them again. These Plugins exist only in memory and share one internal `cordis-dynamic` fiber subtree; `ctx.fs`/`ctx.web` ride along provider-only as capabilities they can use.
Run the TUI with `pnpm run demo:cordis`, the browser UI at `http://127.0.0.1:3081` with `pnpm run demo:cordis web`, or the ACP server with `pnpm run demo:cordis acp` (all need `DEEPSEEK_API_KEY`). See [cordis-agent/README.md](cordis-agent/README.md) for the staged demo script and [the toolset Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md) for the design and sandbox caveats.
Run the browser UI at `http://127.0.0.1:3081` with `pnpm run demo:cordis`, or the ACP server with `pnpm run demo:cordis acp` (both need `DEEPSEEK_API_KEY`). See [the toolset Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md) for the design and sandbox caveats.
## acp-agent
An agent exposed as an **Agent Client Protocol (ACP)** automation server over JSON-RPC stdio, via [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo). Programmatic clients create fresh sessions, send text prompts, consume committed assistant text, answer one-shot permission requests, and cancel work. It owns the ACP keyless snapshot suite.
Run with: `pnpm run demo:acp` (needs `DEEPSEEK_API_KEY`); `pnpm run demo:code-mode acp` boots the same server in Code Mode via the `code-mode.cordis.yml` overlay. See [acp-agent/README.md](acp-agent/README.md) for the protocol and snapshot-test contracts.
Run with: `pnpm run demo:acp` (needs `DEEPSEEK_API_KEY`); `pnpm run demo:code-mode` boots the same server in Code Mode via the `code-mode.cordis.yml` overlay. See [acp-agent/README.md](acp-agent/README.md) for the protocol and snapshot-test contracts.
The default `cordis.yml` composes [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local), [`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox), and [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval). `workspace-write` confines bash and filesystem mutations to each session workspace; a wider retry becomes a one-shot machine permission request over ACP.

View File

@@ -2,34 +2,28 @@
[English](README.md) | 中文
展示 harness 如何组装的可运行演示(不是 workspace。每个示例都是一个 **轻量叶节点**一份选择可替换后端、加载一个应用包package并可添加可选产品工具`cordis.yml`。组合和启动粘合代码位于 [`@deepseek-ai/dsh-tui-demo`](../packages/examples/tui-demo)、[`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo)、[`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 及它们共享的 [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) 组合包中。没有 `start.ts`;终端 `demo:*` 脚本通过 [`dsh`](../apps/cli/README.md) CLI命令行界面启动(该 CLI 挂载 `tui-demo` 组合包)无头ACPAgent Client Protocol脚本则调用 `cli-demo`/`acp-demo` bin。
展示 harness 如何接线的可运行演示(不是 workspace。每个示例都是一个 **轻量叶节点**要么是一份选择可替换后端、加载一个应用包package`cordis.yml` 配置树,要么是一个 **overlay**——由 `dsh --config` 叠加到交付组合([`apps/cli/config/base.cordis.yml`](../apps/cli/config/base.cordis.yml) 加一份 surface overlay之上的 patch 列表。成组的组合位于 [`@deepseek-ai/dsh-cli-demo`](../packages/examples/cli-demo)、[`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 及它们共享的 [`@deepseek-ai/dsh-agent-spine-demo`](../packages/examples/agent-spine-demo) 组合包中`dsh` 的各 surface 则改用平铺 config tree。没有 `start.ts`;终端 `demo:*` 脚本通过 [`dsh`](../apps/cli/README.md) CLI命令行界面启动无头ACPAgent Client Protocol脚本则调用 `cli-demo`/`acp-demo` bin。
## headless-agent
非交互式 agent智能体演示接受一个位置参数形式的任务,在 `@deepseek-ai/dsh-cli-demo` 应用上运行一个完整模型/工具轮次,持久化新会话,打印 `text``json``stream-json`,然后退出。
非交互式 agent智能体演示接受一个位置任务`@deepseek-ai/dsh-cli-demo` 应用上运行一个完整模型/工具轮次,持久化新会话,打印 `text``json``stream-json`,然后退出。
运行:`pnpm run demo:headless "task"`(需要 `DEEPSEEK_API_KEY`)。输出契约、安全边界和快照套件详见 [headless-agent/README.md](headless-agent/README.md)。
## tui-agent
交互式编码 agentDeepSeek V4、文件系统与 bash 工具、subagent、工作流、`todo_write`、压缩compaction和全屏 TUI。这里也是 TUI PTY 与快照场景的归属地。
运行:`pnpm run demo:tui`(需要 `DEEPSEEK_API_KEY`)。使用 `pnpm run demo:code-mode` 运行其 Code Mode 覆盖。控制与组合详见 [tui-agent/README.md](tui-agent/README.md)。
## jsonrpc-agent
通过 Python SDK 驱动的无人值守编码 agentJSON-RPC stdio、仅前台 `bash``read`/`write`/`edit`、一个前台 `subagent``todo_write`、JSONL 持久化和压缩。它不包含终端 UI、stdout 日志、批准、skill(技能)和后台任务控制。详见 [jsonrpc-agent/README.md](jsonrpc-agent/README.md)。
通过 Python SDK 驱动的无人值守编码 agentJSON-RPC stdio、仅前台 `bash``read`/`write`/`edit`、一个前台 `subagent``todo_write`、JSONL 持久化和压缩。它不包含终端 UI、stdout 日志、批准、skill 和后台任务控制。详见 [jsonrpc-agent/README.md](jsonrpc-agent/README.md)。
## cordis-agent
## web-cordis
**自指** 演示:编码主干加 [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis),其三个工具(`cordis_inspect`/`cordis_mount`/`cordis_unmount`)使 agent 可以检查当前 DSH 进程、挂载模型编写的临时插件(事件监听器、一个全新工具,或一个供另一临时插件注入的服务),并再次卸载它们。这些插件只存在于内存中,共享一个内部 `cordis-dynamic` fiber 子树;`ctx.fs`/`ctx.web` 仅作为它们可用的能力提供方。
**自指** 演示:编码主干加 [`@deepseek-ai/dsh-tool-cordis`](../packages/cordis/tool-cordis),其三个工具(`cordis_inspect`/`cordis_mount`/`cordis_unmount`)使 agent 可以检查当前 DSH 进程、挂载模型编写的临时 Plugin(事件监听器、一个全新工具,或一个供另一临时 Plugin 注入的服务),并再次卸载它们。这些 Plugin 只存在于内存中,共享一个内部 `cordis-dynamic` fiber 子树;`ctx.fs`/`ctx.web` 仅作为它们可用的能力提供方。
使用 `pnpm run demo:cordis` 运行 TUI使用 `pnpm run demo:cordis web``http://127.0.0.1:3081` 启动浏览器 UI或使用 `pnpm run demo:cordis acp` 启动 ACP 服务器(者均需 `DEEPSEEK_API_KEY`)。分阶段演示脚本详见 [cordis-agent/README.md](cordis-agent/README.md)设计与沙箱注意事项详见[工具集 Agent Noteagent 决策记录)](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)。
使用 `pnpm run demo:cordis``http://127.0.0.1:3081` 启动浏览器 UI或使用 `pnpm run demo:cordis acp` 启动 ACP 服务器(者均需 `DEEPSEEK_API_KEY`)。设计与沙箱注意事项详见[工具集 Agent Note](../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)。
## acp-agent
一个通过 JSON-RPC stdio 公开、作为 **Agent Client Protocol (ACP)** 自动化服务器运行的 agent由 [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 提供。程序化客户端可以创建新会话、发送文本提示词、消费已提交的 assistant 文本、回答一次性权限请求并取消工作。它拥有 ACP 无密钥快照套件。
作为 **Agent Client Protocol (ACP)** 自动化服务器通过 JSON-RPC stdio 公开的 agent由 [`@deepseek-ai/dsh-acp-demo`](../packages/examples/acp-demo) 提供。程序化客户端可以创建新会话、发送文本提示词、消费已提交的 assistant 文本、回答一次性权限请求并取消工作。它拥有 ACP 无密钥快照套件。
运行:`pnpm run demo:acp`(需要 `DEEPSEEK_API_KEY``pnpm run demo:code-mode acp` 通过 `code-mode.cordis.yml` 覆盖以 Code Mode 启动同一服务器。协议与快照测试契约详见 [acp-agent/README.md](acp-agent/README.md)。
运行:`pnpm run demo:acp`(需要 `DEEPSEEK_API_KEY``pnpm run demo:code-mode` 通过 `code-mode.cordis.yml` 覆盖以 Code Mode 启动同一服务器。协议与快照测试契约详见 [acp-agent/README.md](acp-agent/README.md)。
默认 `cordis.yml` 组合 [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local)、[`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox) 和 [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval)。`workspace-write` 将 bash 和文件系统变更限制在每个会话 workspace 中;请求更广泛沙箱权限的重试会通过 ACP 触发一次性机器权限请求。
默认 `cordis.yml` 组合 [`@deepseek-ai/dsh-sandbox-local`](../packages/sandbox/sandbox-local)、[`@deepseek-ai/dsh-bash-sandbox`](../packages/bash/bash-sandbox) 和 [`@deepseek-ai/dsh-user-approval`](../packages/ui/user-approval)。`workspace-write` 将 bash 和文件系统变更限制在每个会话 workspace 中;范围更广的重试会通过 ACP 成为一次性机器权限请求。

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 examples/acp-agent/README.md
README.md: 0d63ec1f2d9165b9faf0817bd94fbe15b97fa961
README.zh.md: 84482aad8352ab38527dcf4d9e1bfefc8d496c91
README.md: a37954d4d52900413a506f227759a0a7dd2a25d0
README.zh.md: a38431ea2f01fe16ff39b5dd00ec23e881713f5d

View File

@@ -6,7 +6,7 @@ Automation-oriented [Agent Client Protocol](https://agentclientprotocol.com) ser
```sh
pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
pnpm run demo:code-mode acp # same protocol with the Code Mode tool transport
pnpm run demo:code-mode # same protocol with the Code Mode tool transport
```
The leaf loads the ACP app, DeepSeek adapter, sandboxed bash and filesystem stacks, one-shot approval policy, compaction, subagents, workflows, hooks, a derived session-query index, and repeat guard. The app creates one fresh agent per `session/new`, persists sessions to JSONL, and keeps stdout protocol-pure. [`session-query.cordis.yml`](session-query.cordis.yml) explicitly opts into the workspace-authorized query tools and generic timeout/spill policies for their dedicated snapshot; [`fs.cordis.yml`](fs.cordis.yml) adds spill storage for filesystem scenarios, [`code-mode.cordis.yml`](code-mode.cordis.yml) adds `run_code` and its generated TypeScript SDK, and [`web.cordis.yml`](web.cordis.yml) adds the web seam, the local fetch provider, `web_fetch`, and a loopback HTML fixture server for the web-fetch snapshot.

View File

@@ -6,7 +6,7 @@
```sh
pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
pnpm run demo:code-mode acp # same protocol with the Code Mode tool transport
pnpm run demo:code-mode # same protocol with the Code Mode tool transport
```
该叶节点加载 ACP 应用、DeepSeek 适配器、受沙箱限制的 bash 与文件系统栈、一次性批准策略、压缩compaction、subagent、工作流、钩子、派生会话查询索引和重复守卫。应用为每次 `session/new` 创建一个新 agent将会话持久化到 JSONL并保持 stdout 只含协议内容。[`session-query.cordis.yml`](session-query.cordis.yml) 为其专用快照显式选用 workspace 授权的查询工具和通用超时/溢出策略;[`fs.cordis.yml`](fs.cordis.yml) 为文件系统场景添加溢出存储,[`code-mode.cordis.yml`](code-mode.cordis.yml) 添加 `run_code` 及其生成的 TypeScript SDK[`web.cordis.yml`](web.cordis.yml) 则为 web-fetch 快照添加 web seam、本地抓取提供方、`web_fetch` 与一个回环 HTML fixture测试前置数据服务器。

View File

@@ -1,6 +1,6 @@
# Code Mode adds `ctx.codeRuntime` and changes the registry to one wire tool,
# `run_code`, plus its generated TypeScript SDK prompt. The app bin selects this
# overlay for `demo:code-mode acp` and snapshot recording, and selects the sibling
# overlay for `demo:code-mode` and snapshot recording, and selects the sibling
# replay overlay for `DSH_SNAPSHOT=replay`. A config patch replaces the whole app
# config, so unchanged base fields are restated below.
- id: base

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{"type":"session","version":0,"id":"ada8966c-9fa3-441b-8721-37ff1e795e6a","createdAt":1783352137161,"cwd":"{{cwd}}","parentSession":"96cf59c9-b347-48b9-b234-a5200913ad05","seedLength":38,"delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352134837,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352134838,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is MARMALADE. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"917c2f1a-be80-4f54-86e8-c94fe6859bdd"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352134838,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is MARMALADE. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"ecede90b-f918-4b3c-81cc-aefcc375d269"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352134838,"data":{"title":"Remember this fact for later:","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1783352134840,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1783352134840,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
@@ -12,21 +12,22 @@
{"type":"assistant/chunk","seq":32,"time":1783352135771,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"OK"}}}}
{"type":"assistant/chunk","seq":33,"time":1783352135771,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}}}}
{"type":"assistant/chunk","seq":34,"time":1783352135771,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":35,"time":1783352135773,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember the codeword \"MARMALADE\" and reply with just \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"5927ef74-0269-4474-a6c0-45c09c1adac5"},"usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34],"surfaceOp":"append"}
{"type":"assistant/message","seq":35,"time":1783352135773,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember the codeword \"MARMALADE\" and reply with just \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"d6c3a4bf-20e0-459f-9bc9-945f6650b5f1"},"usage":{"inputTokens":2885,"outputTokens":25,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34],"surfaceOp":"append"}
{"type":"step/end","seq":36,"time":1783352135773,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":37,"time":1783352135773,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/start","seq":38,"time":1783352137162,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":39,"time":1783352137163,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"840f1fca-2577-47c1-acee-c47125098882"},"surfaceOp":"append"}
{"type":"step/start","seq":40,"time":1783352137163,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":41,"time":1785142305260,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":42,"time":1783352137783,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":43,"time0":1783352137783,"data":{"turn":2,"step":1,"index":0,"dt":[178,28,31,26,0,0,0,28,1,0,0,0,0,28,0,0,0,0,28,28,1,0,0,28,0,0,29,0,0,28,1,28,1],"texts":["The"," user"," asked"," me"," to"," remember"," the"," project"," cod","ew","ord"," \"","M","ARM","AL","ADE","\""," and"," now"," they","'re"," asking"," what"," it"," is","."," I"," should"," just"," reply"," with"," that"," word","."]}}
{"type":"assistant/chunk","seq":77,"time":1783352138275,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":78,"time0":1783352138275,"data":{"turn":2,"step":1,"index":1,"dt":[0,0,30],"texts":["M","ARM","AL","ADE"]}}
{"type":"assistant/chunk","seq":82,"time":1783352138307,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."}}}}
{"type":"assistant/chunk","seq":83,"time":1783352138307,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"MARMALADE"}}}}
{"type":"assistant/chunk","seq":84,"time":1783352138307,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}}}}
{"type":"assistant/chunk","seq":85,"time":1785142305270,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":86,"time":1785142305270,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."},{"type":"text","text":"MARMALADE"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"0376771a-3af4-41ec-9ee6-750ba6d65b25"},"usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}},"sourceEventSeqs":[42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85],"surfaceOp":"append"}
{"type":"step/end","seq":87,"time":1785142305270,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":88,"time":1785142305270,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":38,"time":1785396256785,"data":{}}
{"type":"turn/start","seq":39,"time":1785381572224,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":40,"time":1785381572224,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"6f050d06-7445-4651-9958-345b6410f3d7"},"surfaceOp":"append"}
{"type":"step/start","seq":41,"time":1785381572240,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":42,"time":1785381572241,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":43,"time":1783352137783,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":44,"time0":1783352137961,"data":{"turn":2,"step":1,"index":0,"dt":[28,31,26,0,0,0,28,1,0,0,0,0,28,0,0,0,0,28,28,1,0,0,28,0,0,29,0,0,28,1,28,1,0],"texts":["The"," user"," asked"," me"," to"," remember"," the"," project"," cod","ew","ord"," \"","M","ARM","AL","ADE","\""," and"," now"," they","'re"," asking"," what"," it"," is","."," I"," should"," just"," reply"," with"," that"," word","."]}}
{"type":"assistant/chunk","seq":78,"time":1783352138275,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":79,"time0":1783352138275,"data":{"turn":2,"step":1,"index":1,"dt":[0,30,2],"texts":["M","ARM","AL","ADE"]}}
{"type":"assistant/chunk","seq":83,"time":1783352138307,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."}}}}
{"type":"assistant/chunk","seq":84,"time":1783352138307,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"MARMALADE"}}}}
{"type":"assistant/chunk","seq":85,"time":1785142305270,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}}}}
{"type":"assistant/chunk","seq":86,"time":1785381572250,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":87,"time":1785381572250,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user asked me to remember the project codeword \"MARMALADE\" and now they're asking what it is. I should just reply with that word."},{"type":"text","text":"MARMALADE"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"db43685f-dd37-4558-926d-7a758305a84d"},"usage":{"inputTokens":97,"outputTokens":39,"cacheReadTokens":2816,"reasoningTokens":34}},"sourceEventSeqs":[43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86],"surfaceOp":"append"}
{"type":"step/end","seq":88,"time":1785381572250,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":89,"time":1785381572251,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -1,6 +1,6 @@
{"type":"session","version":0,"id":"02b3a8dd-1d5e-4866-825f-5fbf5000a632","createdAt":1783352147504,"cwd":"{{cwd}}","parentSession":"959ffdf5-03e2-465e-9482-009b704632dc","seedLength":32,"delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783352142834,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352142834,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is SAFFRON. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"867b46b8-e2fa-4257-a2b1-a8fa12abe782"},"surfaceOp":"append"}
{"type":"user/message","seq":1,"time":1783352142834,"data":{"content":[{"type":"text","text":"Remember this fact for later: the project codeword is SAFFRON. Reply with the single word OK and stop. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"9f3b1367-3a0e-4793-9ecf-ae67a79f24d2"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1783352142834,"data":{"title":"Remember this fact for later:","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1783352142835,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1783352142836,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
@@ -12,21 +12,22 @@
{"type":"assistant/chunk","seq":26,"time":1783352143768,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"OK"}}}}
{"type":"assistant/chunk","seq":27,"time":1783352143768,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}}}}
{"type":"assistant/chunk","seq":28,"time":1783352143768,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":29,"time":1783352143771,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember a codeword and just reply with \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"5dc3014f-f57f-4686-bbe9-8b89079c0b18"},"usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
{"type":"assistant/message","seq":29,"time":1783352143771,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to remember a codeword and just reply with \"OK\"."},{"type":"text","text":"OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"59fa3190-4060-40db-a0a5-97f2fa4172f3"},"usage":{"inputTokens":2883,"outputTokens":19,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
{"type":"step/end","seq":30,"time":1783352143771,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":31,"time":1783352143771,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/start","seq":32,"time":1783352147508,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":33,"time":1783352147509,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"9809d0e2-3997-4c6c-83ea-f28538b83ad9"},"surfaceOp":"append"}
{"type":"step/start","seq":34,"time":1783352147509,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":35,"time":1785142306299,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":36,"time":1783352147925,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":37,"time0":1783352147925,"data":{"turn":2,"step":1,"index":0,"dt":[94,29,1,0,27,0,1,0,0,0,29,0,0,0,35,0,0,0,0,26,29,31,0,30,0,0,27,1,27,0],"texts":["The"," user"," is"," asking"," me"," to"," recall"," the"," project"," cod","ew","ord"," that"," was"," mentioned"," earlier"," in"," the"," conversation","."," I"," was"," told"," to"," remember"," it",":"," SA","FF","RON","."]}}
{"type":"assistant/chunk","seq":68,"time":1783352148313,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":69,"time0":1783352148313,"data":{"turn":2,"step":1,"index":1,"dt":[0,31],"texts":["SA","FF","RON"]}}
{"type":"assistant/chunk","seq":72,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."}}}}
{"type":"assistant/chunk","seq":73,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SAFFRON"}}}}
{"type":"assistant/chunk","seq":74,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}}}}
{"type":"assistant/chunk","seq":75,"time":1785142306309,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":76,"time":1785142306309,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."},{"type":"text","text":"SAFFRON"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"7c0c4a97-79fe-4429-a963-8e24633e6335"},"usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}},"sourceEventSeqs":[36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75],"surfaceOp":"append"}
{"type":"step/end","seq":77,"time":1785142306309,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":78,"time":1785142306309,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":32,"time":1785396258235,"data":{}}
{"type":"turn/start","seq":33,"time":1785381573526,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":34,"time":1785381573526,"data":{"content":[{"type":"text","text":"What is the project codeword mentioned earlier in this conversation? Reply with exactly that one word and nothing else."}],"source":{"kind":"user"},"role":"user","id":"9f252dc0-3b24-4607-b761-30711b726edb"},"surfaceOp":"append"}
{"type":"step/start","seq":35,"time":1785381573543,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":36,"time":1785381573543,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"resume"}}
{"type":"assistant/chunk","seq":37,"time":1783352147925,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":38,"time0":1783352148019,"data":{"turn":2,"step":1,"index":0,"dt":[29,1,0,27,0,1,0,0,0,29,0,0,0,35,0,0,0,0,26,29,31,0,30,0,0,27,1,27,0,1],"texts":["The"," user"," is"," asking"," me"," to"," recall"," the"," project"," cod","ew","ord"," that"," was"," mentioned"," earlier"," in"," the"," conversation","."," I"," was"," told"," to"," remember"," it",":"," SA","FF","RON","."]}}
{"type":"assistant/chunk","seq":69,"time":1783352148313,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":70,"time0":1783352148313,"data":{"turn":2,"step":1,"index":1,"dt":[31,1],"texts":["SA","FF","RON"]}}
{"type":"assistant/chunk","seq":73,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."}}}}
{"type":"assistant/chunk","seq":74,"time":1783352148345,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SAFFRON"}}}}
{"type":"assistant/chunk","seq":75,"time":1785142306309,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}}}}
{"type":"assistant/chunk","seq":76,"time":1785381573552,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":77,"time":1785381573552,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user is asking me to recall the project codeword that was mentioned earlier in the conversation. I was told to remember it: SAFFRON."},{"type":"text","text":"SAFFRON"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"a39affbc-097b-4106-912a-99538d18eff8"},"usage":{"inputTokens":95,"outputTokens":35,"cacheReadTokens":2816,"reasoningTokens":31}},"sourceEventSeqs":[37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76],"surfaceOp":"append"}
{"type":"step/end","seq":78,"time":1785381573553,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":79,"time":1785381573553,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -1,6 +0,0 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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 examples/cordis-agent/README.md
README.md: 55970e932bc16d8361932daa9ea55af83ef73d33
README.zh.md: a8ec332d8b3673d6656663eb3bfd7d37a4e328f6

View File

@@ -1,37 +0,0 @@
# cordis-agent
English | [中文](README.zh.md)
The self-referential harness demo: the DeepSeek V4 coding spine on the full-screen TUI plus [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md), which lets the model inspect the current DSH process, mount in-memory temporary Plugins, and unmount them. Temporary Plugins remain active across turns but disappear on unmount, toolset unload, or DSH restart; they create no files or configuration and may affect other sessions in the process. The `ctx.fs` and `ctx.web` services are provider-only capabilities available to those Plugins. The design lives in [the toolset Agent Note](../../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md).
## Run it
```sh
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run demo:cordis # TUI (default)
pnpm run demo:cordis web # browser UI at http://127.0.0.1:3081
pnpm run demo:cordis acp # ACP server
```
The intended demo is staged — verify the listener link first, then let the agent extend itself:
```
> Mount a temporary Plugin that listens to the 'agent/status' event and logs every status change, then run `echo hi` with bash.
[tool call] cordis_mount({"code": "return { name: 'status-logger', apply(ctx) { ctx.on('agent/status', (agent, status) => console.log('status →', status)) } }"})
[tool result] Temporary Plugin dyn-1 is running (plugin "status-logger"; available until unmounted or DSH restarts).
[tool call] bash({"command": "echo hi"})
[cordis:dyn-1] status → … ← the temporary listener firing, live
> Now give yourself a reverse_text tool and use it on "harness".
[tool call] cordis_mount({"code": "return { name: 'reverse-text', inject: ['tools'], apply(ctx) { ctx.tools.register(harness.defineTool({ name: 'reverse_text', … })) } }"})
[tool call] reverse_text({"text": "harness"}) ← a tool the agent built for itself, one step earlier
> Unmount both temporary Plugins.
[tool call] cordis_unmount({"id": "dyn-1"})
```
Ask for `cordis_inspect` with `what: "api"` or `what: "events"` to see the generated service/event reference used to write Plugin code, and mount two cooperating temporary Plugins (`ctx.provide` in one, `inject` in the other) to watch Cordis park and revive the consumer.
## End-to-end tests
`tests/keyless-smoke.e2e.ts` boots the real `cordis.yml` through the Loader with a dummy key and asserts the banner, package-name resolution, and clean EOF exit. `tests/cordis-tools.e2e.ts` is the with-key smoke: a real model mounts a temporary status listener and the test verifies its tagged console line, creates and uses a `reverse_text` tool, and composes two temporary Plugins through provide/inject. [`packages/cordis/tool-cordis`](../../packages/cordis/tool-cordis) carries the unit coverage under the per-file 100% gate.

View File

@@ -1,37 +0,0 @@
# cordis-agent
[English](README.md) | 中文
自指 harness 演示:在全屏 TUI 上运行 DeepSeek V4 编码主干,并加载 [`@deepseek-ai/dsh-tool-cordis`](../../packages/cordis/tool-cordis/README.md)。后者让模型检查当前 DSH 进程、挂载仅存于内存的临时插件,并卸载它们。临时插件可跨轮次保持活跃,但会在卸载、工具集卸载或 DSH 重启后消失;它们不创建文件或配置,也可能影响同一进程中的其他会话。`ctx.fs``ctx.web` 仅以能力提供方形式加载,供这些插件使用。设计详见[工具集 Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md)。
## 运行
```sh
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run demo:cordis # TUI (default)
pnpm run demo:cordis web # browser UI at http://127.0.0.1:3081
pnpm run demo:cordis acp # ACP server
```
预期演示分阶段进行:先验证监听器链路,再让 agent智能体扩展自身
```
> Mount a temporary Plugin that listens to the 'agent/status' event and logs every status change, then run `echo hi` with bash.
[tool call] cordis_mount({"code": "return { name: 'status-logger', apply(ctx) { ctx.on('agent/status', (agent, status) => console.log('status →', status)) } }"})
[tool result] Temporary Plugin dyn-1 is running (plugin "status-logger"; available until unmounted or DSH restarts).
[tool call] bash({"command": "echo hi"})
[cordis:dyn-1] status → … ← the temporary listener firing, live
> Now give yourself a reverse_text tool and use it on "harness".
[tool call] cordis_mount({"code": "return { name: 'reverse-text', inject: ['tools'], apply(ctx) { ctx.tools.register(harness.defineTool({ name: 'reverse_text', … })) } }"})
[tool call] reverse_text({"text": "harness"}) ← a tool the agent built for itself, one step earlier
> Unmount both temporary Plugins.
[tool call] cordis_unmount({"id": "dyn-1"})
```
请求 `cordis_inspect` 并使用 `what: "api"``what: "events"`,即可查看编写插件代码所用的生成服务/事件资料。还可挂载两个协作临时插件(一个中调用 `ctx.provide`,另一个中使用 `inject`),观察 Cordis 如何暂停并恢复消费方。
## 端到端测试
`tests/keyless-smoke.e2e.ts` 使用虚拟密钥通过 Loader 启动真实 `cordis.yml`,并断言横幅、包名解析,以及收到 EOF 后正常退出。`tests/cordis-tools.e2e.ts` 是带密钥的冒烟测试:真实模型挂载一个临时状态监听器,测试验证其带标记的控制台输出行;然后创建并使用 `reverse_text` 工具,再通过 provide/inject 组合两个临时插件。[`packages/cordis/tool-cordis`](../../packages/cordis/tool-cordis) 包含相关单元测试,并受逐文件 100% 覆盖率门禁约束。

View File

@@ -1,55 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# Cordis Agent App Composition
The self-referential demo puts @deepseek-ai/dsh-tool-cordis on the coding spine, letting the agent inspect its current-process runtime and mount or unmount in-memory temporary Plugins.
```mermaid
flowchart LR
cfg["examples/cordis-agent<br/>cordis.yml"]
plugin_cordis_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg --> plugin_cordis_hmr
plugin_cordis_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg --> plugin_cordis_llm_deepseek
plugin_cordis_subprocess["subprocess<br/>@deepseek-ai/dsh-subprocess-local"]
cfg --> plugin_cordis_subprocess
plugin_cordis_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg --> plugin_cordis_bash
plugin_cordis_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg --> plugin_cordis_fs_local
plugin_cordis_web["web<br/>@deepseek-ai/dsh-web"]
cfg --> plugin_cordis_web
plugin_cordis_web_fetch_local["web-fetch-local<br/>@deepseek-ai/dsh-web-fetch-local"]
cfg --> plugin_cordis_web_fetch_local
plugin_cordis_token_meter["token-meter<br/>@deepseek-ai/dsh-token-meter"]
cfg --> plugin_cordis_token_meter
plugin_cordis_tui_agent["tui-agent<br/>@deepseek-ai/dsh-tui-demo"]
cfg --> plugin_cordis_tui_agent
plugin_cordis_tui_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
plugin_cordis_tui_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
plugin_cordis_tui_agent --> frontdoor_tui["@deepseek-ai/dsh-tui<br/>pre-created main agent"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
plugin_cordis_tool_cordis["tool-cordis<br/>@deepseek-ai/dsh-tool-cordis"]
cfg --> plugin_cordis_tool_cordis
```
| Plugin id | Package / module |
| --- | --- |
| `hmr` | `@cordisjs/plugin-hmr` |
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
| `subprocess` | `@deepseek-ai/dsh-subprocess-local` |
| `bash` | `@deepseek-ai/dsh-bash-local` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
| `web` | `@deepseek-ai/dsh-web` |
| `web-fetch-local` | `@deepseek-ai/dsh-web-fetch-local` |
| `token-meter` | `@deepseek-ai/dsh-token-meter` |
| `tui-agent` | `@deepseek-ai/dsh-tui-demo` |
| `tool-cordis` | `@deepseek-ai/dsh-tool-cordis` |
Source config: [`examples/cordis-agent/cordis.yml`](cordis.yml).
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.

View File

@@ -1,88 +0,0 @@
# Self-referential TUI demo: the coding spine plus tools to inspect the live
# service/plugin/tool/temporary/API/event state, mount a model-written temporary
# Plugin, and quiescently unmount it. The app bin loads the gitignored
# root `.env` before reading the required DeepSeek key and optional base URL.
# Trust stance: the vm and context façade limit accidental global/framework
# access but are not a security boundary; temporary Plugin code reaches live capabilities
# such as `ctx.bash`. Grant this toolset like bash access. See
# ../../.agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md.
# Development-only hot reload; production assemblies omit it.
- id: hmr
name: '@cordisjs/plugin-hmr'
config:
root: ['.']
# The DeepSeek adapter. Shipped default: full thinking at max effort on every
# request (wire-only defaults; they never enter the request header).
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
baseURL: !!js process.env.DEEPSEEK_BASE_URL
thinking: enabled
reasoningEffort: max
# Local bash executor for agent-spine-demo's tool-bash schema — gives the agent an
# ordinary tool whose calls make the mounted listeners observably fire.
# Managed child-process groups for the bash executor (spawn/kill/output plumbing).
- id: subprocess
name: '@deepseek-ai/dsh-subprocess-local'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 60000
# Filesystem service for mounted plugins (ctx.fs) — the local provider only.
# The model-facing read/write/edit tools stay unmounted on purpose: this demo
# is about the agent building its own tools over the services.
- id: fs-local
name: '@deepseek-ai/dsh-fs-local'
config:
cwd: !!js process.cwd()
# Web service for mounted plugins (ctx.web): the seam plus the anonymous local
# fetch provider (keyless). No search provider is loaded — ctx.web search
# calls fail loud until a deployment adds one.
- id: web
name: '@deepseek-ai/dsh-web'
- id: web-fetch-local
name: '@deepseek-ai/dsh-web-fetch-local'
- id: token-meter
name: '@deepseek-ai/dsh-token-meter'
# The app bundle pre-creates the self-referential demo's `main` agent.
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-pro
resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"
persistenceRoot: './.sessions'
workspaceContext:
maxBytes: 65536
welcome: 'cordis-agent ready. Ask it to inspect its runtime, mount a temporary listener, or invent a temporary tool for itself.'
persona: |
You are cordis-agent, a self-referential harness demo powered by the
{{model}} model.
You run INSIDE a cordis plugin runtime, and your cordis_* tools operate
on that live runtime: cordis_inspect to look around (its `api` and
`events` sections document the service methods, type shapes, and events
your Plugin code can use), cordis_mount to mount an in-memory temporary
Plugin (an event listener, a brand-new tool for yourself, or a service
another temporary Plugin injects), cordis_unmount to clean one up. These
Plugins remain across turns but disappear on unmount, toolset unload, or
DSH restart and may affect other sessions in this process. In Plugin code, NEVER use Node
built-ins (require/setTimeout/fetch) — use the runtime's cordis services
via inject: fs, web, bash, and timer (ctx.setTimeout). Prefer small
single-purpose plugins, prefer plain notification events over waterfall
events unless you intend to intercept, and unmount what you no longer
need. Report results briefly.
# The self-referential cordis toolset (loaded after the app so ctx.tools exists).
- id: tool-cordis
name: '@deepseek-ai/dsh-tool-cordis'

View File

@@ -1,7 +0,0 @@
{
"name": "cordis-agent-example",
"private": true,
"version": "0.0.1",
"type": "module",
"description": "Runnable demo: the self-referential harness — an agent that inspects and modifies its own cordis runtime"
}

View File

@@ -1,163 +0,0 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { Context } from 'cordis'
import { createUserMessage, CallId } from '@deepseek-ai/dsh-llm'
import { cordisHarness, waitForIdle } from './harness.ts'
import { SessionId } from '@deepseek-ai/dsh-session'
const testToolSignal = new AbortController().signal
/**
* With-key smoke for the self-referential cordis tools: a REAL model drives
* cordis_mount/cordis_unmount against the live context the test observes.
* World-verified, not self-reported: the mounted listener must actually WRITE
* its tagged console line, the self-made tool must actually EXIST in the
* registry and appear as a real `tool/call`, the cross-mount service must
* actually LAND in the reflect store. Key-gated (see vitest.e2e.config.ts).
*/
let ctx: Context | undefined
afterEach(async () => {
vi.restoreAllMocks()
// Always dispose the harness, even on failure/retry/timeout: agent-loop
// teardown stops the loop, and disposing the tree unwinds every dynamic
// mount the model left behind.
await ctx?.fiber.dispose()
ctx = undefined
})
/** The tagged write-through lines (`[cordis:dyn-n] …`) captured by a console spy. */
function taggedCalls(log: { mock: { calls: unknown[][] } }): unknown[][] {
return log.mock.calls.filter(call => typeof call[0] === 'string' && /^\[cordis:dyn-\d+\]$/.test(call[0]))
}
/** Model-facing text of one tool result, concatenated. */
function resultText(result: { content: { type: string; text?: string }[] }): string {
return result.content.filter(block => block.type === 'text').map(block => block.text).join('')
}
describe.skipIf(!process.env.DEEPSEEK_API_KEY)('cordis tools: a real model modifies its own runtime', () => {
it('mounts a temporary status listener whose tagged output actually fires, then unmounts it', async () => {
ctx = await cordisHarness()
const log = vi.spyOn(console, 'log').mockImplementation(() => {})
const agent = ctx.agentLoop.create(SessionId('cordis-e2e-listener'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup(createUserMessage({
content: [{
type: 'text',
text: 'Use cordis_mount to create a temporary Plugin that listens to the \'agent/status\' '
+ 'Cordis event and logs every change with console.log. Reply "running" once done.',
}], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
// The WORLD check: the turn's own running→idle transition must have driven
// the mounted listener through the tagged sandbox console.
expect(taggedCalls(log).length).toBeGreaterThan(0)
const mid = await ctx.tools.execute({
signal: testToolSignal,
callId: CallId('verify-mounted'), name: 'cordis_inspect', arguments: { what: 'temporary' },
})
expect(resultText(mid)).toContain('dyn-')
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Now unmount the temporary Plugin you just mounted.' }], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
const after = await ctx.tools.execute({
signal: testToolSignal,
callId: CallId('verify-unmounted'), name: 'cordis_inspect', arguments: { what: 'temporary' },
})
expect(resultText(after)).toContain('No temporary Plugins are running.')
}, 120_000)
it('builds itself a reverse_text tool and actually calls it', async () => {
ctx = await cordisHarness()
const agent = ctx.agentLoop.create(SessionId('cordis-e2e-selftool'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup(createUserMessage({
content: [{
type: 'text',
text: 'Give yourself a new tool: use cordis_mount to create a temporary Plugin with '
+ 'inject ["tools"] that calls harness.registerTool(ctx, harness.defineTool({...})) '
+ 'to register a tool named reverse_text with one required string parameter '
+ '"text", returning the text reversed. Then CALL reverse_text with the '
+ 'exact text "harness" and report its exact output.',
}], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
// World checks: the tool exists in the registry, was invoked as a real tool call, and its
// RESULT (the self-made execute actually running) is the reversed string. Model prose is only
// self-report and is deliberately not asserted.
expect(ctx.tools.get('reverse_text')).toBeDefined()
const events = [...agent.session.events]
const calls = events.filter(event => event.type === 'tool/call')
expect(calls.some(event => event.data.name === 'cordis_mount')).toBe(true)
const reverseCalls = calls.filter(event => event.data.name === 'reverse_text')
expect(reverseCalls.length).toBeGreaterThan(0)
const reverseResults = events
.filter(event => event.type === 'tool/result')
.filter(event => reverseCalls.some(call => call.data.callId === event.data.message.source.callId))
.flatMap(event => event.data.message.content[0].content.filter(block => block.type === 'text').map(block => block.text))
// On failure, surface what the model actually mounted and what the tool
// returned — an e2e failing at a distance is undebuggable without it.
const mountCode = calls
.filter(event => event.data.name === 'cordis_mount')
.map(event => event.data.arguments)
.join('\n---\n')
const trace = events.map((event) => {
switch (event.type) {
case 'tool/call': return `tool/call:${event.data.name}`
case 'tool/result': return `tool/result:${event.data.message.content[0].isError ? 'ERR:' + JSON.stringify(event.data.message.content[0].content).slice(0, 200) : 'ok'}`
case 'turn/end': return `turn/end:${JSON.stringify(event.data.reason)}`
default: return event.type
}
}).join('\n')
expect(
reverseResults.some(text => text.includes('ssenrah')),
`no reversed output in reverse_text results.\nresults: ${JSON.stringify(reverseResults)}\nmount code: ${mountCode}\ntrace:\n${trace}`,
).toBe(true)
}, 120_000)
it('composes two temporary Plugins through provide/inject, and unmounting the provider parks the consumer', async () => {
ctx = await cordisHarness()
const agent = ctx.agentLoop.create(SessionId('cordis-e2e-compose'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup(createUserMessage({
content: [{
type: 'text',
text: 'Mount TWO separate temporary Plugins with cordis_mount. First a provider: apply calls '
+ 'ctx.provide(\'shouter\', { shout: (s) => s.toUpperCase() }). Second a consumer with '
+ 'inject ["shouter", "tools"] that registers (via harness.registerTool + harness.defineTool) '
+ 'a tool named shout_text with one required string parameter "text" whose execute returns '
+ 'ctx.shouter.shout(args.text) as a text content block. Then CALL shout_text with "quiet" '
+ 'and report the exact output.',
}], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
// World checks: the service is really in the store, the tool really ran.
expect(ctx.get('shouter')).toBeDefined()
expect(ctx.tools.get('shout_text')).toBeDefined()
const events = [...agent.session.events]
const shoutCalls = events
.filter(event => event.type === 'tool/call')
.filter(event => event.data.name === 'shout_text')
expect(shoutCalls.length).toBeGreaterThan(0)
const shoutResults = events
.filter(event => event.type === 'tool/result')
.filter(event => shoutCalls.some(call => call.data.callId === event.data.message.source.callId))
.flatMap(event => event.data.message.content[0].content.filter(block => block.type === 'text').map(block => block.text))
expect(shoutResults.some(text => text.includes('QUIET'))).toBe(true)
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'Now unmount ONLY the provider temporary Plugin (the one that provided shouter).' }], source: { kind: 'user' } }))
await waitForIdle(ctx, agent)
// The consumer must have been parked by cordis itself: service gone,
// dependent tool unregistered, temporary section naming the missing service.
expect(ctx.get('shouter')).toBeUndefined()
expect(ctx.tools.get('shout_text')).toBeUndefined()
const after = await ctx.tools.execute({
signal: testToolSignal,
callId: CallId('verify-parked'), name: 'cordis_inspect', arguments: { what: 'temporary' },
})
expect(resultText(after)).toContain('waiting for: shouter')
}, 120_000)
})

View File

@@ -1,41 +0,0 @@
import { Context } from 'cordis'
import type { Agent } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
/**
* Shared harness for the cordis-agent e2e suite: the agent spine with the real
* DeepSeek adapter and the real `@deepseek-ai/dsh-tool-cordis` plugin, so a
* live model can mount plugins into the very context the test observes. Lives
* outside the *.e2e.ts pattern so importing it never re-registers another
* file's tests.
*/
const PERSONA = 'You are cordis-agent, a self-referential harness demo. '
+ 'Your cordis_* tools operate on the live cordis runtime you run inside: '
+ 'cordis_inspect to look around, cordis_mount to mount a temporary Plugin, cordis_unmount '
+ 'to unmount one. Follow the tool descriptions exactly and report results briefly.'
export async function cordisHarness(): Promise<Context> {
const ctx = new Context()
await mountAgentLoopTestDependencies(ctx, {
systemPrompt: { persona: PERSONA },
})
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(LlmDeepSeek)
await ctx.plugin(ToolCordis)
return ctx
}
export function waitForIdle(ctx: Context, agent: Agent): Promise<void> {
return new Promise((resolve) => {
const dispose = ctx.on('agent/status', (subject, status) => {
if (subject === agent && status === 'idle') {
dispose()
resolve()
}
})
})
}

View File

@@ -1,23 +0,0 @@
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { LOADER_SMOKE_TEST_TIMEOUT_MS } from '@deepseek-ai/dsh-loader-smoke'
import { runTuiPtySmoke } from '../../tui-agent/tests/pty-harness.ts'
const binScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url))
const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
describe('cordis-agent keyless smoke (real Loader tree in a PTY)', () => {
it('boots the full tool-cordis tree and exits cleanly through the TUI', async () => {
const output = await runTuiPtySmoke({
label: 'cordis-agent',
tempDirPrefix: 'cordis-agent-smoke-',
binScript,
configPath,
tsconfigPath,
env: { DEEPSEEK_API_KEY: 'keyless-smoke-no-call' },
actions: [{ waitFor: 'cordis-agent ready.', send: '/exit\r' }],
})
expect(output).toContain('cordis-agent ready.')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
})

View File

@@ -16,11 +16,6 @@
- id: replay
name: '@deepseek-ai/dsh-llm-replay'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 60000
# The confining filesystem stack: the wide deployment default lives on the
# shared policy home; the seeded parent's read-only override must beat it
# INSIDE the child for the scenario to deny.
@@ -39,6 +34,9 @@
- id: tool-fs
name: '@deepseek-ai/dsh-tool-fs'
# This scenario probes filesystem confinement only, so the bash stack is absent:
# without it the bundle's `toolBash: false` is required, because `tool-bash` would
# otherwise wait forever for a `bash` executor this tree never mounts.
- id: agent
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
@@ -46,6 +44,7 @@
workspaceContext: false
skills:
enabled: false
toolBash: false
toolTasks: false
goals: false

View File

@@ -8,6 +8,9 @@
- id: telemetry-redact-rule
name: './telemetry-redact-rule.ts'
- id: subprocess
name: '@deepseek-ai/dsh-subprocess-local'
- id: bash
name: '@deepseek-ai/dsh-bash-local'

View File

@@ -7,15 +7,16 @@
{"type":"tool/result","seq":5,"time":0,"data":{"turn":1,"step":1,"message":{"id":"interrupted-tool-result-unknown-outcome-call-5","role":"user","source":{"kind":"tool","callId":"unknown-outcome-call"},"content":[{"type":"tool-result","toolCallId":"unknown-outcome-call","isError":true,"content":[{"type":"text","text":"The tool call was interrupted after it was recorded, but no result was durably recorded. Its outcome is unknown. Decide whether to retry from the tool semantics: retry only if the operation is read-only or idempotent; if it may have side effects, first verify external state or ask the user. Do not retry blindly."}]}]},"error":{"name":"ToolOutcomeUnknownError","code":"TOOL_OUTCOME_UNKNOWN"}},"surfaceOp":"append","sourceEventSeqs":[4]}
{"type":"step/end","seq":6,"time":0,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":7,"time":0,"data":{"turn":1,"reason":{"kind":"interrupted"}}}
{"type":"turn/start","seq":8,"time":0,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":9,"time":0,"data":{"content":[{"type":"text","text":"Continue safely from the interrupted operation."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":10,"time":0,"data":{"title":"Perform one side-effecting remote mutati","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":11,"time":0,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":12,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":0,"text":"I will verify the external state before deciding whether to retry the side-effecting operation."}}}
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"I will verify the external state before deciding whether to retry the side-effecting operation."}}}}
{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":17,"time":0,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"I will verify the external state before deciding whether to retry the side-effecting operation."}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"}},"sourceEventSeqs":[13,14,15,16],"surfaceOp":"append"}
{"type":"step/end","seq":18,"time":0,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":19,"time":0,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":8,"time":0,"data":{}}
{"type":"turn/start","seq":9,"time":0,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":10,"time":0,"data":{"content":[{"type":"text","text":"Continue safely from the interrupted operation."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":11,"time":0,"data":{"title":"Perform one side-effecting remote mutati","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":12,"time":0,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":13,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":0,"text":"I will verify the external state before deciding whether to retry the side-effecting operation."}}}
{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"I will verify the external state before deciding whether to retry the side-effecting operation."}}}}
{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":18,"time":0,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"I will verify the external state before deciding whether to retry the side-effecting operation."}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"}},"sourceEventSeqs":[14,15,16,17],"surfaceOp":"append"}
{"type":"step/end","seq":19,"time":0,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":20,"time":0,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -3,26 +3,27 @@
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Tighten this session to read-only."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"sandbox/mode","seq":2,"time":0,"data":{"mode":"read-only"}}
{"type":"turn/end","seq":3,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/start","seq":4,"time":0,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":5,"time":0,"data":{"content":[{"type":"text","text":"Delegate the write probe to a subagent."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":6,"time":0,"data":{"title":"Tighten this session to read-only.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":7,"time":0,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":8,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"delegate-write","name":"subagent","argumentsDelta":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}}
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}}}
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":14,"time":0,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
{"type":"tool/call","seq":15,"time":0,"data":{"turn":2,"step":1,"callId":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}
{"type":"tool/result","seq":16,"time":0,"data":{"turn":2,"step":1,"message":{"source":{"kind":"tool","callId":"delegate-write"},"content":[{"type":"tool-result","toolCallId":"delegate-write","content":[{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"}
{"type":"step/end","seq":17,"time":0,"data":{"turn":2,"step":1}}
{"type":"step/start","seq":18,"time":0,"data":{"turn":2,"step":2}}
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"text-delta","index":0,"text":"The delegated child was denied by the sandbox. PARENT_DONE"}}}
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The delegated child was denied by the sandbox. PARENT_DONE"}}}}
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":24,"time":0,"data":{"turn":2,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"The delegated child was denied by the sandbox. PARENT_DONE"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
{"type":"step/end","seq":25,"time":0,"data":{"turn":2,"step":2}}
{"type":"turn/end","seq":26,"time":0,"data":{"turn":2,"reason":{"kind":"completed"}}}
{"type":"session/end-seed","seq":4,"time":0,"data":{}}
{"type":"turn/start","seq":5,"time":0,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":6,"time":0,"data":{"content":[{"type":"text","text":"Delegate the write probe to a subagent."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":7,"time":0,"data":{"title":"Tighten this session to read-only.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":8,"time":0,"data":{"turn":2,"step":1}}
{"type":"request/header","seq":9,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"delegate-write","name":"subagent","argumentsDelta":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}}
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":15,"time":0,"data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
{"type":"tool/call","seq":16,"time":0,"data":{"turn":2,"step":1,"callId":"delegate-write","name":"subagent","arguments":"{\"description\": \"Delegated write probe\", \"prompt\": \"Use the write tool exactly once with file_path set to exactly the relative path inherited.txt and content escaped. If the write is denied, reply with the single word CHILD_DENIED and the denial marker line; do not retry and do not request escalation. If it succeeds, reply CHILD_WROTE.\"}"}}
{"type":"tool/result","seq":17,"time":0,"data":{"turn":2,"step":1,"message":{"source":{"kind":"tool","callId":"delegate-write"},"content":[{"type":"tool-result","toolCallId":"delegate-write","content":[{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[16],"surfaceOp":"append"}
{"type":"step/end","seq":18,"time":0,"data":{"turn":2,"step":1}}
{"type":"step/start","seq":19,"time":0,"data":{"turn":2,"step":2}}
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"text-delta","index":0,"text":"The delegated child was denied by the sandbox. PARENT_DONE"}}}
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The delegated child was denied by the sandbox. PARENT_DONE"}}}}
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":24,"time":0,"data":{"turn":2,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":25,"time":0,"data":{"turn":2,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"The delegated child was denied by the sandbox. PARENT_DONE"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[20,21,22,23,24],"surfaceOp":"append"}
{"type":"step/end","seq":26,"time":0,"data":{"turn":2,"step":2}}
{"type":"turn/end","seq":27,"time":0,"data":{"turn":2,"reason":{"kind":"completed"}}}

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 examples/jsonrpc-agent/README.md
README.md: 6ee4e9d824315bde76b7a534679f018df9a6d3e8
README.zh.md: 43290fc3659750724679a370be5b33ffdca2a5cb
README.md: 5e7b7d79415a4af0b0d16c61b0a4590dce57e545
README.zh.md: 00450838e7d10ae9e95624b5f89992dd747d5440

View File

@@ -25,3 +25,16 @@ The surrounding runtime also loads JSONL session persistence and automatic conte
| `DSH_SYSTEM_PROMPT` | Deployment-provided coding persona |
Pass the config path through the Python SDK's `cordis` option or `DSH_CORDIS_CONFIG`. The bundled executable already carries every plugin named by this file; the target machine does not need Node.js.
## Persistent tools variant
[`persistent-tools.cordis.yml`](persistent-tools.cordis.yml) is a minimal runnable variant whose model-facing surface is exactly:
- owner-scoped persistent `bash`
- `str_replace_editor` with `view`, `create`, `str_replace`, and `insert`
It composes the real local PTY, filesystem intent policy, and session sandbox policy. The keyless SDK snapshot drives the shipped JSON-RPC runtime through both tools, proves that shell cwd/environment survive across calls, and pins the notification stream, turn result, and persisted JSONL:
```bash
pnpm exec vitest run --config vitest.snapshot.config.ts -t persistent-tools
```

View File

@@ -25,3 +25,16 @@
| `DSH_SYSTEM_PROMPT` | 由部署提供的编码人格 |
通过 Python SDK 的 `cordis` 选项或 `DSH_CORDIS_CONFIG` 传入配置路径。内置可执行文件已携带此文件中指定的每个插件;目标机器无需 Node.js。
## 持久工具变体
[`persistent-tools.cordis.yml`](persistent-tools.cordis.yml) 是一个最小可运行变体,面向模型的能力严格只有:
- agent 独占、状态持久的 `bash`
- 提供 `view``create``str_replace``insert``str_replace_editor`
它组合真实本地 PTY、文件系统 intent 策略与 session 沙箱策略。无密钥 SDK 快照会通过正式 JSON-RPC runtime 驱动这两个工具,验证 shell 的 cwd 与环境变量能跨调用保留,并锁定通知流、轮次结果与已持久化的 JSONL
```bash
pnpm exec vitest run --config vitest.snapshot.config.ts -t persistent-tools
```

View File

@@ -0,0 +1,58 @@
# Minimal unattended composition for the persistent Bash and string-replace
# editor. It is runnable through the JSON-RPC example runtime and intentionally
# keeps the model-facing surface to exactly these two tools.
- id: jsonrpc
name: '@deepseek-ai/dsh-jsonrpc'
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
baseURL: !!js process.env.DEEPSEEK_BASE_URL
- id: sandbox
name: '@deepseek-ai/dsh-sandbox-local'
- id: sandbox-policy
name: '@deepseek-ai/dsh-sandbox-policy'
config:
mode: danger-full-access
workspaceRoot: !!js process.env.DSH_CWD ?? process.cwd()
- id: pty
name: '@deepseek-ai/dsh-pty'
- id: pty-local
name: '@deepseek-ai/dsh-pty-local'
- id: fs-sandbox
name: '@deepseek-ai/dsh-fs-sandbox'
config:
cwd: !!js process.env.DSH_CWD ?? process.cwd()
- id: fs-policy
name: '@deepseek-ai/dsh-fs-policy'
- id: agent-spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
includeHarnessIdentity: false
persona: 'You are a helpful software engineer assistant.'
workspaceContext: false
skills:
enabled: false
toolBash: false
toolTasks: false
- id: persistent-bash
name: '@deepseek-ai/dsh-tool-bash-persistent'
- id: str-replace-editor
name: '@deepseek-ai/dsh-tool-str-replace-editor'
- id: sessions
name: '@deepseek-ai/dsh-session-persistence-jsonl'
config:
root: !!js process.env.DSH_SESSION_ROOT ?? './.sessions'
compression: none

View File

@@ -0,0 +1,19 @@
# Keyless replay keeps the persistent-tool composition intact and replaces
# only its live DeepSeek adapter with the fixture-backed provider.
- id: base
name: '@cordisjs/plugin-include'
config:
path: ./persistent-tools.cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- insert:
- id: llm-replay
name: '@deepseek-ai/dsh-llm-replay'
config:
providers:
- id: deepseek
name: DeepSeek
models:
- id: deepseek-v4-flash

View File

@@ -11,7 +11,7 @@
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { delimiter, join } from 'node:path'
import { basename, delimiter, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import {
@@ -31,6 +31,8 @@ const testsDir = dirOf(import.meta.url)
const snapshotsDir = join(testsDir, 'snapshots')
const liveConfig = join(testsDir, '..', 'cordis.yml')
const replayConfig = join(testsDir, '..', 'cordis.snapshot.yml')
const persistentToolsLiveConfig = join(testsDir, '..', 'persistent-tools.cordis.yml')
const persistentToolsReplayConfig = join(testsDir, '..', 'persistent-tools.snapshot.cordis.yml')
const runtimeBin = fileURLToPath(new URL('../../../packages/examples/jsonrpc-demo/src/bin.ts', import.meta.url))
const repoTsconfig = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
@@ -51,6 +53,12 @@ interface SdkScenario {
sessionId: string
/** How many child sessions the turn persists (subagent scenarios). */
children: number
/** Optional scenario-specific live and replay compositions. */
configs?: { live: string; replay: string }
/** Cwd-relative files whose final contents are part of the scenario contract. */
expectedFiles?: Readonly<Record<string, string>>
/** Assembled model-facing tool names and required argument keys. */
expectedTools?: Readonly<Record<string, readonly string[]>>
}
const SCENARIOS: SdkScenario[] = [
@@ -72,6 +80,15 @@ const SCENARIOS: SdkScenario[] = [
sessionId: 'sdk-snapshot-subagent',
children: 1,
},
{
name: 'persistent-tools',
prompt: 'Prove that bash state persists. Then create {{cwd}}/note.txt with a tab-indented line, view it, replace that literal tab-indented line, and make the persistent shell exit with code 9.',
sessionId: 'persistent-tools-snapshot',
children: 0,
configs: { live: persistentToolsLiveConfig, replay: persistentToolsReplayConfig },
expectedFiles: { 'note.txt': 'target:\n\tnew\n' },
expectedTools: { bash: ['command'], str_replace_editor: ['command', 'path'] },
},
]
interface PersistedLog {
@@ -80,6 +97,10 @@ interface PersistedLog {
readonly header: Record<string, unknown>
}
interface MissingFile {
readonly missing: true
}
async function jsonlFiles(dir: string): Promise<string[]> {
const entries = await readdir(dir, { recursive: true })
return entries.filter(entry => entry.endsWith('.jsonl')).map(entry => join(dir, entry)).sort()
@@ -94,6 +115,20 @@ async function persistedLogs(sessionsRoot: string): Promise<PersistedLog[]> {
}))
}
interface LoggedRequestHeader {
type?: string
data?: { header?: { tools?: Array<{ name: string; parameters: { required?: string[] } }> } }
}
function assembledToolRequirements(log: PersistedLog): Record<string, string[]> {
const event = log.content.trimEnd().split('\n')
.map(line => JSON.parse(line) as LoggedRequestHeader)
.find(candidate => candidate.type === 'request/header')
const tools = event?.data?.header?.tools
if (tools === undefined) throw new Error('session log has no request/header tools')
return Object.fromEntries(tools.map(tool => [tool.name, tool.parameters.required ?? []]))
}
function contextOf(logs: readonly { content: string; header: Record<string, unknown> }[], cwd: string): NormalizeContext {
return {
sessionIds: logs.flatMap(log => typeof log.header.id === 'string' ? [log.header.id] : []),
@@ -109,6 +144,25 @@ function contextOfContents(contents: readonly string[]): NormalizeContext {
}
}
async function hydrateReplayFixtures(scenario: SdkScenario, cwd: string): Promise<string[]> {
const root = join(cwd, '.replay-fixtures')
await mkdir(root, { recursive: true })
return Promise.all(fixtureFiles(scenario).map(async (source) => {
const destination = join(root, basename(source))
await writeFile(destination, (await readFile(source, 'utf8')).replaceAll('{{cwd}}', cwd))
return destination
}))
}
async function readExpectedFile(path: string): Promise<string | MissingFile> {
try {
return await readFile(path, 'utf8')
} catch (error: unknown) {
if (error instanceof Error && (error as NodeJS.ErrnoException).code === 'ENOENT') return { missing: true }
throw error
}
}
/**
* Normalize the SDK-visible notification stream: embedded `session.event`
* envelopes get the session-log treatment (times zeroed, headers tokenized),
@@ -147,30 +201,30 @@ async function runScenario(scenario: SdkScenario): Promise<{
result: TurnResult
notifications: HarnessNotification[]
logs: PersistedLog[]
observedFiles: Record<string, string | MissingFile>
cwd: string
}> {
const cwd = await mkdtemp(join(tmpdir(), `sdk-snapshot-${scenario.name}-`))
const sessionsRoot = join(cwd, '.sessions')
const scenarioDir = join(snapshotsDir, scenario.name)
const replayFixtures = recording ? [] : await hydrateReplayFixtures(scenario, cwd)
const launch = resolveExampleLaunch({
srcBin: runtimeBin,
configArgs: [],
tsconfigPath: repoTsconfig,
})
const childFixtures = Array.from(
{ length: scenario.children },
(_, index) => join(scenarioDir, `session.${index + 1}.jsonl`),
)
const [parentFixture, ...childFixtures] = replayFixtures
const env: Record<string, string> = {
...Object.fromEntries(Object.entries(process.env).filter(([, value]) => value !== undefined)) as Record<string, string>,
...Object.fromEntries(Object.entries(launch.env).filter(([, value]) => value !== undefined)) as Record<string, string>,
DSH_CORDIS_CONFIG: recording ? liveConfig : replayConfig,
DSH_CORDIS_CONFIG: recording
? scenario.configs?.live ?? liveConfig
: scenario.configs?.replay ?? replayConfig,
DSH_SESSION_ROOT: sessionsRoot,
DSH_CWD: cwd,
DSH_SNAPSHOT: mode,
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
...recording ? {} : {
DSH_SNAPSHOT_FILE: join(scenarioDir, 'session.jsonl'),
...parentFixture === undefined ? {} : {
DSH_SNAPSHOT_FILE: parentFixture,
...childFixtures.length > 0 ? { DSH_SNAPSHOT_CHILD_FILES: childFixtures.join(delimiter) } : {},
},
}
@@ -189,13 +243,19 @@ async function runScenario(scenario: SdkScenario): Promise<{
})
try {
const notifications: HarnessNotification[] = []
const result = await harness.run(scenario.prompt, {
const result = await harness.run(scenario.prompt.replaceAll('{{cwd}}', cwd), {
sessionId: scenario.sessionId,
onNotification: (notification) => { notifications.push(notification) },
})
await harness.close()
const logs = await persistedLogs(sessionsRoot)
return { result, notifications, logs, cwd }
const observedFiles = Object.fromEntries(await Promise.all(
Object.keys(scenario.expectedFiles ?? {}).map(async (path): Promise<[string, string | MissingFile]> => [
path,
await readExpectedFile(join(cwd, path)),
]),
))
return { result, notifications, logs, observedFiles, cwd }
} finally {
await harness.close()
await rm(cwd, { recursive: true, force: true })
@@ -227,7 +287,7 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => {
const notificationsExpectedPath = join(scenarioDir, 'notifications.expected.jsonl')
const resultExpectedPath = join(scenarioDir, 'result.expected.json')
const { result, notifications, logs, cwd } = await runScenario(scenario)
const { result, notifications, logs, observedFiles, cwd } = await runScenario(scenario)
const ordered = orderLogs(logs, scenario)
const actualContext = contextOf(ordered, cwd)
@@ -293,6 +353,12 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => {
// Wire-shape invariants that must hold in every mode.
expect(result.status).toBe('ok')
expect(notifications.at(-1)?.method).toBe('session.finished')
expect(observedFiles).toEqual(scenario.expectedFiles ?? {})
if (scenario.expectedTools !== undefined) {
const parent = ordered[0]
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
expect(assembledToolRequirements(parent)).toEqual(scenario.expectedTools)
}
if (scenario.children > 0) {
expect(notifications.some(n => n.method === 'subagent.started')).toBe(true)
expect(notifications.some(n => n.method === 'subagent.finished')).toBe(true)

View File

@@ -0,0 +1,74 @@
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":0,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Prove that bash state persists. Then create {{cwd}}/note.txt with a tab-indented line, view it, replace that literal tab-indented line, and make the persistent shell exit with code 9."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"session/title","seq":2,"time":0,"data":{"title":"Prove that bash state persists.","messageSeqs":[1],"source":{"kind":"fallback"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/header","seq":4,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":5,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":6,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-1","name":"bash","argumentsDelta":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":10,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[5,6,7,8,9],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":11,"time":0,"data":{"turn":1,"step":1,"callId":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":12,"time":0,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"bash-1"},"content":[{"type":"tool-result","toolCallId":"bash-1","content":[{"type":"text","text":"COUNT=1 CWD=/tmp"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[11],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":13,"time":0,"data":{"turn":1,"step":1}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":14,"time":0,"data":{"turn":1,"step":2}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":16,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-2","name":"bash","argumentsDelta":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":17,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":18,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":20,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[15,16,17,18,19],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":21,"time":0,"data":{"turn":1,"step":2,"callId":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":22,"time":0,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"bash-2"},"content":[{"type":"tool-result","toolCallId":"bash-2","content":[{"type":"text","text":"COUNT=2 CWD=/tmp"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[21],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":23,"time":0,"data":{"turn":1,"step":2}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":24,"time":0,"data":{"turn":1,"step":3}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":25,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-create","name":"str_replace_editor","argumentsDelta":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":27,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":28,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":29,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":30,"time":0,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[25,26,27,28,29],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":31,"time":0,"data":{"turn":1,"step":3,"callId":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":32,"time":0,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"editor-create"},"content":[{"type":"tool-result","toolCallId":"editor-create","content":[{"type":"text","text":"New file created successfully at: {{cwd}}/note.txt"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[31],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":33,"time":0,"data":{"turn":1,"step":3}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":34,"time":0,"data":{"turn":1,"step":4}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":35,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":36,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-view","name":"str_replace_editor","argumentsDelta":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":37,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":38,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":39,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":40,"time":0,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[35,36,37,38,39],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":41,"time":0,"data":{"turn":1,"step":4,"callId":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":42,"time":0,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"editor-view"},"content":[{"type":"tool-result","toolCallId":"editor-view","content":[{"type":"text","text":"Here's the content of {{cwd}}/note.txt with line numbers (which has a total of 3 lines):\n 1 target:\n 2 \told\n 3 \n"}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[41],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":43,"time":0,"data":{"turn":1,"step":4}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":44,"time":0,"data":{"turn":1,"step":5}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":45,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":46,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-replace","name":"str_replace_editor","argumentsDelta":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":47,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":48,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":49,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":50,"time":0,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[45,46,47,48,49],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":51,"time":0,"data":{"turn":1,"step":5,"callId":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":52,"time":0,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"editor-replace"},"content":[{"type":"tool-result","toolCallId":"editor-replace","content":[{"type":"text","text":"The file {{cwd}}/note.txt has been edited successfully."}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[51],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":53,"time":0,"data":{"turn":1,"step":5}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":54,"time":0,"data":{"turn":1,"step":6}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":55,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":56,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-exit","name":"bash","argumentsDelta":"{\"command\":\"exit 9\"}"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":57,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":58,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":59,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":60,"time":0,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[55,56,57,58,59],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":61,"time":0,"data":{"turn":1,"step":6,"callId":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":62,"time":0,"data":{"turn":1,"step":6,"message":{"source":{"kind":"tool","callId":"bash-exit"},"content":[{"type":"tool-result","toolCallId":"bash-exit","content":[{"type":"text","text":"exit\n[shell exited: code 9]\nThe persistent bash shell was reset; the next bash call starts from the workspace with a fresh current directory and environment."}],"isError":false}],"role":"user","id":"{{sessionId}}"}},"sourceEventSeqs":[61],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":63,"time":0,"data":{"turn":1,"step":6}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":64,"time":0,"data":{"turn":1,"step":7}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":65,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":66,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"text-delta","index":0,"text":"PERSISTENT_TOOLS_OK"}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":67,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PERSISTENT_TOOLS_OK"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":68,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":69,"time":0,"data":{"turn":1,"step":7,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":70,"time":0,"data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"text","text":"PERSISTENT_TOOLS_OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[65,66,67,68,69],"surfaceOp":"append"}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":71,"time":0,"data":{"turn":1,"step":7}}}}
{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":72,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}}}
{"method":"session.finished","params":{"sessionId":"{{sessionId}}","status":"ok","reason":{"kind":"completed"}}}

View File

@@ -0,0 +1 @@
{"status":"ok","reason":{"kind":"completed"},"finalResponse":"PERSISTENT_TOOLS_OK"}

View File

@@ -0,0 +1,74 @@
{"type":"session","version":0,"id":"persistent-tools-snapshot","createdAt":1785331618309,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1785331618311,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1785331618311,"data":{"content":[{"type":"text","text":"Prove that bash state persists. Then create {{cwd}}/note.txt with a tab-indented line, view it, replace that literal tab-indented line, and make the persistent shell exit with code 9."}],"source":{"kind":"user"},"role":"user","id":"d0534fe8-a74b-4fcf-913f-d78e36f486bb"},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785331618312,"data":{"title":"Prove that bash state persists.","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785331618312,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785331618313,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":5,"time":1785331618325,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":6,"time":1785331618325,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-1","name":"bash","argumentsDelta":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}
{"type":"assistant/chunk","seq":7,"time":1785331618326,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}
{"type":"assistant/chunk","seq":8,"time":1785331618326,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":9,"time":1785331618326,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":10,"time":1785331618327,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"68f0912b-5e3a-417e-a324-00871206cdf7"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[5,6,7,8,9],"surfaceOp":"append"}
{"type":"tool/call","seq":11,"time":1785331618327,"data":{"turn":1,"step":1,"callId":"bash-1","name":"bash","arguments":"{\"command\":\"cd /tmp && export DSH_EXAMPLE_COUNT=1 && printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}
{"type":"tool/result","seq":12,"time":1785331618649,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"bash-1"},"content":[{"type":"tool-result","toolCallId":"bash-1","content":[{"type":"text","text":"COUNT=1 CWD=/tmp"}],"isError":false}],"role":"user","id":"a83a469c-0321-4f8b-a40e-913c1b433b9d"}},"sourceEventSeqs":[11],"surfaceOp":"append"}
{"type":"step/end","seq":13,"time":1785331618649,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":14,"time":1785331618649,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":15,"time":1785331618652,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":16,"time":1785331618652,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-2","name":"bash","argumentsDelta":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}
{"type":"assistant/chunk","seq":17,"time":1785331618652,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}}}
{"type":"assistant/chunk","seq":18,"time":1785331618652,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":19,"time":1785331618652,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":20,"time":1785331618652,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"425c837c-b7e5-48ef-bc97-282bf5a10221"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[15,16,17,18,19],"surfaceOp":"append"}
{"type":"tool/call","seq":21,"time":1785331618652,"data":{"turn":1,"step":2,"callId":"bash-2","name":"bash","arguments":"{\"command\":\"DSH_EXAMPLE_COUNT=$((DSH_EXAMPLE_COUNT + 1)); printf \\\"COUNT=%s CWD=%s\\\\n\\\" \\\"$DSH_EXAMPLE_COUNT\\\" \\\"$PWD\\\"\"}"}}
{"type":"tool/result","seq":22,"time":1785331618759,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"bash-2"},"content":[{"type":"tool-result","toolCallId":"bash-2","content":[{"type":"text","text":"COUNT=2 CWD=/tmp"}],"isError":false}],"role":"user","id":"1d3fcea8-51d9-47a1-8e8e-283c7b9cf53a"}},"sourceEventSeqs":[21],"surfaceOp":"append"}
{"type":"step/end","seq":23,"time":1785331618759,"data":{"turn":1,"step":2}}
{"type":"step/start","seq":24,"time":1785331618759,"data":{"turn":1,"step":3}}
{"type":"assistant/chunk","seq":25,"time":1785331618762,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":26,"time":1785331618762,"data":{"turn":1,"step":3,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-create","name":"str_replace_editor","argumentsDelta":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}}
{"type":"assistant/chunk","seq":27,"time":1785331618762,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}}}
{"type":"assistant/chunk","seq":28,"time":1785331618762,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":29,"time":1785331618762,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":30,"time":1785331618762,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"6407aec3-f75c-427a-8783-a61bd99327bb"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[25,26,27,28,29],"surfaceOp":"append"}
{"type":"tool/call","seq":31,"time":1785331618762,"data":{"turn":1,"step":3,"callId":"editor-create","name":"str_replace_editor","arguments":"{\"command\":\"create\",\"path\":\"{{cwd}}/note.txt\",\"file_text\":\"target:\\n\\told\\n\"}"}}
{"type":"tool/result","seq":32,"time":1785331618782,"data":{"turn":1,"step":3,"message":{"source":{"kind":"tool","callId":"editor-create"},"content":[{"type":"tool-result","toolCallId":"editor-create","content":[{"type":"text","text":"New file created successfully at: {{cwd}}/note.txt"}],"isError":false}],"role":"user","id":"121833da-381d-492e-9d6c-82eaa9694ef1"}},"sourceEventSeqs":[31],"surfaceOp":"append"}
{"type":"step/end","seq":33,"time":1785331618782,"data":{"turn":1,"step":3}}
{"type":"step/start","seq":34,"time":1785331618782,"data":{"turn":1,"step":4}}
{"type":"assistant/chunk","seq":35,"time":1785331618784,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":36,"time":1785331618784,"data":{"turn":1,"step":4,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-view","name":"str_replace_editor","argumentsDelta":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}}
{"type":"assistant/chunk","seq":37,"time":1785331618784,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}}}
{"type":"assistant/chunk","seq":38,"time":1785331618784,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":39,"time":1785331618784,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":40,"time":1785331618784,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"1cf1d34c-faee-464d-bdd7-413ba7233e23"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[35,36,37,38,39],"surfaceOp":"append"}
{"type":"tool/call","seq":41,"time":1785331618784,"data":{"turn":1,"step":4,"callId":"editor-view","name":"str_replace_editor","arguments":"{\"command\":\"view\",\"path\":\"{{cwd}}/note.txt\"}"}}
{"type":"tool/result","seq":42,"time":1785331618799,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"editor-view"},"content":[{"type":"tool-result","toolCallId":"editor-view","content":[{"type":"text","text":"Here's the content of {{cwd}}/note.txt with line numbers (which has a total of 3 lines):\n 1 target:\n 2 \told\n 3 \n"}],"isError":false}],"role":"user","id":"c88746c2-208d-46aa-8c3d-79ccc88c7f6d"}},"sourceEventSeqs":[41],"surfaceOp":"append"}
{"type":"step/end","seq":43,"time":1785331618799,"data":{"turn":1,"step":4}}
{"type":"step/start","seq":44,"time":1785331618799,"data":{"turn":1,"step":5}}
{"type":"assistant/chunk","seq":45,"time":1785331618801,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":46,"time":1785331618801,"data":{"turn":1,"step":5,"chunk":{"type":"tool-call-delta","index":0,"id":"editor-replace","name":"str_replace_editor","argumentsDelta":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}}
{"type":"assistant/chunk","seq":47,"time":1785331618801,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}}}
{"type":"assistant/chunk","seq":48,"time":1785331618801,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":49,"time":1785331618801,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":50,"time":1785331618802,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"b8832049-1795-4127-b0e0-e31528da0e99"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[45,46,47,48,49],"surfaceOp":"append"}
{"type":"tool/call","seq":51,"time":1785331618802,"data":{"turn":1,"step":5,"callId":"editor-replace","name":"str_replace_editor","arguments":"{\"command\":\"str_replace\",\"path\":\"{{cwd}}/note.txt\",\"old_str\":\"\\told\",\"new_str\":\"\\tnew\"}"}}
{"type":"tool/result","seq":52,"time":1785331618803,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"editor-replace"},"content":[{"type":"tool-result","toolCallId":"editor-replace","content":[{"type":"text","text":"The file {{cwd}}/note.txt has been edited successfully."}],"isError":false}],"role":"user","id":"ee874ae7-c4d9-4075-9b40-45e643a4b159"}},"sourceEventSeqs":[51],"surfaceOp":"append"}
{"type":"step/end","seq":53,"time":1785331618803,"data":{"turn":1,"step":5}}
{"type":"step/start","seq":54,"time":1785331618803,"data":{"turn":1,"step":6}}
{"type":"assistant/chunk","seq":55,"time":1785331618804,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":56,"time":1785331618804,"data":{"turn":1,"step":6,"chunk":{"type":"tool-call-delta","index":0,"id":"bash-exit","name":"bash","argumentsDelta":"{\"command\":\"exit 9\"}"}}}
{"type":"assistant/chunk","seq":57,"time":1785331618804,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}}}}
{"type":"assistant/chunk","seq":58,"time":1785331618804,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":59,"time":1785331618804,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":60,"time":1785331618805,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"tool-call","id":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"8e39f4fe-5538-46be-b24a-84296d638c44"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[55,56,57,58,59],"surfaceOp":"append"}
{"type":"tool/call","seq":61,"time":1785331618805,"data":{"turn":1,"step":6,"callId":"bash-exit","name":"bash","arguments":"{\"command\":\"exit 9\"}"}}
{"type":"tool/result","seq":62,"time":1785331618806,"data":{"turn":1,"step":6,"message":{"source":{"kind":"tool","callId":"bash-exit"},"content":[{"type":"tool-result","toolCallId":"bash-exit","content":[{"type":"text","text":"exit\n[shell exited: code 9]\nThe persistent bash shell was reset; the next bash call starts from the workspace with a fresh current directory and environment."}],"isError":false}],"role":"user","id":"cb4bf07d-474f-46de-a945-94666c849a5f"}},"sourceEventSeqs":[61],"surfaceOp":"append"}
{"type":"step/end","seq":63,"time":1785331618806,"data":{"turn":1,"step":6}}
{"type":"step/start","seq":64,"time":1785331618806,"data":{"turn":1,"step":7}}
{"type":"assistant/chunk","seq":65,"time":1785331618807,"data":{"turn":1,"step":7,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":66,"time":1785331618807,"data":{"turn":1,"step":7,"chunk":{"type":"text-delta","index":0,"text":"PERSISTENT_TOOLS_OK"}}}
{"type":"assistant/chunk","seq":67,"time":1785331618807,"data":{"turn":1,"step":7,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"PERSISTENT_TOOLS_OK"}}}}
{"type":"assistant/chunk","seq":68,"time":1785331618807,"data":{"turn":1,"step":7,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":69,"time":1785331618807,"data":{"turn":1,"step":7,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":70,"time":1785331618808,"data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"text","text":"PERSISTENT_TOOLS_OK"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"42e7f4c0-f936-4616-8af3-4f486f27fbb5"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[65,66,67,68,69],"surfaceOp":"append"}
{"type":"step/end","seq":71,"time":1785331618808,"data":{"turn":1,"step":7}}
{"type":"turn/end","seq":72,"time":1785331618808,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -7,13 +7,19 @@
"dependencies": {
"@cordisjs/plugin-hmr": "workspace:*",
"@cordisjs/plugin-include": "workspace:*",
"@cordisjs/plugin-timer": "workspace:*",
"@deepseek-ai/dsh-acp-demo": "workspace:*",
"@deepseek-ai/dsh-agent": "workspace:*",
"@deepseek-ai/dsh-agent-loop": "workspace:*",
"@deepseek-ai/dsh-agent-spine-demo": "workspace:*",
"@deepseek-ai/dsh-app-boot": "workspace:*",
"@deepseek-ai/dsh-bash": "workspace:*",
"@deepseek-ai/dsh-bash-local": "workspace:*",
"@deepseek-ai/dsh-bash-sandbox": "workspace:*",
"@deepseek-ai/dsh-cli-demo": "workspace:*",
"@deepseek-ai/dsh-code-runtime-worker": "workspace:*",
"@deepseek-ai/dsh-command-goal": "workspace:*",
"@deepseek-ai/dsh-commands": "workspace:*",
"@deepseek-ai/dsh-compact-basic": "workspace:*",
"@deepseek-ai/dsh-compact-tool-result-prune": "workspace:*",
"@deepseek-ai/dsh-fs-local": "workspace:*",
@@ -23,6 +29,7 @@
"@deepseek-ai/dsh-goal-session": "workspace:*",
"@deepseek-ai/dsh-hooks-claude": "workspace:*",
"@deepseek-ai/dsh-hooks-codex": "workspace:*",
"@deepseek-ai/dsh-invariants": "workspace:*",
"@deepseek-ai/dsh-jsonrpc": "workspace:*",
"@deepseek-ai/dsh-llm": "workspace:*",
"@deepseek-ai/dsh-llm-deepseek": "workspace:*",
@@ -32,30 +39,39 @@
"@deepseek-ai/dsh-lsp-local": "workspace:*",
"@deepseek-ai/dsh-permission": "workspace:*",
"@deepseek-ai/dsh-plan-mode": "workspace:*",
"@deepseek-ai/dsh-subprocess-local": "workspace:*",
"@deepseek-ai/dsh-pty": "workspace:*",
"@deepseek-ai/dsh-pty-local": "workspace:*",
"@deepseek-ai/dsh-repeat-tool-guard": "workspace:*",
"@deepseek-ai/dsh-sandbox-local": "workspace:*",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:*",
"@deepseek-ai/dsh-session": "workspace:*",
"@deepseek-ai/dsh-session-checkpoint-policy": "workspace:*",
"@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*",
"@deepseek-ai/dsh-session-query": "workspace:*",
"@deepseek-ai/dsh-session-query-sqlite": "workspace:*",
"@deepseek-ai/dsh-session-reference": "workspace:*",
"@deepseek-ai/dsh-session-telemetry-otel": "workspace:*",
"@deepseek-ai/dsh-session-title": "workspace:*",
"@deepseek-ai/dsh-session-title-first-message-llm": "workspace:*",
"@deepseek-ai/dsh-skill": "workspace:*",
"@deepseek-ai/dsh-skill-local": "workspace:*",
"@deepseek-ai/dsh-spill-local": "workspace:*",
"@deepseek-ai/dsh-spill-policy": "workspace:*",
"@deepseek-ai/dsh-subagent": "workspace:*",
"@deepseek-ai/dsh-subagent-acp": "workspace:*",
"@deepseek-ai/dsh-subagent-fork": "workspace:*",
"@deepseek-ai/dsh-subagent-dsh-sdk": "workspace:*",
"@deepseek-ai/dsh-subagent-fork": "workspace:*",
"@deepseek-ai/dsh-subagent-spawn": "workspace:*",
"@deepseek-ai/dsh-subprocess-local": "workspace:*",
"@deepseek-ai/dsh-system-prompt": "workspace:*",
"@deepseek-ai/dsh-tasks-local": "workspace:*",
"@deepseek-ai/dsh-time-context": "workspace:*",
"@deepseek-ai/dsh-timeout-policy": "workspace:*",
"@deepseek-ai/dsh-token-meter": "workspace:*",
"@deepseek-ai/dsh-tool-ask-user": "workspace:*",
"@deepseek-ai/dsh-tool-bash": "workspace:*",
"@deepseek-ai/dsh-tool-bash-persistent": "workspace:*",
"@deepseek-ai/dsh-tool-cordis": "workspace:*",
"@deepseek-ai/dsh-tool-fs": "workspace:*",
"@deepseek-ai/dsh-tool-fs-search": "workspace:*",
@@ -64,18 +80,20 @@
"@deepseek-ai/dsh-tool-pty": "workspace:*",
"@deepseek-ai/dsh-tool-ralph": "workspace:*",
"@deepseek-ai/dsh-tool-session-query": "workspace:*",
"@deepseek-ai/dsh-tool-skill": "workspace:*",
"@deepseek-ai/dsh-tool-str-replace-editor": "workspace:*",
"@deepseek-ai/dsh-tool-subagent": "workspace:*",
"@deepseek-ai/dsh-tool-tasks": "workspace:*",
"@deepseek-ai/dsh-tool-todo": "workspace:*",
"@deepseek-ai/dsh-tool-web": "workspace:*",
"@deepseek-ai/dsh-tool-workflow": "workspace:*",
"@deepseek-ai/dsh-tools": "workspace:*",
"@deepseek-ai/dsh-tui-demo": "workspace:*",
"@deepseek-ai/dsh-tui": "workspace:*",
"@deepseek-ai/dsh-user-approval": "workspace:*",
"@deepseek-ai/dsh-user-interaction": "workspace:*",
"@deepseek-ai/dsh-web": "workspace:*",
"@deepseek-ai/dsh-web-fetch-local": "workspace:*",
"@deepseek-ai/dsh-workflow-workerthread": "workspace:*"
},
"devDependencies": {
"node-pty": "1.1.0"
"@deepseek-ai/dsh-workflow-workerthread": "workspace:*",
"@deepseek-ai/dsh-workspace-context": "workspace:*"
}
}

View File

@@ -1,6 +0,0 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# 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 examples/tui-agent/README.md
README.md: ea8695d37ea247a38644392a4572c1ea9855fd44
README.zh.md: c6acd39d8713816d870c00fa8597754d0d09880a

View File

@@ -1,80 +0,0 @@
# tui-agent
English | [中文](README.zh.md)
The full-screen interactive coding agent: DeepSeek V4, local bash and filesystem tools, compaction, subagents, workflows and fresh-agent Ralph iteration, plan mode (`/plan` enters and `exit_plan_mode` reviews the exit), timeout/spill policy, and JSONL persistence through [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo), loaded from `cordis.yml`. The sibling [`headless-agent`](../headless-agent/README.md) runs the same capability class as a one-shot pipe-friendly task, and [`acp-agent`](../acp-agent/README.md) serves it over JSON-RPC.
## Run it
```sh
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run demo:tui
```
Both the demo script and the installable `dsh` CLI ([`apps/cli`](../../apps/cli/README.md)) boot this example's `cordis.yml` as the shipped default config; `dsh` additionally applies the personal overlay from `~/.dsh` and uses the invoking directory as the workspace.
Type a coding task. The agent works through the `read`/`write`/`edit` filesystem tools for ordinary file operations and `bash` (+ the generic `task_output` / `task_list` / `task_kill` for background tasks) for shell commands, searches, and test runs, each in a fresh `bash -c` (the system prompt tells the model to pass `workdir` instead of `cd`). Both the fs tools and bash resolve relative paths against the session workspace. It can also delegate with `subagent`/`subagent_fork`.
The `todo_write` task tracker is opt-in and not in the shipped config: add `@deepseek-ai/dsh-tool-todo` to `cordis.yml` (or a personal-config overlay under `~/.dsh`) to expose it. Once loaded, the model records a whole-list plan to the session log and the TUI renders it.
The TUI renders Markdown history, reasoning, tool-owned terminal/diff/generic cards, token totals, and — when `todo_write` is loaded — the latest plan. Long tool bodies keep a head/tail preview; Ctrl+O expands or collapses every card. Enter submits or steers while the agent runs, Ctrl+R toggles reasoning, Escape cancels, and `/help` lists commands. `/plan` selects plan mode for the next step; `/plan <message>` also submits the message into that step, while `/plan off` selects the default mode without model input. `/status` expands the current session's identity, activity counts, exact token/cache buckets, context use, and timestamps without interrupting a running turn. `/model` opens a keyboard selector for the current provider catalog; use Up/Down to focus a model, Shift+Tab to cycle its advertised reasoning efforts, and Enter to select, or use `/model <model>` and `/model <provider>/<model>` for direct selection. `ask_user_question` opens a wide bottom-left keyboard panel with batch progress and numbered options.
### Resuming a prior session
Each run starts a fresh session by default (its event log lands under `./.sessions/`). To **continue** a previous conversation, pass its id to the installed `dsh` CLI — the `main` agent then rehydrates the persisted log instead of starting fresh, so the model sees the earlier turns as history:
```sh
dsh --resume <prior-session-id>
```
`/resume` opens a searchable keyboard selector with titles, activity, last-turn results, model route, durable goal phase, and live/persisted state. The installed `dsh` host flushes and disposes the current app, then replaces the process with `dsh --resume <id>`. The TUI still prints that command on exit and shows it when a custom host cannot hand off. `dsh --resume <id>` provides the id on the boot context, which `cordis.yml` reads (`resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"`); with no flag the agent starts a new session. A missing or unreadable id starts no agent and emits `agent-loop/config-start-failed`: the TUI prints the failure and exits nonzero. The selector has no cross-process session lock, so deployments with concurrent hosts must coordinate session ownership separately.
## Code Mode
[`code-mode.cordis.yml`](code-mode.cordis.yml) overlays the same tree with the worker-thread runtime and `tools: { mode: code }`. The model receives one `run_code` transport plus a generated TypeScript SDK for the visible tools; only program output returns to model context. Use `mode: both` to expose native calls alongside `run_code`. See the [Code Mode Agent Note](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md) for the execution contract.
```sh
pnpm run demo:code-mode # this overlay under the TUI (default UI)
pnpm run demo:code-mode acp # the acp-agent example's same-shaped overlay
```
Try a task that spans several tool calls, e.g.:
> Count the lines of every `*.md` file under docs/ and write the three largest to summary.txt.
and watch the transcript: one `run_code` call, a program looping over tools, and a result the model curated instead of five round-trips of raw tool output.
## What each leaf entry demonstrates
This example is a thin leaf `cordis.yml`: it picks the swappable backends, loads one app package, and adds product tools that are intentionally outside the shared spine. The spine (sessions, system-prompt, tools, agents, invariants, `agent-loop`) and the front-door cluster (JSONL persistence, the pi-tui channel, the pre-created `main` agent) live inside the [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo) app and the [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) bundle it loads; the leaf wires the backends and model-facing optional tools:
| Entry | Demonstrates |
|---|---|
| `hmr` (`@cordisjs/plugin-hmr`) | the dev/demo edit-reload loop — a **leaf** entry (not baked into the app) because it depends on the Loader's internal module access |
| `llm-deepseek` | the default native adapter |
| `bash` (`dsh-bash-local`) | the executor implementation — the swappable half of the bash seam. The model-facing `bash` schema (`tool-bash`) and generic `task_*` controls (`tool-tasks`) come from `dsh-agent-spine-demo`, so only the executor is a leaf choice |
| `tui-agent` (`@deepseek-ai/dsh-tui-demo`) | the app bundle: the agent-spine demo + JSONL persistence + the pi-tui channel + a pre-created `main` agent |
| `subagent`, `subagent-spawn`, `subagent-fork` | the subagent provider registry plus the two in-process backends: a fresh child and a child seeded with the parent's completed-turn prefix |
| `tool-subagent`, `tool-subagent-fork` | two model-facing `dsh-tool-subagent` loads, each bound to a different provider and exposed under a distinct tool name (`subagent`, `subagent_fork`) |
| `workflow-workerthread`, `tool-workflow` | the worker-thread workflow engine and its model-facing `workflow` tool, with child calls routed through the spawn backend |
| `plan-mode` | the plugin-owned `/plan [message]` entry and `/plan off` exit commands, plan-mode prompt policy, tool restrictions, and reviewed `exit_plan_mode` transition |
| `fs-local`, `fs-policy`, `tool-fs` | the filesystem stack: the local `ctx.fs` provider, the read-before-write/edit policy gate (on the `fs/*` event gate), and the model-facing `read`/`write`/`edit` tools. Relative paths resolve against the session workspace |
## End-to-end tests (`pnpm run test:e2e`)
The UI-independent with-key suites assemble the full stack programmatically through `tests/harness.ts` (no PTY, no Loader):
- `tests/full-loop.e2e.ts` — the canary: real model runs `echo e2e-ok` through the real bash tool; asserts `tool/call`/`tool/result` session events and the final answer.
- `tests/coding-task.e2e.ts` — the swebench-style smoke: a temp dir holds `add.js` (with `a - b` where `a + b` belongs) and a failing `add.test.js`; the agent must fix the bug and verify. The test re-runs `node add.test.js` ITSELF and inspects the files — agent claims are not trusted.
- `tests/resume.e2e.ts` — durable continuity across processes: run 1 tells the real model a secret code and persists the turn to a temp JSONL root, then the whole context is disposed; run 2 is a fresh context over the same root that RESUMES the session id and asks the model to recall the code. The recall can only come from the rehydrated log.
- `tests/compaction.e2e.ts` — the compaction smoke: a real multi-step bash task runs with a deliberately tiny context window so the auto-compaction listener fires MID-SESSION. Verifies the WORLD — a `compact/start…end` pair landed in the real log, the surface shrank (a replace node shadowed older nodes), and the agent still produced a correct final answer after compaction.
- `tests/todo-write.e2e.ts` — loads the opt-in `todo_write` tool, then a real model drives it and the test verifies the resulting `todo/write` session event.
- `tests/code-mode.e2e.ts` — the with-key Code Mode proof: a real model, a two-tool task, asserting the wire tool list was exactly `[run_code]`, the `tool/code-dispatch` events landed under the parent call, and the curated answer came back.
These self-skip without `DEEPSEEK_API_KEY`. The keyless `tests/tui-keyless-smoke.e2e.ts` boots the real Loader tree in a PTY (the one sanctioned PTY surface): the base boot + `/plan` + `/exit`, a scripted-LLM conversation with a question dialog and tool round-trip, the Code Mode overlay welcome line, and the resume-failure exit path.
## Snapshot tests
`tests/snapshots/<scenario>/session.jsonl` supplies recorded user prompts and model chunks; sibling child logs drive subagents and workflows. The keyless suite executes those scripts through the real loop and tool implementations, then compares readable expected terminal cell/style output. Use `pnpm run test:snapshot:refresh` for presentation-only changes and `pnpm run test:snapshot:record` with a DeepSeek key when a recorded model journey changes. The implemented [TUI snapshot Agent Note](../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) owns the scenario matrix and the split between recorded journeys, transient package snapshots, and PTY coverage.

View File

@@ -1,80 +0,0 @@
# tui-agent
[English](README.md) | 中文
全屏交互式编码 agent智能体DeepSeek V4、本地 bash 与文件系统工具、压缩compaction、subagent、工作流与全新 agent Ralph 迭代、plan mode`/plan` 进入,`exit_plan_mode` 评审退出)、超时/溢出策略,以及通过 [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo) 提供的 JSONL 持久化;该应用从 `cordis.yml` 加载。同级 [`headless-agent`](../headless-agent/README.md) 以适合管道调用的单次任务形式运行同一能力类,[`acp-agent`](../acp-agent/README.md) 则通过 JSON-RPC 提供该能力。
## 运行
```sh
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run demo:tui
```
演示脚本和可安装的 `dsh` CLI命令行界面见 [`apps/cli`](../../apps/cli/README.md))都会以此示例的 `cordis.yml` 作为已交付的默认配置启动;`dsh` 还会应用 `~/.dsh` 中的个人覆盖,并将调用目录作为工作区。
输入一项编码任务。agent 使用 `read`/`write`/`edit` 文件系统工具处理常规文件操作,使用 `bash`(加上面向后台任务的通用 `task_output`/`task_list`/`task_kill`)执行 shell 命令、搜索和测试。每次 bash 调用都在新的 `bash -c` 中运行(系统提示词要求模型传递 `workdir`,而不是使用 `cd`)。文件系统工具和 bash 都会相对于会话工作区解析相对路径。agent 还可以通过 `subagent`/`subagent_fork` 委托。
`todo_write` 任务跟踪器是选用的,不在已交付配置中:请将 `@deepseek-ai/dsh-tool-todo` 添加到 `cordis.yml`(或在 `~/.dsh` 下使用个人配置覆盖以公开该工具。加载后模型会把整表计划记录到会话日志TUI 则渲染它。
TUI 渲染 Markdown 历史、推理reasoning、工具自有的终端diff通用卡片、token 总量,以及加载 `todo_write` 时的最新计划。较长的工具正文保留首尾预览Ctrl+O 展开或折叠所有卡片。Enter 用于提交,或在 agent 运行时进行 steering中途引导Ctrl+R 切换推理Escape 取消,`/help` 列出命令。`/plan` 为下一步骤选择 plan mode`/plan <message>` 还会将消息提交到该步骤,`/plan off` 则在没有模型输入的情况下选择默认 mode。`/status` 会展开当前会话的标识、活动计数、精确 token缓存 bucket、上下文用量和时间戳而不中断正在运行的轮次。`/model` 打开当前提供方目录的键盘选择器;使用 Up/Down 聚焦模型,使用 Shift+Tab 循环切换为该模型公布的推理强度,再用 Enter 选择;也可以使用 `/model <model>``/model <provider>/<model>` 直接选择。`ask_user_question` 会打开一个位于左下方的宽键盘面板,包含批次进度和编号选项。
### 恢复早先的会话
每次运行默认都会启动新会话(其事件日志落在 `./.sessions/` 下)。如需 **继续** 先前对话,请将其 id 传给已安装的 `dsh` CLI此时 `main` agent 会重新水化持久日志,而不会从头开始,因此模型会将早先轮次视为历史:
```sh
dsh --resume <prior-session-id>
```
`/resume` 打开可搜索键盘选择器,显示标题、活动、上一轮结果、模型路由、持久化目标阶段和实时/已持久化状态。已安装的 `dsh` 宿主会等待刷写完成,对当前应用执行 dispose资源释放然后以 `dsh --resume <id>` 替换进程。TUI 仍会在退出时打印该命令,并在自定义宿主无法移交时显示它。`dsh --resume <id>` 在启动上下文中提供 id`cordis.yml` 会读取它(`resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"`没有标志时agent 会开始新会话。缺失或无法读取的 id 不会启动 agent而会发出 `agent-loop/config-start-failed`TUI 打印失败并以非零状态退出。选择器没有跨进程会话锁,因此拥有并发宿主的部署必须自行协调会话所有权。
## Code Mode
[`code-mode.cordis.yml`](code-mode.cordis.yml) 在同一树上覆盖 worker 线程运行时和 `tools: { mode: code }`。模型会收到一个 `run_code` 传输工具,加上一份为可见工具生成的 TypeScript SDK只有程序输出会返回模型上下文。使用 `mode: both` 可在 `run_code` 旁同时公开原生调用。执行契约详见 [Code Mode Agent Noteagent 决策记录)](../../.agents/notes/implemented/feature/2026-06-15-code-mode.md)。
```sh
pnpm run demo:code-mode # this overlay under the TUI (default UI)
pnpm run demo:code-mode acp # the acp-agent example's same-shaped overlay
```
尝试一项横跨多个工具调用的任务,例如:
> 统计 docs/ 下每个 `*.md` 文件的行数,并将最大的三个写入 summary.txt。
然后观察 transcript文本记录一次 `run_code` 调用、一个循环调用工具的程序,以及模型筛选后的结果,而不是五次原始工具输出往返。
## 每个叶节点配置项所演示的内容
此示例是轻量叶节点 `cordis.yml`它选择可替换后端、加载一个应用包package并添加有意放在共享主干外的产品工具。主干会话、系统提示词、工具、agent、不变式、`agent-loop`和入口集群JSONL 持久化、pi-tui 通道、预创建的 `main` agent位于 [`@deepseek-ai/dsh-tui-demo`](../../packages/examples/tui-demo) 应用及其加载的 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) 组合包中;叶节点负责接线后端与面向模型的可选工具:
| 配置项 | 演示内容 |
|---|---|
| `hmr` (`@cordisjs/plugin-hmr`) | 开发/演示的编辑-重载循环:它是 **叶节点** 配置项(不内置到应用),因为它依赖 Loader 的内部模块访问 |
| `llm-deepseek` | 默认原生适配器 |
| `bash` (`dsh-bash-local`) | 执行器实现bash seam 中可替换的实现侧。面向模型的 `bash` schema`tool-bash`)和通用 `task_*` 控制(`tool-tasks`)由 `dsh-agent-spine-demo` 提供,因此叶节点只选择执行器 |
| `tui-agent` (`@deepseek-ai/dsh-tui-demo`) | 应用组合包agent-spine 演示 + JSONL 持久化 + pi-tui 通道 + 预创建的 `main` agent |
| `subagent`, `subagent-spawn`, `subagent-fork` | subagent 提供方注册表加两个进程内后端:新子 agent以及用父 agent 已完成轮次前缀播种的子 agent |
| `tool-subagent`, `tool-subagent-fork` | 两次面向模型的 `dsh-tool-subagent` 加载,每次绑定不同提供方,并以不同工具名(`subagent``subagent_fork`)公开 |
| `workflow-workerthread`, `tool-workflow` | worker 线程工作流引擎及其面向模型的 `workflow` 工具,子调用通过 spawn 后端路由 |
| `plan-mode` | 插件拥有的 `/plan [message]` 进入命令和 `/plan off` 退出命令、plan-mode 提示词策略、工具限制,以及经评审的 `exit_plan_mode` 转换 |
| `fs-local`, `fs-policy`, `tool-fs` | 文件系统栈:本地 `ctx.fs` 提供方、先读后写/编辑策略门禁(位于 `fs/*` 事件门禁),以及面向模型的 `read`/`write`/`edit` 工具。相对路径相对于会话工作区解析 |
## 端到端测试(`pnpm run test:e2e`
与 UI 无关的带密钥套件通过 `tests/harness.ts` 以程序方式组装完整栈(无 PTY、无 Loader
- `tests/full-loop.e2e.ts`canary 测试:真实模型通过真实 bash 工具运行 `echo e2e-ok`;断言 `tool/call`/`tool/result` 会话事件和最终答案。
- `tests/coding-task.e2e.ts`:类 swebench 冒烟测试:临时目录包含 `add.js`(其中 `a - b` 写在本应是 `a + b` 的位置)和失败的 `add.test.js`agent 必须修复错误并验证。测试会自行重新运行 `node add.test.js` 并检查文件,不信任 agent 的说法。
- `tests/resume.e2e.ts`:跨进程持久连续性:第一次运行告诉真实模型一个密码并将轮次持久化到临时 JSONL 根目录,然后 dispose 整个上下文;第二次运行在同一根目录上创建新上下文,恢复会话 id 并要求模型回忆密码。只有重新水化的日志能够提供该回忆。
- `tests/compaction.e2e.ts`:压缩冒烟测试:一项真实多步 bash 任务在故意设得很小的上下文窗口中运行,使自动压缩监听器在会话中途触发。测试验证外部状态:真实日志中出现 `compact/start…end` 对,模型可见内容缩减(一个替换节点遮蔽了较旧节点),且 agent 在压缩后仍给出正确最终答案。
- `tests/todo-write.e2e.ts`:加载选用的 `todo_write` 工具,由真实模型驱动,测试验证产生的 `todo/write` 会话事件。
- `tests/code-mode.e2e.ts`:带密钥 Code Mode 证明:使用真实模型和双工具任务,断言协议层工具列表精确为 `[run_code]``tool/code-dispatch` 事件位于父调用下,且筛选后的答案已返回。
这些测试在没有 `DEEPSEEK_API_KEY` 时自行跳过。无密钥 `tests/tui-keyless-smoke.e2e.ts` 通过 PTY 启动真实 Loader 树(唯一获准的 PTY 界面):基础启动 + `/plan` + `/exit`,一次带问题对话框和工具往返的脚本 LLM大语言模型对话Code Mode 覆盖配置的欢迎行,以及恢复失败退出路径。
## 快照测试
`tests/snapshots/<scenario>/session.jsonl` 提供已录制的用户提示词和模型分片;同级子日志驱动 subagent 和工作流。无密钥套件通过真实循环和工具实现执行这些脚本,然后比较可读的预期终端单元格/样式输出。对于仅涉及展示的变更,使用 `pnpm run test:snapshot:refresh`;已录制的模型流程改变时,使用 DeepSeek 密钥运行 `pnpm run test:snapshot:record`。已实现的 [TUI 快照 Agent Note](../../.agents/notes/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) 规定了场景矩阵,以及已录制旅程、包级瞬态快照与 PTY 覆盖之间的分工。

View File

@@ -1,32 +0,0 @@
# Code Mode keeps the TUI composition while adding the worker runtime and
# reducing the model-facing registry to the `run_code` transport.
- id: base
name: '@cordisjs/plugin-include'
config:
path: ./cordis.yml
patches:
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-pro
resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"
persistenceRoot: './.sessions'
resumeCommand: 'dsh --resume {session}'
workspaceContext:
maxBytes: 65536
tools:
mode: code
welcome: 'TUI Code Mode ready. Give it a multi-tool task.'
ui:
showReasoning: true
maxToolOutputLines: 6
persona: |
You are a coding agent powered by the {{model}} model.
You work by writing TypeScript programs for run_code: batch related
tool work into one program, loop and branch where it helps, and print
or return ONLY the findings that matter.
- insert:
- id: code-runtime
name: '@deepseek-ai/dsh-code-runtime-worker'

View File

@@ -1,103 +0,0 @@
<!-- Generated by scripts/gen-doc-graphs.ts - do not edit by hand.
Run `pnpm run gen-doc-graphs` to regenerate. -->
# TUI Agent App Composition
The TUI agent combines the real DeepSeek adapter, coding tools, compaction, subagents, and workflows with the full-screen terminal app package.
```mermaid
flowchart LR
cfg["examples/tui-agent<br/>cordis.yml"]
plugin_tui_hmr["hmr<br/>@cordisjs/plugin-hmr"]
cfg --> plugin_tui_hmr
plugin_tui_llm_deepseek["llm-deepseek<br/>@deepseek-ai/dsh-llm-deepseek"]
cfg --> plugin_tui_llm_deepseek
plugin_tui_llm_pi_ai["llm-pi-ai<br/>@deepseek-ai/dsh-llm-pi-ai"]
cfg --> plugin_tui_llm_pi_ai
plugin_tui_subprocess["subprocess<br/>@deepseek-ai/dsh-subprocess-local"]
cfg --> plugin_tui_subprocess
plugin_tui_bash["bash<br/>@deepseek-ai/dsh-bash-local"]
cfg --> plugin_tui_bash
plugin_tui_tui_agent["tui-agent<br/>@deepseek-ai/dsh-tui-demo"]
cfg --> plugin_tui_tui_agent
plugin_tui_tui_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
plugin_tui_tui_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
plugin_tui_tui_agent --> frontdoor_tui["@deepseek-ai/dsh-tui<br/>pre-created main agent"]
bundle_agent_core --> spine_llm["ctx.llm"]
bundle_agent_core --> spine_sessions["ctx.sessions"]
bundle_agent_core --> spine_tools["ctx.tools + tool-bash"]
bundle_agent_core --> spine_loop["ctx.agents + ctx.agentLoop"]
plugin_tui_session_title_llm["session-title-llm<br/>@deepseek-ai/dsh-session-title-first-message-llm"]
cfg --> plugin_tui_session_title_llm
plugin_tui_token_meter["token-meter<br/>@deepseek-ai/dsh-token-meter"]
cfg --> plugin_tui_token_meter
plugin_tui_tool_result_prune["tool-result-prune<br/>@deepseek-ai/dsh-compact-tool-result-prune"]
cfg --> plugin_tui_tool_result_prune
plugin_tui_compact_basic["compact-basic<br/>@deepseek-ai/dsh-compact-basic"]
cfg --> plugin_tui_compact_basic
plugin_tui_subagent["subagent<br/>@deepseek-ai/dsh-subagent"]
cfg --> plugin_tui_subagent
plugin_tui_subagent_spawn["subagent-spawn<br/>@deepseek-ai/dsh-subagent-spawn"]
cfg --> plugin_tui_subagent_spawn
plugin_tui_subagent_fork["subagent-fork<br/>@deepseek-ai/dsh-subagent-fork"]
cfg --> plugin_tui_subagent_fork
plugin_tui_tool_subagent["tool-subagent<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_tui_tool_subagent
plugin_tui_tool_subagent_fork["tool-subagent-fork<br/>@deepseek-ai/dsh-tool-subagent"]
cfg --> plugin_tui_tool_subagent_fork
plugin_tui_workflow_workerthread["workflow-workerthread<br/>@deepseek-ai/dsh-workflow-workerthread"]
cfg --> plugin_tui_workflow_workerthread
plugin_tui_tool_workflow["tool-workflow<br/>@deepseek-ai/dsh-tool-workflow"]
cfg --> plugin_tui_tool_workflow
plugin_tui_tool_ralph["tool-ralph<br/>@deepseek-ai/dsh-tool-ralph"]
cfg --> plugin_tui_tool_ralph
plugin_tui_plan_mode["plan-mode<br/>@deepseek-ai/dsh-plan-mode"]
cfg --> plugin_tui_plan_mode
plugin_tui_fs_local["fs-local<br/>@deepseek-ai/dsh-fs-local"]
cfg --> plugin_tui_fs_local
plugin_tui_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
cfg --> plugin_tui_fs_policy
plugin_tui_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
cfg --> plugin_tui_tool_fs
plugin_tui_tool_fs_search["tool-fs-search<br/>@deepseek-ai/dsh-tool-fs-search"]
cfg --> plugin_tui_tool_fs_search
plugin_tui_timeout_policy["timeout-policy<br/>@deepseek-ai/dsh-timeout-policy"]
cfg --> plugin_tui_timeout_policy
plugin_tui_spill_local["spill-local<br/>@deepseek-ai/dsh-spill-local"]
cfg --> plugin_tui_spill_local
plugin_tui_spill_policy["spill-policy<br/>@deepseek-ai/dsh-spill-policy"]
cfg --> plugin_tui_spill_policy
```
| Plugin id | Package / module |
| --- | --- |
| `hmr` | `@cordisjs/plugin-hmr` |
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
| `llm-pi-ai` | `@deepseek-ai/dsh-llm-pi-ai` |
| `subprocess` | `@deepseek-ai/dsh-subprocess-local` |
| `bash` | `@deepseek-ai/dsh-bash-local` |
| `tui-agent` | `@deepseek-ai/dsh-tui-demo` |
| `session-title-llm` | `@deepseek-ai/dsh-session-title-first-message-llm` |
| `token-meter` | `@deepseek-ai/dsh-token-meter` |
| `tool-result-prune` | `@deepseek-ai/dsh-compact-tool-result-prune` |
| `compact-basic` | `@deepseek-ai/dsh-compact-basic` |
| `subagent` | `@deepseek-ai/dsh-subagent` |
| `subagent-spawn` | `@deepseek-ai/dsh-subagent-spawn` |
| `subagent-fork` | `@deepseek-ai/dsh-subagent-fork` |
| `tool-subagent` | `@deepseek-ai/dsh-tool-subagent` |
| `tool-subagent-fork` | `@deepseek-ai/dsh-tool-subagent` |
| `workflow-workerthread` | `@deepseek-ai/dsh-workflow-workerthread` |
| `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
| `tool-ralph` | `@deepseek-ai/dsh-tool-ralph` |
| `plan-mode` | `@deepseek-ai/dsh-plan-mode` |
| `fs-local` | `@deepseek-ai/dsh-fs-local` |
| `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
| `tool-fs` | `@deepseek-ai/dsh-tool-fs` |
| `tool-fs-search` | `@deepseek-ai/dsh-tool-fs-search` |
| `timeout-policy` | `@deepseek-ai/dsh-timeout-policy` |
| `spill-local` | `@deepseek-ai/dsh-spill-local` |
| `spill-policy` | `@deepseek-ai/dsh-spill-policy` |
Source config: [`examples/tui-agent/cordis.yml`](cordis.yml).
Maintenance mode: hybrid: the leaf plugin list is parsed from its `cordis.yml`; app package expansion is curated from package source.

View File

@@ -1,196 +0,0 @@
# Full-screen TUI coding agent with swappable model and local-bash backends.
# `dsh-tui-demo` supplies the agent spine, workspace instructions, generic
# task controls, JSONL persistence, the pi-tui front door, and `main`.
# HMR remains a leaf because it depends on Loader internals. The app bin loads
# the gitignored root `.env`; this file reads `DEEPSEEK_API_KEY` and optional
# `DEEPSEEK_BASE_URL` through `!!js`.
- id: hmr
name: '@cordisjs/plugin-hmr'
config:
root: ['.']
# The native DeepSeek adapter. Shipped default: full thinking at max effort on
# every request (wire-only defaults; they never enter the request header).
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
config:
apiKey: !!js process.env.DEEPSEEK_API_KEY
baseURL: !!js process.env.DEEPSEEK_BASE_URL
thinking: enabled
reasoningEffort: max
- id: llm-pi-ai
name: '@deepseek-ai/dsh-llm-pi-ai'
config:
providers:
- provider: openai
apiKey: !!js process.env.OPENAI_API_KEY
baseURL: !!js process.env.OPENAI_BASE_URL
- provider: anthropic
apiKey: !!js process.env.ANTHROPIC_API_KEY
baseURL: !!js process.env.ANTHROPIC_BASE_URL
# Local executor for the app bundle's bash tool.
# Managed child-process groups for the bash executor (spawn/kill/output plumbing).
- id: subprocess
name: '@deepseek-ai/dsh-subprocess-local'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
config:
timeoutMs: 60000
# The app bundle pre-creates the TUI's `main` agent.
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: deepseek
model: deepseek-v4-pro
# `dsh --resume <id>` provides the session id on the boot context (the ids
# live under ./.sessions); with no flag the identifier is undefined and a
# fresh session starts each run. The typeof guard tolerates a launcher that
# never provides the slot, reading undefined rather than throwing.
resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"
persistenceRoot: './.sessions'
# Printed on exit and listed by `/resume`; `{session}` fills the live id.
# `dsh --resume <id>` resumes that session, so run it from this cwd.
resumeCommand: 'dsh --resume {session}'
workspaceContext:
maxBytes: 65536
ui:
showReasoning: true
maxToolOutputLines: 6
# Keep the persona to identity and behavior; tool plugins own tool guidance.
# The loop resolves {{model}} from this agent's configuration.
persona: |
You are a coding agent powered by the {{model}} model.
Verify your work by running the code or tests. Keep answers brief and
factual.
# Model-made session titles on the first-message cadence: replaces the spine's
# deterministic fallback title with a short model summary. The TUI renders the
# logged `session/title` as the banner subtitle and the terminal window title.
# Omitting provider/model inherits the main request's exact route.
- id: session-title-llm
name: '@deepseek-ai/dsh-session-title-first-message-llm'
config:
targetWords: 5
targetCjkCharacters: 10
maxInputBytes: 4096
maxOutputTokens: 64
timeoutMs: 60000
# Replay-aware request pressure with one service-wide context window.
- id: token-meter
name: '@deepseek-ai/dsh-token-meter'
- id: tool-result-prune
name: '@deepseek-ai/dsh-compact-tool-result-prune'
# Summarize an older range after measured pressure or a canonical provider overflow.
# Service-wide policy provides pressure, retention, and one overflow-retry default.
- id: compact-basic
name: '@deepseek-ai/dsh-compact-basic'
# Expose fresh-child `spawn` and completed-prefix `fork` through independent
# in-process backends. Each tool instance needs a distinct `toolName`; the registry
# rejects duplicates. These leaves follow the app because it provides `ctx.agents` and `ctx.tools`.
- id: subagent
name: '@deepseek-ai/dsh-subagent'
- id: subagent-spawn
name: '@deepseek-ai/dsh-subagent-spawn'
config:
providerName: spawn
- id: subagent-fork
name: '@deepseek-ai/dsh-subagent-fork'
config:
providerName: fork
- id: tool-subagent
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: spawn
toolName: subagent
- id: tool-subagent-fork
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: fork
toolName: subagent_fork
# The worker-thread workflow engine fans a model-written JavaScript script's
# `agent()` calls out through the spawn backend; the adjacent tool exposes it to the model.
- id: workflow-workerthread
name: '@deepseek-ai/dsh-workflow-workerthread'
config:
provider: spawn
- id: tool-workflow
name: '@deepseek-ai/dsh-tool-workflow'
# A separate fixed consumer demonstrates fresh-agent Ralph iteration without
# changing the workflow tool or same-session goal behavior.
- id: tool-ralph
name: '@deepseek-ai/dsh-tool-ralph'
# Plan mode gives the TUI plugin-owned /plan [message] entry and /plan off exit
# commands; the reviewed exit rides the TUI's user-interaction provider.
- id: plan-mode
name: '@deepseek-ai/dsh-plan-mode'
config:
section: |
You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.
Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.
The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed only to keep the request shape stable. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.
Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.
Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.
When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.
# Policy loads before the model-facing filesystem tools so writes and edits require
# an observed file. This single-session app resolves relative paths from the process cwd.
- id: fs-local
name: '@deepseek-ai/dsh-fs-local'
config:
cwd: !!js process.cwd()
- id: fs-policy
name: '@deepseek-ai/dsh-fs-policy'
- id: tool-fs
name: '@deepseek-ai/dsh-tool-fs'
# Bash-backed discovery tools (glob/grep): fixed ripgrep commands through the
# local bash executor above — not ctx.fs. Capped results save the complete
# formatted list through the spill backend below (ctx.spillStore, optional).
- id: tool-fs-search
name: '@deepseek-ai/dsh-tool-fs-search'
# The tool-call timeout enforcer: arms each declared ToolDefinition.timeoutMs
# (the search tools above declare 30s) as a deadline on exec.signal. Without
# it a declared budget is advisory and only the bash executor's own timeout
# backstop applies.
- id: timeout-policy
name: '@deepseek-ai/dsh-timeout-policy'
# Tool-output spill stack: a local backend that saves oversized tool text under
# a private session-scoped dir, and the tools/post-execute policy that replaces
# an over-budget plain-text result with a preview + the spill locator/retrieval
# hint. A leaf pair after the app (needs ctx.tools). The policy is a no-op until
# a tool returns more than maxInlineBytes of plain text.
- id: spill-local
name: '@deepseek-ai/dsh-spill-local'
- id: spill-policy
name: '@deepseek-ai/dsh-spill-policy'
config:
maxInlineBytes: 50000

View File

@@ -1,7 +0,0 @@
{
"name": "tui-agent-example",
"private": true,
"version": "0.0.1",
"type": "module",
"description": "Runnable demo: the coding agent through the full-screen terminal UI"
}

View File

@@ -1,150 +0,0 @@
import type { Context } from 'cordis'
import type {
GenerateOptions,
LlmModelInfo,
LlmResolvedModelInfo,
StreamChunk,
} from '@deepseek-ai/dsh-llm'
import { CallId, LlmAdapter, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
const CONTROL_PROBE = '\u001b]2;MODEL_CONTROLLED\u0007\u001b[999CMODEL_CURSOR\u009b31mMODEL_C1'
const INITIAL_TEXT = `I need one decision before I continue. ${CONTROL_PROBE}`
const FINAL_TEXT = 'Decision received. Scripted TUI run complete.'
const DEFAULT_MODE_PROBE = 'Confirm the scripted run left plan mode.'
const DEFAULT_MODE_TEXT = 'Default mode confirmed.'
// The `skill` scenario types `/skill:scripted-skill`; the manual-invocation front
// door delivers the loaded skill as a user turn wrapped in `<skill name="…">`. The
// body marker below lives in the fixture skill, so echoing it back proves the whole
// block (name attribute plus body) reached the model, not just the command text.
const SKILL_BLOCK_OPEN = '<skill name="scripted-skill">'
const SKILL_BODY_MARKER = 'SCRIPTED SKILL BODY MARKER'
const SKILL_RECEIVED_TEXT = 'Scripted skill body received.'
const TITLE_TEXT = 'scripted session title'
function textChunks(text: string): StreamChunk[] {
return [
{ type: 'block-start', index: 0, blockType: 'text' },
...Array.from(text, (char): StreamChunk => ({ type: 'text-delta', index: 0, text: char })),
{ type: 'block-end', index: 0, block: { type: 'text', text } },
{ type: 'usage', usage: { inputTokens: 20, outputTokens: text.length } },
{ type: 'finish', reason: { kind: 'stop' } },
]
}
/** Keyless adapter for the real-PTY TUI tests: the two-step conversation and the `/skill:` round-trip. */
class ScriptedTuiAdapter extends LlmAdapter {
override listModels(provider: string): Promise<readonly LlmModelInfo[]> {
return Promise.resolve([
{ provider, id: 'tui-scripted-model', name: 'Scripted Base' },
{ provider, id: 'tui-scripted-model-pro', name: 'Scripted Pro' },
])
}
override resolveModel(
provider: string,
model: string,
): Promise<LlmResolvedModelInfo> {
return Promise.resolve({
provider,
id: model,
name: model === 'tui-scripted-model-pro' ? 'Scripted Pro' : 'Scripted Base',
context: { contextWindow: 128_000 },
...model !== 'tui-scripted-model-pro'
? {}
: {
reasoning: {
efforts: [
{ id: ReasoningEffortId('off'), name: 'Off' },
{ id: ReasoningEffortId('high'), name: 'High' },
{ id: ReasoningEffortId('max'), name: 'Max' },
],
defaultEffort: ReasoningEffortId('high'),
},
},
})
}
override async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
// The session-title provider's auxiliary request carries no tool schemas,
// unlike every agent turn; answer it with a fixed title so the PTY test can
// assert the logged title reaches the terminal window title.
if ((options.tools?.length ?? 0) === 0) {
for (const chunk of textChunks(TITLE_TEXT)) yield chunk
return
}
if (
options.model !== 'tui-scripted-model-pro'
|| !options.system?.includes('tui-scripted-model-pro')
|| options.reasoningEffort !== ReasoningEffortId('max')
) {
throw new Error('the scripted TUI request did not apply the selected model and reasoning effort')
}
const lastMessage = options.messages.at(-1)
// The loop appends plugin-sourced context (the plan-mode notice, the
// tool-skill catalog) AFTER the admitted prompt, so the scripted trigger
// may sit one or more user messages back: scan the whole trailing run of
// user-role messages since the last assistant message.
const trailingUserTexts: string[] = []
for (let index = options.messages.length - 1; index >= 0; index--) {
const message = options.messages[index]
if (message?.role !== 'user') break
for (const block of message.content) {
if (block.type === 'text') trailingUserTexts.push(block.text)
}
}
const lastText = trailingUserTexts.join('\n')
if (lastText.includes(DEFAULT_MODE_PROBE)) {
if (options.system?.includes('Stay in plan mode for this scripted TUI test.')) {
throw new Error('the scripted TUI request retained plan guidance after /plan off')
}
for (const chunk of textChunks(DEFAULT_MODE_TEXT)) yield chunk
return
}
if (lastText.includes(SKILL_BLOCK_OPEN)) {
const ack = lastText.includes(SKILL_BODY_MARKER)
? SKILL_RECEIVED_TEXT
: 'Scripted skill block arrived without its body.'
for (const chunk of textChunks(ack)) yield chunk
return
}
const hasToolResult = lastMessage?.content.some(block => block.type === 'tool-result') ?? false
if (hasToolResult) {
for (const chunk of textChunks(FINAL_TEXT)) yield chunk
return
}
const args = JSON.stringify({
questions: [{
id: 'mode',
header: 'Execution mode',
question: 'How should the scripted run proceed?',
options: [
{ label: 'Safe', description: 'Use the guarded path.' },
{ label: 'Fast', description: 'Use the shorter path.' },
],
}],
})
const callId = CallId('call-ask-mode')
yield { type: 'block-start', index: 0, blockType: 'text' }
for (const char of INITIAL_TEXT) yield { type: 'text-delta', index: 0, text: char }
yield { type: 'block-end', index: 0, block: { type: 'text', text: INITIAL_TEXT } }
yield { type: 'block-start', index: 1, blockType: 'tool-call' }
yield { type: 'tool-call-delta', index: 1, id: callId, name: 'ask_user_question', argumentsDelta: args }
yield {
type: 'block-end',
index: 1,
block: { type: 'tool-call', id: callId, name: 'ask_user_question', arguments: args },
}
yield { type: 'usage', usage: { inputTokens: 20, outputTokens: 10 } }
yield { type: 'finish', reason: { kind: 'tool-calls' } }
}
}
export const name = 'tui-scripted-llm'
export const inject = ['llm']
/** Register the network-free adapter used by the PTY fixture. */
export function apply(ctx: Context): void {
ctx.llm.registerAdapter(['tui-scripted'], new ScriptedTuiAdapter())
}

View File

@@ -1,55 +0,0 @@
# Real Loader composition for the keyless conversational PTY test. The app
# bundle supplies the production agent/TUI/user-question stack; only the model
# is scripted so the terminal interaction is deterministic and network-free.
- id: scripted-llm
name: './tui-scripted-llm.ts'
# Managed child-process groups for the bash executor (spawn/kill/output plumbing).
- id: subprocess
name: '@deepseek-ai/dsh-subprocess-local'
- id: bash
name: '@deepseek-ai/dsh-bash-local'
- id: fs-local
name: '@deepseek-ai/dsh-fs-local'
config:
cwd: !!js process.cwd()
- id: token-meter
name: '@deepseek-ai/dsh-token-meter'
- id: plan-mode
name: '@deepseek-ai/dsh-plan-mode'
config:
section: 'Stay in plan mode for this scripted TUI test.'
- id: tui-agent
name: '@deepseek-ai/dsh-tui-demo'
config:
provider: tui-scripted
model: tui-scripted-model
persistenceRoot: './.sessions'
# The smoke's log inspection reads plain `.jsonl`; keep the scripted
# fixture uncompressed like the other snapshot-facing configs.
persistenceCompression: none
resumeSessionId: !!js "typeof resumeSessionId === 'string' ? resumeSessionId : undefined"
resumeCommand: 'dsh --resume {session}'
workspaceContext:
maxBytes: 65536
welcome: 'scripted TUI ready.'
persona: 'Scripted model {{model}}.'
ui:
showReasoning: true
# Model-made session titles, as in the shipped cordis.yml: the scripted adapter
# answers the tool-less title request with a fixed string so the PTY test can
# assert the logged title reaches the terminal window title.
- id: session-title-llm
name: '@deepseek-ai/dsh-session-title-first-message-llm'
config:
targetWords: 5
targetCjkCharacters: 10
maxInputBytes: 4096
maxOutputTokens: 64
timeoutMs: 10000

View File

@@ -1,236 +0,0 @@
import { mkdirSync, writeFileSync } from 'node:fs'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { execa } from 'execa'
import { resolveExampleLaunch, type ExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
const POSIX_PTY_DRIVER = String.raw`
import errno, json, os, pty, select, signal, sys, time
node, launch_args_json, launch_env_json, cwd, actions_json, expected_exit, timeout_seconds = sys.argv[1:]
env = os.environ.copy()
env.update(json.loads(launch_env_json))
env.update({"COLUMNS": "100", "LINES": "30"})
# Deterministic banner: a developer shell's COLORTERM=truecolor would switch the
# banner to the per-letter gradient (one SGR per letter), breaking literal
# DEEPSEEK assertions. The gradient path has its own unit and snapshot coverage.
env.pop("COLORTERM", None)
actions = json.loads(actions_json)
pid, fd = pty.fork()
if pid == 0:
os.chdir(cwd)
os.execvpe(node, [node, *json.loads(launch_args_json)], env)
output = bytearray()
action_index = 0
deadline = time.monotonic() + float(timeout_seconds)
status = None
while time.monotonic() < deadline:
ready, _, _ = select.select([fd], [], [], 0.05)
if ready:
try:
chunk = os.read(fd, 65536)
except OSError as error:
if error.errno != errno.EIO:
raise
chunk = b""
if chunk:
output.extend(chunk)
while action_index < len(actions) and actions[action_index]["waitFor"].encode() in output:
action = actions[action_index]
if "writeFile" in action:
target = os.path.join(cwd, action["writeFile"]["path"])
os.makedirs(os.path.dirname(target), exist_ok=True)
with open(target, "w", encoding="utf-8") as handle:
handle.write(action["writeFile"]["content"])
if "send" in action:
os.write(fd, action["send"].encode())
else:
os.write(fd, action["send"].encode())
action_index += 1
waited, candidate = os.waitpid(pid, os.WNOHANG)
if waited == pid:
status = candidate
break
if status is None:
os.kill(pid, signal.SIGKILL)
_, status = os.waitpid(pid, 0)
sys.stdout.buffer.write(output)
if action_index != len(actions):
sys.stderr.write(f"completed {action_index}/{len(actions)} PTY actions before timeout\n")
sys.exit(124)
actual_exit = os.waitstatus_to_exitcode(status)
if actual_exit != int(expected_exit):
sys.stderr.write(f"expected exit {expected_exit}, got {actual_exit}\n")
sys.exit(125)
`
/** One terminal input or workspace mutation performed after its marker renders. */
type TuiPtyAction =
| { readonly waitFor: string; readonly send: string }
| {
readonly waitFor: string
readonly writeFile: { readonly path: string; readonly content: string }
readonly send?: string
}
/** Inputs for a keyless real-Loader TUI process smoke. */
export interface TuiPtySmokeOptions {
readonly label: string
readonly tempDirPrefix: string
readonly binScript: string
/** Config argument; ignored when {@link configArgs} is set. */
readonly configPath?: string
/** Full argument vector for the bin (e.g. `[]` for a bin with a built-in default config). */
readonly configArgs?: readonly string[]
readonly tsconfigPath: string
readonly actions?: readonly TuiPtyAction[]
readonly env?: Readonly<NodeJS.ProcessEnv>
readonly expectedExitCode?: number
readonly timeoutMs?: number
/** Seed the isolated workspace (`cwd`, with `$DSH_HOME` at `.dsh` and the agents home at `.agents`) before launch. */
readonly prepare?: (cwd: string) => Promise<void>
/** Inspect the workspace after a passing run, before the temp dir is removed. */
readonly inspect?: (cwd: string) => Promise<void>
}
function definedEnv(env: NodeJS.ProcessEnv): Record<string, string> {
return Object.fromEntries(
Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined),
)
}
async function runPosixPtySmoke(
launch: ExampleLaunch,
cwd: string,
options: TuiPtySmokeOptions,
timeoutMs: number,
): Promise<string> {
// The driver owns the PTY deadline (`timeoutMs`); the outer execa deadline
// only backstops a wedged python3 process itself.
const result = await execa('python3', [
'-c',
POSIX_PTY_DRIVER,
launch.command,
JSON.stringify(launch.args),
JSON.stringify(launch.env),
cwd,
JSON.stringify(options.actions ?? []),
String(options.expectedExitCode ?? 0),
String(timeoutMs / 1_000),
], {
stdin: 'ignore',
timeout: timeoutMs + 5_000,
killSignal: 'SIGKILL',
reject: false,
stripFinalNewline: false,
})
if (result.timedOut) {
throw new Error(`${options.label} PTY driver did not exit. stdout:\n${result.stdout}\nstderr:\n${result.stderr}`)
}
if (result.failed) {
throw new Error(`${options.label} PTY driver exited ${String(result.exitCode)}. stdout:\n${result.stdout}\nstderr:\n${result.stderr}`)
}
return result.stdout
}
async function runWindowsPtySmoke(
launch: ExampleLaunch,
cwd: string,
options: TuiPtySmokeOptions,
timeoutMs: number,
): Promise<string> {
const pty = await import('node-pty')
return await new Promise((resolve, reject) => {
const actions = options.actions ?? []
const expectedExitCode = options.expectedExitCode ?? 0
let output = ''
let actionIndex = 0
let timedOut = false
const terminal = pty.spawn(launch.command, launch.args, {
name: 'xterm-256color',
cols: 100,
rows: 30,
cwd,
env: definedEnv({
...process.env,
...launch.env,
// Match the POSIX driver: no COLORTERM, so the banner never takes the
// truecolor gradient path under a developer's shell.
COLORTERM: undefined,
COLUMNS: '100',
LINES: '30',
}),
})
const timer = setTimeout(() => {
timedOut = true
terminal.kill()
}, timeoutMs)
terminal.onData((chunk) => {
output += chunk
while (actionIndex < actions.length && output.includes(actions[actionIndex]!.waitFor)) {
const action = actions[actionIndex]!
if ('writeFile' in action) {
const target = join(cwd, action.writeFile.path)
mkdirSync(dirname(target), { recursive: true })
writeFileSync(target, action.writeFile.content)
const input = action.send
if (input !== undefined) terminal.write(input)
} else {
terminal.write(action.send)
}
actionIndex += 1
}
})
terminal.onExit(({ exitCode, signal }) => {
clearTimeout(timer)
if (timedOut) {
reject(new Error(`${options.label} PTY process did not exit before ${String(timeoutMs)}ms. output:\n${output}`))
} else if (actionIndex !== actions.length) {
reject(new Error(`${options.label} completed ${String(actionIndex)}/${String(actions.length)} PTY actions. output:\n${output}`))
} else if (exitCode !== expectedExitCode) {
reject(new Error(`${options.label} expected exit ${String(expectedExitCode)}, got ${String(exitCode)} (signal ${String(signal)}). output:\n${output}`))
} else {
resolve(output)
}
})
})
}
/**
* Boot an example in a real pseudo-terminal (ConPTY on Windows), drive
* marker-gated input, and return captured bytes after the expected process exit.
* @param options - launch paths, environment, actions, and expected exit code.
* @returns complete pseudo-terminal output.
*/
export async function runTuiPtySmoke(options: TuiPtySmokeOptions): Promise<string> {
const cwd = await mkdtemp(join(tmpdir(), options.tempDirPrefix))
const timeoutMs = options.timeoutMs ?? 25_000
try {
await options.prepare?.(cwd)
const launch = resolveExampleLaunch({
srcBin: options.binScript,
// `configPath` is the dsh `--config <path>` tree override; `configArgs`
// is the raw-args escape (e.g. `['--resume', <id>]`) for other flags.
configArgs: options.configArgs !== undefined
? [...options.configArgs]
/* v8 ignore next -- every caller passes configPath or configArgs; the fallback keeps the type total */
: options.configPath !== undefined ? ['--config', options.configPath] : [],
tsconfigPath: options.tsconfigPath,
env: {
DSH_HOME: join(cwd, '.dsh'),
DSH_AGENTS_HOME: join(cwd, '.agents'),
...options.env,
},
})
const output = process.platform === 'win32'
? await runWindowsPtySmoke(launch, cwd, options, timeoutMs)
: await runPosixPtySmoke(launch, cwd, options, timeoutMs)
// Inspect the workspace before `finally` removes it (e.g. the session log).
await options.inspect?.(cwd)
return output
} finally {
await rm(cwd, { recursive: true, force: true })
}
}

View File

@@ -1,30 +0,0 @@
{"type":"session","version":0,"id":"e128dda9-ed11-4868-8266-0ef90d03c3d6","createdAt":1783352050748,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1783352050753,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352050753,"data":{"content":[{"type":"text","text":"Use the bash tool to run exactly: echo TERMINAL_OK. Then reply with the single word DONE and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783352050755,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783352050756,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783352051421,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":5,"time0":1783352051422,"data":{"turn":1,"step":1,"index":0,"dt":[168,28,0,1,0,0,26,30,0,0,1,0,27,1,0,0,0],"texts":["The"," user"," wants"," me"," to"," run"," a"," simple"," bash"," command"," and"," then"," reply"," with"," \"","D","ONE","\"."]}}
{"type":"assistant/chunk","seq":23,"time":1783352051790,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"tool-call-chunks","seq0":24,"time0":1783352051791,"data":{"turn":1,"step":1,"index":1,"dt":[29,0,0,0,0,28,0,0,0,29,0,0,28,1,0,29,0,0,0,32,0,0,0,0,0,74,0,0,13,0],"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","args":["","{","\"","command","\"",": ","\"","echo"," TER","MIN","AL","_OK","\"",", ","\"","description","\"",": ","\"","E","cho"," TER","MIN","AL","_OK"," to"," verify"," terminal"," access","\"","}"]}}
{"type":"assistant/chunk","seq":55,"time":1783352052117,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to run a simple bash command and then reply with \"DONE\"."}}}}
{"type":"assistant/chunk","seq":56,"time":1783352052118,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","arguments":"{\"command\": \"echo TERMINAL_OK\", \"description\": \"Echo TERMINAL_OK to verify terminal access\"}"}}}}
{"type":"assistant/chunk","seq":57,"time":1783352052118,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2877,"outputTokens":90,"cacheReadTokens":0,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":58,"time":1783352052118,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":59,"time":1783352052121,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a simple bash command and then reply with \"DONE\"."},{"type":"tool-call","id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","arguments":"{\"command\": \"echo TERMINAL_OK\", \"description\": \"Echo TERMINAL_OK to verify terminal access\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":2877,"outputTokens":90,"cacheReadTokens":0,"reasoningTokens":18}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58],"surfaceOp":"append"}
{"type":"tool/call","seq":60,"time":1783352052121,"data":{"turn":1,"step":1,"callId":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","arguments":"{\"command\": \"echo TERMINAL_OK\", \"description\": \"Echo TERMINAL_OK to verify terminal access\"}"}}
{"type":"tool/result","seq":61,"time":1783352052136,"data":{"turn":1,"step":1,"callId":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","content":[{"type":"text","text":"TERMINAL_OK\n"}],"isError":false},"sourceEventSeqs":[60],"surfaceOp":"append"}
{"type":"step/end","seq":62,"time":1783352052137,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":63,"time":1783352052137,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":64,"time":1783352052701,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":65,"time0":1783352052702,"data":{"turn":1,"step":2,"index":0,"dt":[78,29,29,0,0,29,0,0,0,0,0,28,1,28,1,0,0,32,0,0,0],"texts":["The"," command"," ran"," successfully"," and"," output"," \"","TER","MIN","AL","_OK","\"."," I"," should"," now"," reply"," with"," just"," \"","D","ONE","\"."]}}
{"type":"assistant/chunk","seq":87,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":88,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
{"type":"assistant/chunk","seq":89,"time":1783352052986,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
{"type":"assistant/chunk","seq":90,"time":1783352052986,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The command ran successfully and output \"TERMINAL_OK\". I should now reply with just \"DONE\"."}}}}
{"type":"assistant/chunk","seq":91,"time":1783352052986,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
{"type":"assistant/chunk","seq":92,"time":1783352052986,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":168,"outputTokens":25,"cacheReadTokens":2816,"reasoningTokens":22}}}}
{"type":"assistant/chunk","seq":93,"time":1783352052986,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":94,"time":1783352052987,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The command ran successfully and output \"TERMINAL_OK\". I should now reply with just \"DONE\"."},{"type":"text","text":"DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":168,"outputTokens":25,"cacheReadTokens":2816,"reasoningTokens":22}},"sourceEventSeqs":[64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93],"surfaceOp":"append"}
{"type":"step/end","seq":95,"time":1783352052987,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":96,"time":1783352052987,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,55 +0,0 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Use the bash tool to — DSH TUI snapshot"
cursor hidden column=7 viewportRow=24 bufferRow=24
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Use the bash tool to"
style 1-20 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Use the bash tool to run exactly: echo TERMINAL_OK. Then reply with the single word DONE and stop. "
6| <blank>
7| "Assistant "
style 0-8 fg=bright-magenta bold underline
8| "Reasoning "
style 0-8 dim italic
9| "The user wants me to run a simple bash command and then reply with \"DONE\". "
style 0-73 dim italic
10| <blank>
11| "● Tool / bash / Echo TERMINAL_OK to verify terminal access"
style 0-57 fg=green
12| "$ echo TERMINAL_OK "
style 0-17 dim
13| "TERMINAL_OK "
style 0-10 dim
14| "[exit 0] "
style 0-7 dim
15| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
16| <blank>
17| "Assistant "
style 0-8 fg=bright-magenta bold underline
18| "Reasoning "
style 0-8 dim italic
19| "The command ran successfully and output \"TERMINAL_OK\". I should now reply with just \"DONE\". "
style 0-90 dim italic
20| "DONE "
21| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
22| <blank>
23| "/workspace/project deepseek-v4-flash ↑3.0k ↓115 cache 48% 3% contex"
style 0-46 fg=bright-magenta bold
style 49-65 dim
style 68-88 dim
style 91-99 dim
24| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
25-35| <blank>

View File

@@ -1,32 +0,0 @@
{"type":"session","version":0,"id":"main-session","createdAt":1785052797743,"cwd":"{{cwd}}"}
{"type":"turn/start","seq":0,"time":1785052797817,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1785052797818,"data":{"content":[{"type":"text","text":"Using ONE run_code program: call the bash tool exactly once with the command `seq 1 200 | awk '{printf \"line %04d: the quick brown fox jumps over the lazy dog\\n\", $1}'`, then return ONLY the number of lines in its output. Reply with just that number and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785052797825,"data":{"title":"Using ONE run_code program: call","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785052797826,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785052797827,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":5,"time":1785052798220,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":6,"time0":1785052798221,"data":{"turn":1,"step":1,"index":0,"dt":[170,30,0,0,0,30,1,0,0,28,0,0,0,29,30,0,30,0,30,0,0,0,30,0,0,0,0,0,30,30,1],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," run","_code"," program"," that"," calls"," bash"," exactly"," once"," with"," a"," specific"," command",","," then"," returns"," only"," the"," number"," of"," lines"," in"," its"," output","."]}}
{"type":"assistant/chunk","seq":38,"time":1785052798781,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"tool-call-chunks","seq0":39,"time0":1785052798781,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,28,0,0,0,30,29,1,0,30,30,1,0,30,0,0,28,1,0,0,30,0,0,0,0,1,28,1,0,0,0,0,58,1,0,15,0,0,0,0,39,0,1,0,28,0,0,29,0,0,0,0,0,30,0,0,0,0,1,29,1,0,0,0,29,0,0,40,0,0,20,1,29,0,30,0,30,0,0,31,0,0,30,0,0,30,0,0,30,0,1,0,29,0,0,30,31,0,0,0,0,0,30,0,0,0,1,0,28,0,0],"id":"call_00_R6g9Uzx4h0jeUv9g3fno7490","name":"run_code","args":["","{","\"","description","\"",": ","\"","Count"," lines"," in"," seq","/","awk"," output","\"",", ","\"","code","\"",": ","\"","const"," result"," ="," await"," tools",".b","ash","({\\n"," "," command",":"," \\\"","seq"," ","1"," ","200"," |"," awk"," '{","printf"," \\\\\\\"","line"," %","04","d",":"," the"," quick"," brown"," fox"," jumps"," over"," the"," lazy"," dog","\\\\\\\\","n","\\\\\\\","," $","1","}'","\\\",\\n"," "," description",":"," \\\"","Generate"," ","200"," lines"," of"," text","\\\"\\n","});\\n\\n","//"," Count"," lines"," in"," stdout","\\n","const"," lines"," ="," result",".k","ind"," ==="," \\\"","fore","ground","\\\""," ?"," result",".stdout",".text",".trim","().","split","(\\\"\\\\","n","\\\").","length"," :"," ","0",";\\n","return"," lines",";","\"","}"]}}
{"type":"assistant/chunk","seq":152,"time":1785052799793,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that calls bash exactly once with a specific command, then returns only the number of lines in its output."}}}}
{"type":"assistant/chunk","seq":153,"time":1785052799793,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_R6g9Uzx4h0jeUv9g3fno7490","name":"run_code","arguments":"{\"description\": \"Count lines in seq/awk output\", \"code\": \"const result = await tools.bash({\\n command: \\\"seq 1 200 | awk '{printf \\\\\\\"line %04d: the quick brown fox jumps over the lazy dog\\\\\\\\n\\\\\\\", $1}'\\\",\\n description: \\\"Generate 200 lines of text\\\"\\n});\\n\\n// Count lines in stdout\\nconst lines = result.kind === \\\"foreground\\\" ? result.stdout.text.trim().split(\\\"\\\\n\\\").length : 0;\\nreturn lines;\"}"}}}}
{"type":"assistant/chunk","seq":154,"time":1785052799794,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":90,"outputTokens":186,"cacheReadTokens":3968,"reasoningTokens":32}}}}
{"type":"assistant/chunk","seq":155,"time":1785052799794,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":156,"time":1785052799798,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that calls bash exactly once with a specific command, then returns only the number of lines in its output."},{"type":"tool-call","id":"call_00_R6g9Uzx4h0jeUv9g3fno7490","name":"run_code","arguments":"{\"description\": \"Count lines in seq/awk output\", \"code\": \"const result = await tools.bash({\\n command: \\\"seq 1 200 | awk '{printf \\\\\\\"line %04d: the quick brown fox jumps over the lazy dog\\\\\\\\n\\\\\\\", $1}'\\\",\\n description: \\\"Generate 200 lines of text\\\"\\n});\\n\\n// Count lines in stdout\\nconst lines = result.kind === \\\"foreground\\\" ? result.stdout.text.trim().split(\\\"\\\\n\\\").length : 0;\\nreturn lines;\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":90,"outputTokens":186,"cacheReadTokens":3968,"reasoningTokens":32}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155],"surfaceOp":"append"}
{"type":"tool/call","seq":157,"time":1785052799799,"data":{"turn":1,"step":1,"callId":"call_00_R6g9Uzx4h0jeUv9g3fno7490","name":"run_code","arguments":"{\"description\": \"Count lines in seq/awk output\", \"code\": \"const result = await tools.bash({\\n command: \\\"seq 1 200 | awk '{printf \\\\\\\"line %04d: the quick brown fox jumps over the lazy dog\\\\\\\\n\\\\\\\", $1}'\\\",\\n description: \\\"Generate 200 lines of text\\\"\\n});\\n\\n// Count lines in stdout\\nconst lines = result.kind === \\\"foreground\\\" ? result.stdout.text.trim().split(\\\"\\\\n\\\").length : 0;\\nreturn lines;\"}"}}
{"type":"tool/code-dispatch-start","seq":158,"time":1785052799893,"data":{"parentCallId":"call_00_R6g9Uzx4h0jeUv9g3fno7490","subCallId":"call_00_R6g9Uzx4h0jeUv9g3fno7490:code:1","name":"bash","arguments":{"command":"seq 1 200 | awk '{printf \"line %04d: the quick brown fox jumps over the lazy dog\\n\", $1}'","description":"Generate 200 lines of text"}}}
{"type":"tool/code-dispatch","seq":159,"time":1785052799923,"data":{"parentCallId":"call_00_R6g9Uzx4h0jeUv9g3fno7490","subCallId":"call_00_R6g9Uzx4h0jeUv9g3fno7490:code:1","name":"bash","arguments":{"command":"seq 1 200 | awk '{printf \"line %04d: the quick brown fox jumps over the lazy dog\\n\", $1}'","description":"Generate 200 lines of text"},"isError":false,"content":[{"type":"text","text":"line 0001: the quick brown fox jumps over the lazy dog\nline 0002: the quick brown fox jumps over the lazy dog\nline 0003: the quick brown fox jumps over the lazy dog\nline 0004: the quick s over the lazy dog\nline 0198: the quick brown fox jumps over the lazy dog\nline 0199: the quick brown fox jumps over the lazy dog\nline 0200: the quick brown fox jumps over the lazy dog\n\n\n(Omitted 10629 bytes. Full formatted result stored at: {{cwd}}/.spill/session-2d2b9e84a250/825a63550249-bash.txt. Use read with offset/limit, or grep this path to search within it.)"}]}}
{"type":"tool/result","seq":160,"time":1785052799925,"data":{"turn":1,"step":1,"callId":"call_00_R6g9Uzx4h0jeUv9g3fno7490","content":[{"type":"text","text":"200"}],"isError":false},"sourceEventSeqs":[157],"surfaceOp":"append"}
{"type":"step/end","seq":161,"time":1785052799926,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":162,"time":1785052799928,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":163,"time":1785052800414,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":164,"time0":1785052800415,"data":{"turn":1,"step":2,"index":0,"dt":[157,32,0,0,0,1,30,1,30,0,0,0,33,1,0,0,0,31,0],"texts":["The"," result"," is"," ","200"," lines","."," The"," user"," wants"," me"," to"," reply"," with"," just"," that"," number"," and"," stop","."]}}
{"type":"assistant/chunk","seq":184,"time":1785052800731,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":185,"time":1785052800731,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"200"}}}
{"type":"assistant/chunk","seq":186,"time":1785052800731,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The result is 200 lines. The user wants me to reply with just that number and stop."}}}}
{"type":"assistant/chunk","seq":187,"time":1785052800731,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"200"}}}}
{"type":"assistant/chunk","seq":188,"time":1785052800732,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":33,"outputTokens":22,"cacheReadTokens":4224,"reasoningTokens":20}}}}
{"type":"assistant/chunk","seq":189,"time":1785052800732,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":190,"time":1785052800733,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The result is 200 lines. The user wants me to reply with just that number and stop."},{"type":"text","text":"200"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":33,"outputTokens":22,"cacheReadTokens":4224,"reasoningTokens":20}},"sourceEventSeqs":[163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189],"surfaceOp":"append"}
{"type":"step/end","seq":191,"time":1785052800733,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":192,"time":1785052800733,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,59 +0,0 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Using ONE run_code program: call — DSH TUI snapshot"
cursor hidden column=7 viewportRow=26 bufferRow=26
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Using ONE run_code program: call"
style 1-32 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Using ONE run_code program: call the bash tool exactly once with the command seq 1 200 | awk "
style 77-99 fg=cyan
6| "'{printf \"line %04d: the quick brown fox jumps over the lazy dog\\n\", $1}', then return ONLY the "
style 0-72 fg=cyan
7| "number of lines in its output. Reply with just that number and stop. "
8| <blank>
9| "Assistant "
style 0-8 fg=bright-magenta bold underline
10| "Reasoning "
style 0-8 dim italic
11| "The user wants me to write a single run_code program that calls bash exactly once with a specific "
style 0-99 dim italic
12| "command, then returns only the number of lines in its output. "
style 0-60 dim italic
13| <blank>
14| "● Tool / run_code"
style 0-16 fg=green
15| "Count lines in seq/awk output "
style 0-99 dim
16| "200 "
style 0-99 dim
17| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
18| <blank>
19| "Assistant "
style 0-8 fg=bright-magenta bold underline
20| "Reasoning "
style 0-8 dim italic
21| "The result is 200 lines. The user wants me to reply with just that number and stop. "
style 0-82 dim italic
22| "200 "
23| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
24| <blank>
25| "/workspace/project deepseek-v4-flash ↑123 ↓208 cache 99% 3% c"
style 0-52 fg=bright-magenta bold
style 55-71 dim
style 74-93 dim
style 96-99 dim
26| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
27-35| <blank>

View File

@@ -1,34 +0,0 @@
{"type":"session","version":0,"id":"main-session","createdAt":1785014512062,"cwd":"{{cwd}}"}
{"type":"turn/start","seq":0,"time":1785014512139,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1785014512140,"data":{"content":[{"type":"text","text":"Using ONE run_code program: call the bash tool twice — exactly `echo CODE_ONE` then exactly `echo CODE_TWO`. Inside that same program, console.log exactly `captured output`, then return the two outputs joined with a plus sign. Reply with that joined string only and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"session/title","seq":2,"time":1785014512146,"data":{"title":"Using ONE run_code program: call","messageSeqs":[1],"source":{"kind":"fallback"}}}
{"type":"step/start","seq":3,"time":1785014512147,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":4,"time":1785014512148,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":5,"time":1785014512526,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":6,"time0":1785014512527,"data":{"turn":1,"step":1,"index":0,"dt":[92,26,0,0,0,27,0,1,20,1,0,0,0,25,1,0,0,0,24,1,24,26,0,24,1,25,0,0,0,1,0,24,0,1,0,0,0,24,1,0,0,0,24,0,1,0,24,1,0,0,0,0,24,1,0,24,0,0,0,1,1,23,0,0,0,0,1,24,25,1,24,1,0,0,0,25,0,25,1,0,0,25,0,0,24,1,0,0,0,25,0,0,24,1,0,25,1,0,0,25,23,26,1,0,0,25,0,0,24,1,0,0,24,0,1,0,24,1,0,0,25,0,0,1,0,0,23,0,1,0,0,0,24,1,0,0,0,0,24,0,0,0,0,1,24,1,0,0,0,0,25,0,0,0,0,1,24,0,0,24,1,0,0,0,24,0,1,0,0,0,33,0,0,0,16,1,0,0,24,1,0,0,0,26,1,0,23,25,0,0,25,1,0,24,0,1,0,0,24,1,0],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," `","run","_code","`"," program"," that",":\n","1","."," Calls"," `","bash","`"," tool"," twice"," -"," first"," with"," `","echo"," CODE","_","ONE","`,"," then"," with"," `","echo"," CODE","_T","WO","`\n","2","."," `","console",".log","`"," exactly"," `","capt","ured"," output","`\n","3","."," Returns"," the"," two"," outputs"," joined"," with"," a"," plus"," sign","\n\n","Let"," me"," think"," about"," the"," structure","."," The"," `","bash","`"," tool"," returns"," an"," object"," with"," stdout","/st","derr","."," I"," need"," to"," extract"," the"," stdout"," text"," from"," each"," call",".\n\n","Looking"," at"," the"," bash"," output"," type",":\n","```\n","{\n"," "," kind",":"," \"","fore","ground","\";\n"," "," exit","Code",":"," number"," |"," null",";\n"," "," signal",":"," string"," |"," null",";\n"," "," timed","Out",":"," boolean",";\n"," "," ab","orted",":"," boolean",";\n"," "," timeout","Ms",":"," number",";\n"," "," stdout",":"," {\n"," "," text",":"," string",";\n"," "," truncated",":"," boolean",";\n"," "," spill","Path","?:"," string",";\n"," "," };\n"," "," st","derr",":"," {"," ..."," };\n"," "," sand","box","?:"," {"," ..."," };\n","}\n","```\n\n","So"," I"," need"," to"," access"," `.","std","out",".text","`"," from"," each"," result",".\n\n","Let"," me"," write"," the"," program","."]}}
{"type":"assistant/chunk","seq":208,"time":1785014513974,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"tool-call-chunks","seq0":209,"time0":1785014513974,"data":{"turn":1,"step":1,"index":1,"dt":[24,1,0,0,0,24,1,0,24,0,0,40,10,26,0,25,1,0,0,24,1,0,0,0,29,0,0,0,0,0,20,1,0,0,0,24,0,0,0,0,1,25,0,1,0,26,0,0,0,0,0,23,0,0,0,0,0,25,0,0,0,0,0,24,0,0,0,0,1,25,0,0,0,0,0,24,1,0,0,0,0,31,1,17,1,24,0,25,1,0,0,0,24,1,0,0,0,25,25,25,0,0,0,0,0,26,0,0,0,0,1,23,0,1,0,0,0,24,1,24,1,0,24,1,0,0,0,25,0],"id":"call_00_D5QaUXWyA2cPRIFIT6o05977","name":"run_code","args":["","{","\"","description","\"",": ","\"","Call"," bash"," twice",","," log",","," join"," outputs","\"",", ","\"","code","\"",": ","\"","\\n","const"," r","1"," ="," await"," tools",".b","ash","({\\n"," "," command",":"," \\\"","echo"," CODE","_","ONE","\\\",\\n"," "," description",":"," \\\"","E","cho"," CODE","_","ONE","\\\"\\n","});\\n\\n","const"," r","2"," ="," await"," tools",".b","ash","({\\n"," "," command",":"," \\\"","echo"," CODE","_T","WO","\\\",\\n"," "," description",":"," \\\"","E","cho"," CODE","_T","WO","\\\"\\n","});\\n\\n","//"," Extract"," stdout"," text"," and"," trim"," trailing"," new","lines","\\n","const"," out","1"," ="," r","1",".stdout",".text",".trim","();\\n","const"," out","2"," ="," r","2",".stdout",".text",".trim","();\\n\\n","console",".log","(\\\"","capt","ured"," output","\\\");\\n\\n","return"," out","1"," +"," \\\"+","\\\""," +"," out","2",";\\n","\"","}"]}}
{"type":"assistant/chunk","seq":339,"time":1785014514829,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Calls `bash` tool twice - first with `echo CODE_ONE`, then with `echo CODE_TWO`\n2. `console.log` exactly `captured output`\n3. Returns the two outputs joined with a plus sign\n\nLet me think about the structure. The `bash` tool returns an object with stdout/stderr. I need to extract the stdout text from each call.\n\nLooking at the bash output type:\n```\n{\n kind: \"foreground\";\n exitCode: number | null;\n signal: string | null;\n timedOut: boolean;\n aborted: boolean;\n timeoutMs: number;\n stdout: {\n text: string;\n truncated: boolean;\n spillPath?: string;\n };\n stderr: { ... };\n sandbox?: { ... };\n}\n```\n\nSo I need to access `.stdout.text` from each result.\n\nLet me write the program."}}}}
{"type":"assistant/chunk","seq":340,"time":1785014514830,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_D5QaUXWyA2cPRIFIT6o05977","name":"run_code","arguments":"{\"description\": \"Call bash twice, log, join outputs\", \"code\": \"\\nconst r1 = await tools.bash({\\n command: \\\"echo CODE_ONE\\\",\\n description: \\\"Echo CODE_ONE\\\"\\n});\\n\\nconst r2 = await tools.bash({\\n command: \\\"echo CODE_TWO\\\",\\n description: \\\"Echo CODE_TWO\\\"\\n});\\n\\n// Extract stdout text and trim trailing newlines\\nconst out1 = r1.stdout.text.trim();\\nconst out2 = r2.stdout.text.trim();\\n\\nconsole.log(\\\"captured output\\\");\\n\\nreturn out1 + \\\"+\\\" + out2;\\n\"}"}}}}
{"type":"assistant/chunk","seq":341,"time":1785014514830,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":85,"outputTokens":373,"cacheReadTokens":3968,"reasoningTokens":202}}}}
{"type":"assistant/chunk","seq":342,"time":1785014514830,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":343,"time":1785014514837,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Calls `bash` tool twice - first with `echo CODE_ONE`, then with `echo CODE_TWO`\n2. `console.log` exactly `captured output`\n3. Returns the two outputs joined with a plus sign\n\nLet me think about the structure. The `bash` tool returns an object with stdout/stderr. I need to extract the stdout text from each call.\n\nLooking at the bash output type:\n```\n{\n kind: \"foreground\";\n exitCode: number | null;\n signal: string | null;\n timedOut: boolean;\n aborted: boolean;\n timeoutMs: number;\n stdout: {\n text: string;\n truncated: boolean;\n spillPath?: string;\n };\n stderr: { ... };\n sandbox?: { ... };\n}\n```\n\nSo I need to access `.stdout.text` from each result.\n\nLet me write the program."},{"type":"tool-call","id":"call_00_D5QaUXWyA2cPRIFIT6o05977","name":"run_code","arguments":"{\"description\": \"Call bash twice, log, join outputs\", \"code\": \"\\nconst r1 = await tools.bash({\\n command: \\\"echo CODE_ONE\\\",\\n description: \\\"Echo CODE_ONE\\\"\\n});\\n\\nconst r2 = await tools.bash({\\n command: \\\"echo CODE_TWO\\\",\\n description: \\\"Echo CODE_TWO\\\"\\n});\\n\\n// Extract stdout text and trim trailing newlines\\nconst out1 = r1.stdout.text.trim();\\nconst out2 = r2.stdout.text.trim();\\n\\nconsole.log(\\\"captured output\\\");\\n\\nreturn out1 + \\\"+\\\" + out2;\\n\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":85,"outputTokens":373,"cacheReadTokens":3968,"reasoningTokens":202}},"sourceEventSeqs":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342],"surfaceOp":"append"}
{"type":"tool/call","seq":344,"time":1785014514839,"data":{"turn":1,"step":1,"callId":"call_00_D5QaUXWyA2cPRIFIT6o05977","name":"run_code","arguments":"{\"description\": \"Call bash twice, log, join outputs\", \"code\": \"\\nconst r1 = await tools.bash({\\n command: \\\"echo CODE_ONE\\\",\\n description: \\\"Echo CODE_ONE\\\"\\n});\\n\\nconst r2 = await tools.bash({\\n command: \\\"echo CODE_TWO\\\",\\n description: \\\"Echo CODE_TWO\\\"\\n});\\n\\n// Extract stdout text and trim trailing newlines\\nconst out1 = r1.stdout.text.trim();\\nconst out2 = r2.stdout.text.trim();\\n\\nconsole.log(\\\"captured output\\\");\\n\\nreturn out1 + \\\"+\\\" + out2;\\n\"}"}}
{"type":"tool/code-dispatch-start","seq":345,"time":1785014514956,"data":{"parentCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977","subCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977:code:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Echo CODE_ONE"}}}
{"type":"tool/code-dispatch","seq":346,"time":1785014514990,"data":{"parentCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977","subCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977:code:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Echo CODE_ONE"},"isError":false,"content":[{"type":"text","text":"CODE_ONE\n"}]}}
{"type":"tool/code-dispatch-start","seq":347,"time":1785014514991,"data":{"parentCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977","subCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977:code:2","name":"bash","arguments":{"command":"echo CODE_TWO","description":"Echo CODE_TWO"}}}
{"type":"tool/code-dispatch","seq":348,"time":1785014515013,"data":{"parentCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977","subCallId":"call_00_D5QaUXWyA2cPRIFIT6o05977:code:2","name":"bash","arguments":{"command":"echo CODE_TWO","description":"Echo CODE_TWO"},"isError":false,"content":[{"type":"text","text":"CODE_TWO\n"}]}}
{"type":"tool/result","seq":349,"time":1785014515017,"data":{"turn":1,"step":1,"callId":"call_00_D5QaUXWyA2cPRIFIT6o05977","content":[{"type":"text","text":"captured output\nCODE_ONE+CODE_TWO"}],"isError":false},"sourceEventSeqs":[344],"surfaceOp":"append"}
{"type":"step/end","seq":350,"time":1785014515018,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":351,"time":1785014515022,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":352,"time":1785014515610,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":353,"time0":1785014515611,"data":{"turn":1,"step":2,"index":0,"dt":[116,25,0,26,1,0,25,26,1,0,0,26,1,0,0,25,0,0,26,0,0,26,0,0,0,1,1,24,0,1,0,0,25,26,0,0,0,0,2,24,0,1,0,25,1,0,0,0,25,1,0,0,0,25,0,0,0,0,0,26,1,0,0,0],"texts":["The"," program"," ran"," successfully","."," The"," output"," shows",":\n","-"," `","capt","ured"," output","`"," (","from"," console",".log",")\n","-"," `","CODE","_","ONE","+","CODE","_T","WO","`"," (","the"," returned"," joined"," string",")\n\n","The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only"," and"," stop","."," So"," I","'ll"," reply"," with"," just"," `","CODE","_","ONE","+","CODE","_T","WO","`."]}}
{"type":"assistant/chunk","seq":418,"time":1785014516169,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":419,"time0":1785014516169,"data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0,1,27],"texts":["CODE","_","ONE","+","CODE","_T","WO"]}}
{"type":"assistant/chunk","seq":426,"time":1785014516199,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The output shows:\n- `captured output` (from console.log)\n- `CODE_ONE+CODE_TWO` (the returned joined string)\n\nThe user asked me to reply with that joined string only and stop. So I'll reply with just `CODE_ONE+CODE_TWO`."}}}}
{"type":"assistant/chunk","seq":427,"time":1785014516200,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}}}
{"type":"assistant/chunk","seq":428,"time":1785014516200,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":97,"outputTokens":73,"cacheReadTokens":4352,"reasoningTokens":65}}}}
{"type":"assistant/chunk","seq":429,"time":1785014516200,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":430,"time":1785014516201,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The program ran successfully. The output shows:\n- `captured output` (from console.log)\n- `CODE_ONE+CODE_TWO` (the returned joined string)\n\nThe user asked me to reply with that joined string only and stop. So I'll reply with just `CODE_ONE+CODE_TWO`."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":97,"outputTokens":73,"cacheReadTokens":4352,"reasoningTokens":65}},"sourceEventSeqs":[352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429],"surfaceOp":"append"}
{"type":"step/end","seq":431,"time":1785014516202,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":432,"time":1785014516202,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,143 +0,0 @@
terminal 100x36 buffer=normal length=62 base=26 viewport=26
lifecycle started=1 stopped=0 progress=inactive
title "Using ONE run_code program: call — DSH TUI snapshot"
cursor hidden column=7 viewportRow=35 bufferRow=61
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Using ONE run_code program: call"
style 1-32 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Using ONE run_code program: call the bash tool twice — exactly echo CODE_ONE then exactly echo "
style 63-75 fg=cyan
style 90-99 fg=cyan
6| "CODE_TWO. Inside that same program, console.log exactly captured output, then return the two outputs"
style 0-7 fg=cyan
style 56-70 fg=cyan
7| "joined with a plus sign. Reply with that joined string only and stop. "
8| <blank>
9| "Assistant "
style 0-8 fg=bright-magenta bold underline
10| "Reasoning "
style 0-8 dim italic
11| "The user wants me to write a single run_code program that: "
style 0-35 dim italic
style 36-43 fg=cyan
style 44-57 dim italic
12| "1. Calls bash tool twice - first with echo CODE_ONE, then with echo CODE_TWO "
style 0-2 fg=bright-magenta
style 3-8 dim italic
style 9-12 fg=cyan
style 13-37 dim italic
style 38-50 fg=cyan
style 51-62 dim italic
style 63-75 fg=cyan
13| "2. console.log exactly captured output "
style 0-2 fg=bright-magenta
style 3-13 fg=cyan
style 14-22 dim italic
style 23-37 fg=cyan
14| "3. Returns the two outputs joined with a plus sign "
style 0-2 fg=bright-magenta
style 3-49 dim italic
15| " "
16| "Let me think about the structure. The bash tool returns an object with stdout/stderr. I need to "
style 0-37 dim italic
style 38-41 fg=cyan
style 42-99 dim italic
17| "extract the stdout text from each call. "
style 0-38 dim italic
18| " "
19| "Looking at the bash output type: "
style 0-31 dim italic
20| " "
21| " "
22| " { "
style 2-2 fg=cyan
23| " kind: \"foreground\"; "
style 2-22 fg=cyan
24| " exitCode: number | null; "
style 2-27 fg=cyan
25| " signal: string | null; "
style 2-25 fg=cyan
26| " timedOut: boolean; "
style 2-21 fg=cyan
27| " aborted: boolean; "
style 2-20 fg=cyan
28| " timeoutMs: number; "
style 2-21 fg=cyan
29| " stdout: { "
style 2-12 fg=cyan
30| " text: string; "
style 2-18 fg=cyan
31| " truncated: boolean; "
style 2-24 fg=cyan
32| " spillPath?: string; "
style 2-24 fg=cyan
33| " }; "
style 2-5 fg=cyan
34| " stderr: { ... }; "
style 2-19 fg=cyan
35| " sandbox?: { ... }; "
style 2-21 fg=cyan
36| " } "
style 2-2 fg=cyan
37| " "
38| " "
39| "So I need to access .stdout.text from each result. "
style 0-19 dim italic
style 20-31 fg=cyan
style 32-49 dim italic
40| " "
41| "Let me write the program. "
style 0-24 dim italic
42| <blank>
43| "● Tool / run_code"
style 0-16 fg=green
44| "Call bash twice, log, join outputs "
style 0-99 dim
45| "captured output "
style 0-99 dim
46| "CODE_ONE+CODE_TWO "
style 0-99 dim
47| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
48| <blank>
49| "Assistant "
style 0-8 fg=bright-magenta bold underline
50| "Reasoning "
style 0-8 dim italic
51| "The program ran successfully. The output shows: "
style 0-46 dim italic
52| "- captured output (from console.log) "
style 0-1 fg=bright-magenta
style 2-16 fg=cyan
style 17-35 dim italic
53| "- CODE_ONE+CODE_TWO (the returned joined string) "
style 0-1 fg=bright-magenta
style 2-18 fg=cyan
style 19-47 dim italic
54| " "
55| "The user asked me to reply with that joined string only and stop. So I'll reply with just "
style 0-99 dim italic
56| "CODE_ONE+CODE_TWO. "
style 0-16 fg=cyan
style 17-17 dim italic
57| "CODE_ONE+CODE_TWO "
58| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
59| <blank>
60| "/workspace/project deepseek-v4-flash ↑182 ↓446 cache 98% 4% context"
style 0-37 fg=bright-magenta bold
style 40-56 dim
style 59-78 dim
style 81-90 dim
61| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse

View File

@@ -1,13 +0,0 @@
{"type":"session","version":0,"id":"22222222-2222-4222-8222-222222222222","createdAt":1783950001000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783957884563,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783957884563,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783957884564,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783957884564,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783950001005,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":5,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
{"type":"assistant/chunk","seq":6,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":7,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":8,"time":1783957884564,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":9,"time":1783957884564,"data":{"turn":1,"step":1,"content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
{"type":"step/end","seq":10,"time":1783957884564,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":11,"time":1783957884564,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,13 +0,0 @@
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1783950002000,"cwd":"{{cwd}}","parentSession":"11111111-1111-4111-8111-111111111111","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783957884700,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783957884700,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783957884700,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783957884701,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783950002005,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":5,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
{"type":"assistant/chunk","seq":6,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":7,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":8,"time":1783957884701,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":9,"time":1783957884701,"data":{"turn":1,"step":1,"content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
{"type":"step/end","seq":10,"time":1783957884701,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":11,"time":1783957884701,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,64 +0,0 @@
{"type":"session","version":0,"id":"11111111-1111-4111-8111-111111111111","createdAt":1783950000000,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1783957884479,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783957884479,"data":{"content":[{"type":"text","text":"Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply with exactly ADVANCED_ACP_OK."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783957884486,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783957884486,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783950000005,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":5,"time":1783950000006,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-mount","name":"cordis_mount","argumentsDelta":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}}
{"type":"assistant/chunk","seq":6,"time":1783950000007,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}}}
{"type":"assistant/chunk","seq":7,"time":1783950000008,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":8,"time":1783950000009,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":9,"time":1783957884487,"data":{"turn":1,"step":1,"content":[{"type":"tool-call","id":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
{"type":"tool/call","seq":10,"time":1783957884487,"data":{"turn":1,"step":1,"callId":"advanced-mount","name":"cordis_mount","arguments":"{\"code\":\"return { name: 'snapshot-marker', apply() {} }\"}"}}
{"type":"tool/result","seq":11,"time":1783957884488,"data":{"turn":1,"step":1,"callId":"advanced-mount","content":[{"type":"text","text":"Temporary Plugin dyn-1 is running (plugin \"snapshot-marker\"; available until unmounted or DSH restarts)."}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
{"type":"step/end","seq":12,"time":1783957884489,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":13,"time":1783957884489,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":14,"time":1783950000015,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":15,"time":1783950000016,"data":{"turn":1,"step":2,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-code","name":"run_code","argumentsDelta":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Verify the temporary marker Plugin\"}"}}}
{"type":"assistant/chunk","seq":16,"time":1783950000017,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Verify the temporary marker Plugin\"}"}}}}
{"type":"assistant/chunk","seq":17,"time":1783950000018,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":18,"time":1783950000019,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":19,"time":1783957884490,"data":{"turn":1,"step":2,"content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Verify the temporary marker Plugin\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[14,15,16,17,18],"surfaceOp":"append"}
{"type":"tool/call","seq":20,"time":1783957884490,"data":{"turn":1,"step":2,"callId":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.cordis_inspect({ what: 'temporary' })\", \"description\": \"Verify the temporary marker Plugin\"}"}}
{"type":"tool/code-dispatch","seq":21,"time":1783957884560,"data":{"parentCallId":"advanced-code","subCallId":"advanced-code:code:1","name":"cordis_inspect","arguments":{"what":"temporary"},"isError":false,"resultSummary":"## dynamic\n- dyn-1: snapshot-marker [active]"}}
{"type":"tool/result","seq":22,"time":1783957884561,"data":{"turn":1,"step":2,"callId":"advanced-code","content":[{"type":"text","text":"## dynamic\n- dyn-1: snapshot-marker [active]"}],"isError":false,"meta":{"logs":[]}},"sourceEventSeqs":[20],"surfaceOp":"append"}
{"type":"step/end","seq":23,"time":1783957884561,"data":{"turn":1,"step":2}}
{"type":"step/start","seq":24,"time":1783957884562,"data":{"turn":1,"step":3}}
{"type":"assistant/chunk","seq":25,"time":1783950000026,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":26,"time":1783950000027,"data":{"turn":1,"step":3,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-direct-child","name":"subagent","argumentsDelta":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}}
{"type":"assistant/chunk","seq":27,"time":1783950000028,"data":{"turn":1,"step":3,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}}}
{"type":"assistant/chunk","seq":28,"time":1783950000029,"data":{"turn":1,"step":3,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":29,"time":1783950000030,"data":{"turn":1,"step":3,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":30,"time":1783957884562,"data":{"turn":1,"step":3,"content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[25,26,27,28,29],"surfaceOp":"append"}
{"type":"tool/call","seq":31,"time":1783957884562,"data":{"turn":1,"step":3,"callId":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}
{"type":"tool/result","seq":32,"time":1783957884593,"data":{"turn":1,"step":3,"callId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false},"sourceEventSeqs":[31],"surfaceOp":"append"}
{"type":"step/end","seq":33,"time":1783957884593,"data":{"turn":1,"step":3}}
{"type":"step/start","seq":34,"time":1783957884594,"data":{"turn":1,"step":4}}
{"type":"assistant/chunk","seq":35,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":36,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-workflow","name":"workflow","argumentsDelta":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}}}
{"type":"assistant/chunk","seq":37,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}}}}
{"type":"assistant/chunk","seq":38,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":39,"time":1783957884594,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":40,"time":1783957884594,"data":{"turn":1,"step":4,"content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[35,36,37,38,39],"surfaceOp":"append"}
{"type":"tool/call","seq":41,"time":1783957884594,"data":{"turn":1,"step":4,"callId":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}}
{"type":"tool/result","seq":42,"time":1783957884717,"data":{"turn":1,"step":4,"callId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-acp-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false},"sourceEventSeqs":[41],"surfaceOp":"append"}
{"type":"step/end","seq":43,"time":1783957884718,"data":{"turn":1,"step":4}}
{"type":"step/start","seq":44,"time":1783957884718,"data":{"turn":1,"step":5}}
{"type":"assistant/chunk","seq":45,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":46,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-unmount","name":"cordis_unmount","argumentsDelta":"{\"id\":\"dyn-1\"}"}}}
{"type":"assistant/chunk","seq":47,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}}}}
{"type":"assistant/chunk","seq":48,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":49,"time":1783957884719,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":50,"time":1783957884719,"data":{"turn":1,"step":5,"content":[{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[45,46,47,48,49],"surfaceOp":"append"}
{"type":"tool/call","seq":51,"time":1783957884719,"data":{"turn":1,"step":5,"callId":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\":\"dyn-1\"}"}}
{"type":"tool/result","seq":52,"time":1783957884719,"data":{"turn":1,"step":5,"callId":"advanced-unmount","content":[{"type":"text","text":"Temporary Plugin dyn-1 was unmounted and removed."}],"isError":false},"sourceEventSeqs":[51],"surfaceOp":"append"}
{"type":"step/end","seq":53,"time":1783957884719,"data":{"turn":1,"step":5}}
{"type":"step/start","seq":54,"time":1783957884720,"data":{"turn":1,"step":6}}
{"type":"assistant/chunk","seq":55,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":56,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"text-delta","index":0,"text":"ADVANCED_ACP_OK"}}}
{"type":"assistant/chunk","seq":57,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_ACP_OK"}}}}
{"type":"assistant/chunk","seq":58,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
{"type":"assistant/chunk","seq":59,"time":1783957884720,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":60,"time":1783957884720,"data":{"turn":1,"step":6,"content":[{"type":"text","text":"ADVANCED_ACP_OK"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[55,56,57,58,59],"surfaceOp":"append"}
{"type":"step/end","seq":61,"time":1783957884721,"data":{"turn":1,"step":6}}
{"type":"turn/end","seq":62,"time":1783957884721,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,110 +0,0 @@
terminal 100x36 buffer=normal length=59 base=23 viewport=23
lifecycle started=1 stopped=0 progress=inactive
title "Run this advanced flow exactly — DSH TUI snapshot"
cursor hidden column=7 viewportRow=35 bufferRow=58
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Run this advanced flow exactly"
style 1-30 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Run this advanced flow exactly once: try a no-op temporary Cordis Plugin named snapshot-marker; use "
6| "run_code to inspect the live temporary Plugins through tools.cordis_inspect; delegate once to a "
7| "direct spawn child; run one workflow that delegates to another spawn child; stop dyn-1; then reply "
8| "with exactly ADVANCED_ACP_OK. "
9| <blank>
10| "Assistant "
style 0-8 fg=bright-magenta bold underline
11| <blank>
12| "● Tool / cordis_mount"
style 0-20 fg=green
13| "Mount temporary Cordis Plugin "
style 0-99 dim
14| "Temporary Plugin dyn-1 is running (plugin \"snapshot-marker\"; available until unmounted or DSH "
style 0-99 dim
15| "restarts). "
style 0-99 dim
16| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
17| <blank>
18| "Assistant "
style 0-8 fg=bright-magenta bold underline
19| <blank>
20| "● Tool / run_code"
style 0-16 fg=green
21| "Verify the temporary marker Plugin "
style 0-99 dim
22| " "
23| "Temporary Plugins "
style 0-16 fg=bright-magenta bold dim
24| " "
25| "- Temporary Plugin dyn-1: snapshot-marker [running] — provides: none; waiting for: none; lifetime: "
style 0-1 fg=bright-magenta dim
style 2-99 dim
26| " until unmounted or DSH restarts "
style 0-99 dim
27| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
28| <blank>
29| "Assistant "
style 0-8 fg=bright-magenta bold underline
30| <blank>
31| "● Tool / subagent"
style 0-16 fg=green
32| "DIRECT_CHILD_OK "
style 0-99 dim
33| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
34| <blank>
35| "Assistant "
style 0-8 fg=bright-magenta bold underline
36| <blank>
37| "● Tool / workflow"
style 0-16 fg=green
38| "workflow: advanced-acp-snapshot "
style 0-99 dim
39| "workflow \"advanced-acp-snapshot\" completed (1 agent). "
style 0-99 dim
40| "Return value: "
style 0-99 dim
41| "{ "
style 0-99 dim
42| " \"reply\": \"WORKFLOW_CHILD_OK\" "
style 0-99 dim
43| "} "
style 0-99 dim
44| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
45| <blank>
46| "Assistant "
style 0-8 fg=bright-magenta bold underline
47| <blank>
48| "● Tool / cordis_unmount"
style 0-22 fg=green
49| "Unmount temporary Cordis Plugin dyn-1 "
style 0-99 dim
50| "Temporary Plugin dyn-1 was unmounted and removed. "
style 0-99 dim
51| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
52| <blank>
53| "Assistant "
style 0-8 fg=bright-magenta bold underline
54| "ADVANCED_ACP_OK "
55| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
56| <blank>
57| "/workspace/project deepseek-v4-flash ↑18 ↓18 cache 0% 8% cont"
style 0-52 fg=bright-magenta bold
style 55-71 dim
style 74-90 dim
style 93-99 dim
58| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse

View File

@@ -1,16 +0,0 @@
{"type":"session","version":0,"id":"583a4db2-3350-436c-b4a5-5615fd159052","createdAt":1783600636316,"cwd":"{{cwd}}","parentSession":"3fd7d599-56b1-493a-930d-f1fc5e1556e8","delegationDepth":1}
{"type":"turn/start","seq":0,"time":1783600636316,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783600636316,"data":{"content":[{"type":"text","text":"Reply with exactly the word WF_CHILD_OK and nothing else."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783600636316,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783600636317,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783600638073,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":5,"time0":1783600638073,"data":{"turn":1,"step":1,"index":0,"dt":[100,16,0,0,0,0,24,0,0,0,0,29,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," \"","WF","_CH","ILD","_OK","\""," and"," nothing"," else","."]}}
{"type":"assistant/chunk","seq":23,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":24,"time0":1783600638276,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,0],"texts":["WF","_CH","ILD","_OK"]}}
{"type":"assistant/chunk","seq":28,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."}}}}
{"type":"assistant/chunk","seq":29,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"WF_CHILD_OK"}}}}
{"type":"assistant/chunk","seq":30,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":31,"time":1783600638280,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":32,"time":1783600638281,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly \"WF_CHILD_OK\" and nothing else."},{"type":"text","text":"WF_CHILD_OK"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":17,"outputTokens":23,"cacheReadTokens":3072,"reasoningTokens":18}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"surfaceOp":"append"}
{"type":"step/end","seq":33,"time":1783600638281,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":34,"time":1783600638281,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,29 +0,0 @@
{"type":"session","version":0,"id":"3fd7d599-56b1-493a-930d-f1fc5e1556e8","createdAt":1783600631835,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1783600631838,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783600631838,"data":{"content":[{"type":"text","text":"Use the workflow tool exactly once, with args omitted, meta set to { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" }, and this EXACT script body (copy it verbatim):\nphase('Run')\nconst reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.')\nreturn { reply }\nAfter the workflow returns, reply with the single word WORKFLOW_DONE and stop. Do not use any other tool."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783600631839,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783600631839,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783600634643,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":5,"time0":1783600634643,"data":{"turn":1,"step":1,"index":0,"dt":[991,0,0,0,0,0,0,0,0,0,1,0,0,0,108,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,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,1,0,0,0,8,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," use"," the"," workflow"," tool"," exactly"," once"," with"," specific"," parameters","."," Let"," me"," carefully"," follow"," the"," instructions",":\n\n","1","."," args"," omitted"," (","so"," I"," don","'t"," include"," it",")\n","2","."," meta"," ="," {"," \"","name","\":"," \"","sn","apshot","-flow","\","," \"","description","\":"," \"","one"," child"," for"," the"," snapshot","\""," }\n","3","."," script"," ="," as"," given"," verb","atim","\n","4","."," After"," it"," returns",","," reply"," with"," \"","WORK","FL","OW","_D","ONE","\"\n\n","Let"," me"," do"," exactly"," that","."]}}
{"type":"assistant/chunk","seq":93,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"tool-call-chunks","seq0":94,"time0":1783600635756,"data":{"turn":1,"step":1,"index":1,"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,2,0,0,0,0,5,0,275,0,0,0,0,206,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0],"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","args":["","{","\"","meta","\"",": ","{\"","name","\":"," \"","sn","apshot","-flow","\","," \"","description","\":"," \"","one"," child"," for"," the"," snapshot","\"","}",", ","\"","script","\"",": ","\"","\\n","phase","('","Run","')\\n","const"," reply"," ="," await"," agent","('","Reply"," with"," exactly"," the"," word"," WF","_CH","ILD","_OK"," and"," nothing"," else",".')\\n","return"," {"," reply"," }\\n","\"","}"]}}
{"type":"assistant/chunk","seq":155,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully follow the instructions:\n\n1. args omitted (so I don't include it)\n2. meta = { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" }\n3. script = as given verbatim\n4. After it returns, reply with \"WORKFLOW_DONE\"\n\nLet me do exactly that."}}}}
{"type":"assistant/chunk","seq":156,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","arguments":"{\"meta\": {\"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\"}, \"script\": \"\\nphase('Run')\\nconst reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.')\\nreturn { reply }\\n\"}"}}}}
{"type":"assistant/chunk","seq":157,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3174,"outputTokens":191,"cacheReadTokens":0,"reasoningTokens":88}}}}
{"type":"assistant/chunk","seq":158,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":159,"time":1783600636247,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully follow the instructions:\n\n1. args omitted (so I don't include it)\n2. meta = { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" }\n3. script = as given verbatim\n4. After it returns, reply with \"WORKFLOW_DONE\"\n\nLet me do exactly that."},{"type":"tool-call","id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","arguments":"{\"meta\": {\"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\"}, \"script\": \"\\nphase('Run')\\nconst reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.')\\nreturn { reply }\\n\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3174,"outputTokens":191,"cacheReadTokens":0,"reasoningTokens":88}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158],"surfaceOp":"append"}
{"type":"tool/call","seq":160,"time":1783600636247,"data":{"turn":1,"step":1,"callId":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","arguments":"{\"meta\": {\"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\"}, \"script\": \"\\nphase('Run')\\nconst reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.')\\nreturn { reply }\\n\"}"}}
{"type":"tool/result","seq":161,"time":1783600638304,"data":{"turn":1,"step":1,"callId":"call_00_dD2BLuNeJCTh2iiYC1QR3449","content":[{"type":"text","text":"workflow \"snapshot-flow\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WF_CHILD_OK\"\n}"}],"isError":false},"sourceEventSeqs":[160],"surfaceOp":"append"}
{"type":"step/end","seq":162,"time":1783600638304,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":163,"time":1783600638305,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":164,"time":1783600640028,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":165,"time0":1783600640028,"data":{"turn":1,"step":2,"index":0,"dt":[106,28,33,667,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,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","\""," and"," stop","."]}}
{"type":"assistant/chunk","seq":195,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"text-chunks","seq0":196,"time0":1783600640865,"data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0],"texts":["WORK","FL","OW","_D","ONE"]}}
{"type":"assistant/chunk","seq":201,"time":1783600640865,"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."}}}}
{"type":"assistant/chunk","seq":202,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"WORKFLOW_DONE"}}}}
{"type":"assistant/chunk","seq":203,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":328,"outputTokens":36,"cacheReadTokens":3072,"reasoningTokens":30}}}}
{"type":"assistant/chunk","seq":204,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":205,"time":1783600640865,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The workflow returned successfully with the reply \"WF_CHILD_OK\". Now I need to reply with exactly \"WORKFLOW_DONE\" and stop."},{"type":"text","text":"WORKFLOW_DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":328,"outputTokens":36,"cacheReadTokens":3072,"reasoningTokens":30}},"sourceEventSeqs":[164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204],"surfaceOp":"append"}
{"type":"step/end","seq":206,"time":1783600640865,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":207,"time":1783600640865,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,86 +0,0 @@
terminal 100x36 buffer=normal length=43 base=7 viewport=7
lifecycle started=1 stopped=0 progress=inactive
title "Use the workflow tool exactly — DSH TUI snapshot"
cursor hidden column=7 viewportRow=35 bufferRow=42
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Use the workflow tool exactly"
style 1-29 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Use the workflow tool exactly once, with args omitted, meta set to { \"name\": \"snapshot-flow\", "
6| "\"description\": \"one child for the snapshot\" }, and this EXACT script body (copy it verbatim): "
7| "phase('Run') "
8| "const reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.') "
9| "return { reply } "
10| "After the workflow returns, reply with the single word WORKFLOW_DONE and stop. Do not use any other "
11| "tool. "
12| <blank>
13| "Assistant "
style 0-8 fg=bright-magenta bold underline
14| "Reasoning "
style 0-8 dim italic
15| "The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully "
style 0-99 dim italic
16| "follow the instructions: "
style 0-23 dim italic
17| " "
18| "1. args omitted (so I don't include it) "
style 0-2 fg=bright-magenta
style 3-38 dim italic
19| "2. meta = { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" } "
style 0-2 fg=bright-magenta
style 3-81 dim italic
20| "3. script = as given verbatim "
style 0-2 fg=bright-magenta
style 3-28 dim italic
21| "4. After it returns, reply with \"WORKFLOW_DONE\" "
style 0-2 fg=bright-magenta
style 3-46 dim italic
22| " "
23| "Let me do exactly that. "
style 0-22 dim italic
24| <blank>
25| "● Tool / workflow"
style 0-16 fg=green
26| "workflow: snapshot-flow "
style 0-99 dim
27| "workflow \"snapshot-flow\" completed (1 agent). "
style 0-99 dim
28| "Return value: "
style 0-99 dim
29| "{ "
style 0-99 dim
30| " \"reply\": \"WF_CHILD_OK\" "
style 0-99 dim
31| "} "
style 0-99 dim
32| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
33| <blank>
34| "Assistant "
style 0-8 fg=bright-magenta bold underline
35| "Reasoning "
style 0-8 dim italic
36| "The workflow returned successfully with the reply \"WF_CHILD_OK\". Now I need to reply with exactly "
style 0-99 dim italic
37| "\"WORKFLOW_DONE\" and stop. "
style 0-24 dim italic
38| "WORKFLOW_DONE "
39| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
40| <blank>
41| "/workspace/project deepseek-v4-flash ↑3.5k ↓227 cache 47% 3% context"
style 0-44 fg=bright-magenta bold
style 47-63 dim
style 66-86 dim
style 89-98 dim
42| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse

View File

@@ -1,31 +0,0 @@
{"type":"session","version":0,"id":"228b7b82-84ed-49b7-a567-981c03b28c77","createdAt":1783352113760,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1783352113765,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352113765,"data":{"content":[{"type":"text","text":"Reply with exactly the word: ONE. No tools."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783352113767,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783352113768,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783352114428,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":5,"time0":1783352114428,"data":{"turn":1,"step":1,"index":0,"dt":[114,28,1,0,0,1,28,1,1,0,0,1,24,1,29,1,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","ONE","\""," and"," use"," no"," tools","."]}}
{"type":"assistant/chunk","seq":23,"time":1783352114658,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":24,"time":1783352114658,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
{"type":"assistant/chunk","seq":25,"time":1783352114687,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and use no tools."}}}}
{"type":"assistant/chunk","seq":26,"time":1783352114687,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"ONE"}}}}
{"type":"assistant/chunk","seq":27,"time":1783352114687,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2864,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":28,"time":1783352114687,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":29,"time":1783352114690,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"ONE\" and use no tools."},{"type":"text","text":"ONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":2864,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":18}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
{"type":"step/end","seq":30,"time":1783352114690,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":31,"time":1783352114690,"data":{"turn":1,"reason":{"kind":"completed"}}}
{"type":"turn/start","seq":32,"time":1783352114699,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":33,"time":1783352114699,"data":{"content":[{"type":"text","text":"Reply with exactly the word: TWO. No tools."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":34,"time":1783352114700,"data":{"turn":2,"step":1}}
{"type":"assistant/chunk","seq":35,"time":1783352115341,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":36,"time0":1783352115341,"data":{"turn":2,"step":1,"index":0,"dt":[124,27,1,0,0,28,0,0,31,0,0,0,0,28,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","T","WO","\""," and"," no"," tools","."]}}
{"type":"assistant/chunk","seq":54,"time":1783352115609,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":55,"time":1783352115609,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"T"}}}
{"type":"assistant/chunk","seq":56,"time":1783352115609,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":1,"text":"WO"}}}
{"type":"assistant/chunk","seq":57,"time":1783352115610,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with exactly the word \"TWO\" and no tools."}}}}
{"type":"assistant/chunk","seq":58,"time":1783352115610,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"TWO"}}}}
{"type":"assistant/chunk","seq":59,"time":1783352115610,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":64,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}}}}
{"type":"assistant/chunk","seq":60,"time":1783352115610,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":61,"time":1783352115611,"data":{"turn":2,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"TWO\" and no tools."},{"type":"text","text":"TWO"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":64,"outputTokens":21,"cacheReadTokens":2816,"reasoningTokens":18}},"sourceEventSeqs":[35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"surfaceOp":"append"}
{"type":"step/end","seq":62,"time":1783352115611,"data":{"turn":2,"step":1}}
{"type":"turn/end","seq":63,"time":1783352115611,"data":{"turn":2,"reason":{"kind":"completed"}}}

View File

@@ -1,62 +0,0 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Reply with exactly the word: — DSH TUI snapshot"
cursor hidden column=7 viewportRow=30 bufferRow=30
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Reply with exactly the word:"
style 1-28 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Reply with exactly the word: ONE. No tools. "
6| <blank>
7| "Plan mode on. Use /plan off to leave. "
style 0-36 dim
8| <blank>
9| "Assistant "
style 0-8 fg=bright-magenta bold underline
10| "Reasoning "
style 0-8 dim italic
11| "The user wants me to reply with exactly the word \"ONE\" and use no tools. "
style 0-71 dim italic
12| "ONE "
13| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
14| <blank>
15| "Context · plan-mode"
style 0-18 dim
16| "The user switched this session back to the default mode. "
style 0-55 dim
17| <blank>
18| "Plan mode off. "
style 0-13 dim
19| <blank>
20| "You "
style 0-2 fg=bright-magenta bold underline
21| "Reply with exactly the word: TWO. No tools. "
22| <blank>
23| "Assistant "
style 0-8 fg=bright-magenta bold underline
24| "Reasoning "
style 0-8 dim italic
25| "The user wants me to reply with exactly the word \"TWO\" and no tools. "
style 0-67 dim italic
26| "TWO "
27| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
28| <blank>
29| "/workspace/project deepseek-v4-flash ↑2.9k ↓41 cache 49% 3% co"
style 0-51 fg=bright-magenta bold
style 54-70 dim
style 73-92 dim
style 95-99 dim
30| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
31-35| <blank>

View File

@@ -1,28 +0,0 @@
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"Use the read tool twice in the same assistant message: read a.txt and b.txt. Then reply DONE."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":0,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":5,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_read_a","name":"read","argumentsDelta":"{\"file_path\":\"a.txt\"}"}}}
{"type":"assistant/chunk","seq":6,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_read_a","name":"read","arguments":"{\"file_path\":\"a.txt\"}"}}}}
{"type":"assistant/chunk","seq":7,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_read_b","name":"read","argumentsDelta":"{\"file_path\":\"b.txt\"}"}}}
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_read_b","name":"read","arguments":"{\"file_path\":\"b.txt\"}"}}}}
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":12,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"tool-call","id":"call_read_a","name":"read","arguments":"{\"file_path\":\"a.txt\"}"},{"type":"tool-call","id":"call_read_b","name":"read","arguments":"{\"file_path\":\"b.txt\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[4,5,6,7,8,9,10,11],"surfaceOp":"append"}
{"type":"tool/call","seq":13,"time":0,"data":{"turn":1,"step":1,"callId":"call_read_a","name":"read","arguments":"{\"file_path\":\"a.txt\"}"}}
{"type":"tool/call","seq":14,"time":0,"data":{"turn":1,"step":1,"callId":"call_read_b","name":"read","arguments":"{\"file_path\":\"b.txt\"}"}}
{"type":"tool/result","seq":15,"time":0,"data":{"turn":1,"step":1,"callId":"call_read_a","content":[{"type":"text","text":"<path>{{cwd}}/a.txt</path>\n<type>file</type>\n<content>\n1: alpha\n\n(End of file - total 1 lines)\n</content>"}],"isError":false},"sourceEventSeqs":[13],"surfaceOp":"append"}
{"type":"tool/result","seq":16,"time":0,"data":{"turn":1,"step":1,"callId":"call_read_b","content":[{"type":"text","text":"<path>{{cwd}}/b.txt</path>\n<type>file</type>\n<content>\n1: beta\n\n(End of file - total 1 lines)\n</content>"}],"isError":false},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","seq":17,"time":0,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":18,"time":0,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":19,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
{"type":"assistant/chunk","seq":20,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"DONE"}}}
{"type":"assistant/chunk","seq":21,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}}}
{"type":"assistant/chunk","seq":22,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":1}}}}
{"type":"assistant/chunk","seq":23,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":24,"time":0,"data":{"turn":1,"step":2,"content":[{"type":"text","text":"DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":10,"outputTokens":1}},"sourceEventSeqs":[19,20,21,22,23],"surfaceOp":"append"}
{"type":"step/end","seq":25,"time":0,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":26,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,58 +0,0 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Use the read tool twice — DSH TUI snapshot"
cursor hidden column=7 viewportRow=27 bufferRow=27
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Use the read tool twice"
style 1-23 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Use the read tool twice in the same assistant message: read a.txt and b.txt. Then reply DONE. "
6| <blank>
7| "Assistant "
style 0-8 fg=bright-magenta bold underline
8| <blank>
9| "● Tool / read"
style 0-12 fg=green
10| "Read a.txt "
style 0-99 dim
11| "1: alpha "
style 0-99 dim
12| " "
13| "(End of file - total 1 lines) "
style 0-99 dim
14| <blank>
15| "● Tool / read"
style 0-12 fg=green
16| "Read b.txt "
style 0-99 dim
17| "1: beta "
style 0-99 dim
18| " "
19| "(End of file - total 1 lines) "
style 0-99 dim
20| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
21| <blank>
22| "Assistant "
style 0-8 fg=bright-magenta bold underline
23| "DONE "
24| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
25| <blank>
26| "/workspace/project deepseek-v4-flash ↑20 ↓6 cache 0% 3% context"
style 0-47 fg=bright-magenta bold
style 50-66 dim
style 69-84 dim
style 87-96 dim
27| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
28-35| <blank>

View File

@@ -1,31 +0,0 @@
{"type":"session","version":0,"id":"b0f1f758-dcf0-474e-851d-e62c11ec0a09","createdAt":1783352057652,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"turn/start","seq":0,"time":1783352057655,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
{"type":"user/message","seq":1,"time":1783352057655,"data":{"content":[{"type":"text","text":"Use the todo_write tool to record a plan with exactly three todos: \"read the code\" (in_progress), \"write the fix\" (pending), \"run the tests\" (pending). Send all three in one todo_write call. Then reply with the single word DONE and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
{"type":"step/start","seq":2,"time":1783352057657,"data":{"turn":1,"step":1}}
{"type":"request/header","seq":3,"time":1783352057657,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"assistant/chunk","seq":4,"time":1783352058320,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":5,"time0":1783352058320,"data":{"turn":1,"step":1,"index":0,"dt":[106,40,1,0,0,0,17,0,0,0,1,26,1,1,0,0,1,26,0,31,1,25,0,0,0,29,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," use"," the"," todo","_write"," tool"," to"," record"," a"," plan"," with"," exactly"," three"," todos"," in"," the"," specified"," status","es",","," then"," reply"," with"," \"","D","ONE","\"."]}}
{"type":"assistant/chunk","seq":36,"time":1783352058717,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
{"type":"tool-call-chunks","seq0":37,"time0":1783352058717,"data":{"turn":1,"step":1,"index":1,"dt":[29,1,0,0,28,0,1,29,1,0,0,0,0,26,0,0,0,0,0,30,1,0,0,0,0,26,1,0,0,0,0,28,0,0,0,0,0,29,0,0,0,0,1,28,0,0,0,1,0,27,1,0,28],"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","args":["","{","\"","t","odos","\"",": ","[","{\"","content","\":"," \"","read"," the"," code","\","," \"","status","\":"," \"","in","_pro","gress","\"},"," {\"","content","\":"," \"","write"," the"," fix","\","," \"","status","\":"," \"","pending","\"},"," {\"","content","\":"," \"","run"," the"," tests","\","," \"","status","\":"," \"","pending","\"","}]","}"]}}
{"type":"assistant/chunk","seq":91,"time":1783352059095,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use the todo_write tool to record a plan with exactly three todos in the specified statuses, then reply with \"DONE\"."}}}}
{"type":"assistant/chunk","seq":92,"time":1783352059096,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","arguments":"{\"todos\": [{\"content\": \"read the code\", \"status\": \"in_progress\"}, {\"content\": \"write the fix\", \"status\": \"pending\"}, {\"content\": \"run the tests\", \"status\": \"pending\"}]}"}}}}
{"type":"assistant/chunk","seq":93,"time":1783352059096,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2913,"outputTokens":121,"cacheReadTokens":0,"reasoningTokens":31}}}}
{"type":"assistant/chunk","seq":94,"time":1783352059096,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
{"type":"assistant/message","seq":95,"time":1783352059099,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to use the todo_write tool to record a plan with exactly three todos in the specified statuses, then reply with \"DONE\"."},{"type":"tool-call","id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","arguments":"{\"todos\": [{\"content\": \"read the code\", \"status\": \"in_progress\"}, {\"content\": \"write the fix\", \"status\": \"pending\"}, {\"content\": \"run the tests\", \"status\": \"pending\"}]}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":2913,"outputTokens":121,"cacheReadTokens":0,"reasoningTokens":31}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94],"surfaceOp":"append"}
{"type":"tool/call","seq":96,"time":1783352059099,"data":{"turn":1,"step":1,"callId":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","arguments":"{\"todos\": [{\"content\": \"read the code\", \"status\": \"in_progress\"}, {\"content\": \"write the fix\", \"status\": \"pending\"}, {\"content\": \"run the tests\", \"status\": \"pending\"}]}"}}
{"type":"todo/write","seq":97,"time":1783352059100,"data":{"todos":[{"content":"read the code","status":"in_progress"},{"content":"write the fix","status":"pending"},{"content":"run the tests","status":"pending"}]}}
{"type":"tool/result","seq":98,"time":1783352059101,"data":{"turn":1,"step":1,"callId":"call_00_fjAnBThbDjxepBtp3hDt3264","content":[{"type":"text","text":"Updated todo list: 2 pending, 1 in progress, 0 completed."}],"isError":false},"sourceEventSeqs":[96],"surfaceOp":"append"}
{"type":"step/end","seq":99,"time":1783352059101,"data":{"turn":1,"step":1}}
{"type":"step/start","seq":100,"time":1783352059102,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":101,"time":1783352059732,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":102,"time0":1783352059733,"data":{"turn":1,"step":2,"index":0,"dt":[102,28,0,1,0,28,0,1,0,27,0,1,0,0,29,0,0,0,1,0],"texts":["The"," todos"," have"," been"," written"," successfully","."," Now"," I"," just"," need"," to"," reply"," with"," the"," single"," word"," \"","D","ONE","\"."]}}
{"type":"assistant/chunk","seq":123,"time":1783352059979,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":124,"time":1783352059979,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
{"type":"assistant/chunk","seq":125,"time":1783352059980,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
{"type":"assistant/chunk","seq":126,"time":1783352059980,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The todos have been written successfully. Now I just need to reply with the single word \"DONE\"."}}}}
{"type":"assistant/chunk","seq":127,"time":1783352059980,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
{"type":"assistant/chunk","seq":128,"time":1783352059980,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":237,"outputTokens":24,"cacheReadTokens":2816,"reasoningTokens":21}}}}
{"type":"assistant/chunk","seq":129,"time":1783352059980,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":130,"time":1783352059981,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The todos have been written successfully. Now I just need to reply with the single word \"DONE\"."},{"type":"text","text":"DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":237,"outputTokens":24,"cacheReadTokens":2816,"reasoningTokens":21}},"sourceEventSeqs":[101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129],"surfaceOp":"append"}
{"type":"step/end","seq":131,"time":1783352059981,"data":{"turn":1,"step":2}}
{"type":"turn/end","seq":132,"time":1783352059981,"data":{"turn":1,"reason":{"kind":"completed"}}}

View File

@@ -1,65 +0,0 @@
terminal 100x36 buffer=normal length=36 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Use the todo_write tool to — DSH TUI snapshot"
cursor hidden column=7 viewportRow=31 bufferRow=31
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Use the todo_write tool to"
style 1-26 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "You "
style 0-2 fg=bright-magenta bold underline
5| "Use the todo_write tool to record a plan with exactly three todos: \"read the code\" (in_progress), "
6| "\"write the fix\" (pending), \"run the tests\" (pending). Send all three in one todo_write call. Then "
7| "reply with the single word DONE and stop. "
8| <blank>
9| "Assistant "
style 0-8 fg=bright-magenta bold underline
10| "Reasoning "
style 0-8 dim italic
11| "The user wants me to use the todo_write tool to record a plan with exactly three todos in the "
style 0-99 dim italic
12| "specified statuses, then reply with \"DONE\". "
style 0-42 dim italic
13| <blank>
14| "● Tool / todo_write"
style 0-18 fg=green
15| "Update todo list "
style 0-99 dim
16| "Updated todo list: 2 pending, 1 in progress, 0 completed. "
style 0-99 dim
17| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
18| <blank>
19| "Assistant "
style 0-8 fg=bright-magenta bold underline
20| "Reasoning "
style 0-8 dim italic
21| "The todos have been written successfully. Now I just need to reply with the single word \"DONE\". "
style 0-94 dim italic
22| "DONE "
23| "Model wait 0.0s · Completed 2026-07-21 12:00:00 "
style 0-46 dim
24-25| <blank>
26| "Plan"
style 0-3 fg=bright-magenta bold
27| " ● read the code"
style 2-2 fg=yellow
28| " ○ write the fix"
style 2-2 dim
29| " ○ run the tests"
style 2-2 dim
30| "/workspace/project deepseek-v4-flash ↑3.1k ↓145 cache 47% 3% context"
style 0-37 fg=bright-magenta bold
style 40-56 dim
style 59-79 dim
style 82-91 dim
31| " dsh ◍ "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
32-35| <blank>

View File

@@ -1,440 +0,0 @@
import { createUserMessage, createMessage } from '@deepseek-ai/dsh-llm'
import { realpathSync } from 'node:fs'
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { LOADER_SMOKE_TEST_TIMEOUT_MS } from '@deepseek-ai/dsh-loader-smoke'
import { packChunkRuns, SessionId, type SessionEvent, type SessionHeader } from '@deepseek-ai/dsh-session'
import { logPath, toHeaderLine } from '../../../packages/session-persistence/session-persistence-jsonl/src/format.ts'
import { runTuiPtySmoke, type TuiPtySmokeOptions } from './pty-harness.ts'
const dshBinScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', import.meta.url))
const configPath = fileURLToPath(new URL('../cordis.yml', import.meta.url))
const codeModeConfigPath = fileURLToPath(new URL('../code-mode.cordis.yml', import.meta.url))
const scriptedConfigPath = fileURLToPath(new URL('./fixtures/tui-scripted.cordis.yml', import.meta.url))
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
/**
* Seed the isolated process workspace: ordinary files land in `cwd`, personal
* files in the Harness home (`.dsh`), and skill bundles under the agents
* home's `skills/` root — the same trees `$DSH_HOME` /
* `$DSH_AGENTS_HOME` point the child at.
*/
function seedWorkspace(
files: {
workspace?: Record<string, string>
personal?: Record<string, string>
skills?: Record<string, string>
},
): (cwd: string) => Promise<void> {
return async (cwd) => {
for (const [name, content] of Object.entries(files.workspace ?? {})) {
const file = join(cwd, name)
await mkdir(dirname(file), { recursive: true })
await writeFile(file, content)
}
for (const [name, content] of Object.entries(files.personal ?? {})) {
const file = join(cwd, '.dsh', name)
await mkdir(dirname(file), { recursive: true })
await writeFile(file, content)
}
for (const [name, content] of Object.entries(files.skills ?? {})) {
const file = join(cwd, '.agents', 'skills', name)
await mkdir(dirname(file), { recursive: true })
await writeFile(file, content)
}
}
}
/** Seed one real plaintext JSONL session for the `/resume` selector and host handoff smoke. */
async function seedResumeSession(cwd: string): Promise<void> {
const sessionCwd = realpathSync.native(cwd)
const id = SessionId('resume-target')
const meta: SessionHeader = { version: 0, id, createdAt: 1_700_000_000_000, cwd: sessionCwd }
const events: SessionEvent[] = [
{ type: 'turn/start', seq: 0, time: 1_700_000_000_001, data: { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } } },
{ type: 'user/message', seq: 1, time: 1_700_000_000_002, data: createUserMessage({
content: [{ type: 'text', text: 'persisted prompt' }], source: { kind: 'user' },
}), surfaceOp: 'append' },
{ type: 'step/start', seq: 2, time: 1_700_000_000_003, data: { turn: 1, step: 1 } },
{ type: 'request/header', seq: 3, time: 1_700_000_000_004, data: { header: { config: { provider: 'tui-scripted', model: 'tui-scripted-model' } }, reason: 'initial' } },
{ type: 'assistant/message', seq: 4, time: 1_700_000_000_005, data: {
turn: 1, step: 1,
message: createMessage({
role: 'assistant',
content: [{ type: 'text', text: 'persisted answer' }],
source: {
kind: 'model',
...{ provider: 'tui-scripted', model: 'tui-scripted-model' },
},
}),
}, surfaceOp: 'append' },
{ type: 'step/end', seq: 5, time: 1_700_000_000_006, data: { turn: 1, step: 1 } },
{ type: 'session/title', seq: 6, time: 1_700_000_000_007, data: { title: 'Resume selector design', messageSeqs: [1], source: { kind: 'fallback' } } },
{ type: 'todo/write', seq: 7, time: 1_700_000_000_008, data: { todos: [{ content: 'Preserve restored state', status: 'in_progress' }] } },
{ type: 'turn/end', seq: 8, time: 1_700_000_000_009, data: { turn: 1, reason: { kind: 'completed' } } },
]
const file = logPath(join(cwd, '.sessions'), sessionCwd, id, 'none')
await mkdir(dirname(file), { recursive: true })
await writeFile(file, [
JSON.stringify(toHeaderLine(meta)),
...packChunkRuns(events).map(record => JSON.stringify(record)),
'',
].join('\n'))
}
/** Model-visible startup context from the first request in the workspace's persisted session log. */
interface LoggedRequestContext {
/** The system prompt string the launcher sends. */
system: string
/** The durable skill-catalog message serialized to text. */
skillCatalog: string
}
async function readLoggedRequestContext(cwd: string): Promise<LoggedRequestContext> {
const sessionsDir = join(cwd, '.sessions')
const entries = await readdir(sessionsDir, { recursive: true })
// A single keyless run writes one session log; the source section is global, so any log carries it.
const logRelPath = entries.find(name => name.endsWith('.jsonl'))
if (logRelPath === undefined) throw new Error(`no session log written under ${sessionsDir}`)
const lines = (await readFile(join(sessionsDir, logRelPath), 'utf8')).split('\n').filter(Boolean)
let skillCatalog = ''
for (const line of lines) {
const event = JSON.parse(line) as SessionEvent
if (
event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& event.data.source.plugin === 'dsh-tool-skill'
) {
skillCatalog = JSON.stringify(event.data.content)
}
if (event.type === 'request/header') {
return {
system: event.data.header.system ?? '',
skillCatalog,
}
}
}
throw new Error(`session log ${logRelPath} has no request/header event`)
}
/** Shared defaults: the keyless key, the dsh bin, and the live cordis.yml (passed as the positional config). */
function smoke(overrides: Partial<TuiPtySmokeOptions> & { label: string }): Promise<string> {
return runTuiPtySmoke({
tempDirPrefix: 'tui-agent-smoke-',
binScript: dshBinScript,
configPath,
tsconfigPath,
env: { DEEPSEEK_API_KEY: 'keyless-tui-no-call' },
...overrides,
})
}
// The scripted conversation switches to the pro model first: the scripted
// adapter proves routing + prompt variables by rejecting tool-ful calls on any
// other route (see fixtures/tui-scripted-llm.ts).
const SELECT_PRO_MODEL = [
{ waitFor: 'scripted TUI ready.', send: '/model\r' },
{ waitFor: 'Select model', send: '\x1b[B\x1b[Z\r' },
] as const
describe('tui-agent keyless smoke (real Loader tree in a PTY)', () => {
it('boots pi-tui, sweeps the borderless banner in, enters plan mode, and restores the terminal', async () => {
// With no configured welcome the borderless banner sweeps in left-to-right;
// the detail line's session id (`main-session-<uuid>`) renders only once
// the sweep reaches it, so it marks a settled banner.
const output = await smoke({
label: 'tui-agent boot',
actions: [
{ waitFor: 'main-session-', send: '/plan' },
{ waitFor: '[off|message] — Enter or leave plan mode', send: '\r' },
{ waitFor: 'Plan mode on. Use /plan off to leave.', send: '/exit\r' },
],
})
expect(output).toContain('DEEPSEEK')
expect(output).toContain('HARNESS')
expect(output).toContain('main-session-')
expect(output).toContain('[off|message] — Enter or leave plan mode')
expect(output).toContain('Plan mode on. Use /plan off to leave.')
// Borderless: no box-drawing frame around the banner.
expect(output).not.toContain('╭')
expect(output).not.toContain('╮')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('switches models, streams a response, answers a user-question dialog, and exits cleanly', async () => {
const output = await smoke({
label: 'tui-agent conversation',
tempDirPrefix: 'tui-agent-conversation-',
configPath: scriptedConfigPath,
actions: [
...SELECT_PRO_MODEL,
{ waitFor: 'Model selected: tui-scripted/tui-scripted-model-pro.', send: '/plan exercise the TUI\r' },
// The question text first appears in the streamed tool-call card. Wait
// for the dialog's input legend so Enter cannot arrive before it owns
// terminal input when pre-dispatch policy yields.
{ waitFor: 'Tab custom answer • ↑/↓ navigate • Enter submit • Esc interrupt', send: '\r' },
{ waitFor: 'Decision received. Scripted TUI run complete.', send: '' },
// Session title: the first user message drives the first-message-llm
// provider's tool-less title call; the scripted adapter answers it, the
// accepted title lands in the log, and the TUI renders the terminal
// window title as `<session title> — <configured title>` via OSC 0.
// Gating /status on it keeps the assertion race-free; the diagnostics
// card is then exercised through the same real Loader/PTY composition.
{ waitFor: 'scripted session title — DeepSeek Harness', send: '/plan off\r' },
{ waitFor: 'Plan mode off.', send: 'Confirm the scripted run left plan mode.\r' },
{ waitFor: 'Default mode confirmed.', send: '/status\r' },
{ waitFor: 'Session status', send: '/exit\r' },
],
})
expect(output).toContain('I need one decision before I continue.')
expect(output).toContain('Reasoning effort: Max.')
expect(output).toContain('Plan mode on. Use /plan off to leave.')
expect(output).toContain('Plan mode off.')
expect(output).toContain('Default mode confirmed.')
expect(output).toContain(String.raw`\x1b]2;MODEL_CONTROLLED\x07`)
expect(output).toContain(String.raw`\x1b[999CMODEL_CURSOR`)
expect(output).toContain(String.raw`\x9b31mMODEL_C1`)
expect(output).not.toContain('\u001B]2;MODEL_CONTROLLED\u0007')
expect(output).not.toContain('\u001B[999CMODEL_CURSOR')
expect(output).not.toContain('\u009B31mMODEL_C1')
expect(output).toContain('Safe')
expect(output).toContain('\u001B]0;scripted session title — DeepSeek Harness\u0007')
expect(output).toContain('Session status')
expect(output).toContain('Title')
expect(output).toContain('scripted session title')
expect(output).toContain('Model')
expect(output).toContain('tui-scripted/tui-scripted-model-pro')
expect(output).toContain('KV cache')
expect(output).toContain('Context')
expect(output).toContain('128,000')
expect(output).toContain('System prompt')
expect(output).toContain('You are an AI agent powered by the DeepSeek Harness SDK.')
expect(output).toContain('Registered tools')
expect(output).toContain('ask_user_question')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('loads a local skill via /skill: and delivers its body to the model as a user turn', async () => {
// The whole user-only invocation path in one keyless boot: `ctx.get('skills')`
// resolves in the shipped tree, the client-side `/skill:` command parses,
// and the local provider admits a model-disabled skill by the omitted
// `user-invocable` default. The rendered `<skill name="…">` block reaches
// the model — proven by the scripted adapter echoing the fixture's body
// marker only when it arrives.
const output = await smoke({
label: 'tui-agent skill',
tempDirPrefix: 'tui-agent-skill-',
configPath: scriptedConfigPath,
prepare: seedWorkspace({
skills: {
'scripted-skill/SKILL.md': [
'---',
'name: scripted-skill',
'description: Keyless PTY proof that the skill command loads a local skill into the conversation.',
'disable-model-invocation: true',
'---',
'',
'SCRIPTED SKILL BODY MARKER',
'',
].join('\n'),
},
}),
actions: [
...SELECT_PRO_MODEL,
{ waitFor: 'Model selected: tui-scripted/tui-scripted-model-pro.', send: '/skill:scripted-skill\r' },
{ waitFor: 'Scripted skill body received.', send: '/exit\r' },
],
})
expect(output).not.toContain('[instructions]')
expect(output).toContain('Scripted skill body received.')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('adds a watched local skill to live /skill: autocomplete without restarting', async () => {
const skill = [
'---',
'name: hot-added-skill',
'description: HOT_ADDED_COMPLETION_MARKER',
'---',
'',
'Hot-added body.',
'',
].join('\n')
const output = await smoke({
label: 'tui-agent hot-added skill autocomplete',
tempDirPrefix: 'tui-agent-hot-skill-',
configPath: scriptedConfigPath,
actions: [
{
waitFor: 'scripted TUI ready.',
writeFile: {
path: '.agents/skills/hot-added-skill/SKILL.md',
content: skill,
},
send: '/skill:hot',
},
{ waitFor: 'HOT_ADDED_COMPLETION_MARKER', send: '\x03/exit\r' },
],
})
expect(output).toContain('HOT_ADDED_COMPLETION_MARKER')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('fuzzy-completes an @file path without reading or submitting the file', async () => {
const output = await smoke({
label: 'tui-agent file autocomplete',
tempDirPrefix: 'tui-agent-file-autocomplete-',
prepare: seedWorkspace({
workspace: {
'src/terminal-special-case.ts': 'export const marker = true\n',
'src/other.ts': 'export const other = true\n',
},
}),
actions: [
{ waitFor: 'main-session-', send: '@tsc' },
{ waitFor: 'File · terminal-special-case.t', send: '\t' },
{ waitFor: '@src/terminal-special-case.ts', send: '\x03/exit\r' },
],
})
expect(output).toContain('File · terminal-special-case.t')
expect(output).toContain('@src/terminal-special-case.ts')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('boots the Code Mode overlay tree, renders its banner, and exits cleanly', async () => {
// The overlay's only keyless composition proof: the include+patch tree,
// worker code runtime, and one-tool registry all mount before the banner.
const output = await smoke({
label: 'tui-agent code mode',
tempDirPrefix: 'tui-agent-code-mode-',
configPath: codeModeConfigPath,
actions: [{ waitFor: 'TUI Code Mode ready.', send: '/exit\r' }],
})
expect(output).toContain('TUI Code Mode ready.')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
})
describe('dsh CLI keyless smoke (apps/cli through the same PTY)', () => {
it('exec-replaces the TUI for /resume and restores the same session state', async () => {
const output = await smoke({
label: 'dsh in-place resume',
tempDirPrefix: 'dsh-in-place-resume-',
binScript: dshBinScript,
configPath: scriptedConfigPath,
prepare: seedResumeSession,
actions: [
{ waitFor: 'scripted TUI ready.', send: '/resume\r' },
{ waitFor: 'Resume selector design', send: 'Resume selector design' },
{ waitFor: '⌕ Resume selector design', send: '\r' },
{ waitFor: 'Preserve restored state', send: '/exit\r' },
],
})
const released = output.indexOf('\u001B[?2004l')
const restored = output.indexOf('Resume selector design — DeepSeek Harness')
expect(released).toBeGreaterThanOrEqual(0)
expect(restored).toBeGreaterThan(released)
expect(output).toContain('Preserve restored state')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('boots the shipped default config with no arguments and no personal overlay', async () => {
const output = await smoke({
label: 'dsh default boot',
tempDirPrefix: 'dsh-default-boot-',
binScript: dshBinScript,
configArgs: [],
actions: [{ waitFor: 'main-session-', send: '/exit\r' }],
})
expect(output).toContain('DEEPSEEK')
expect(output).toContain('main-session-')
expect(output).not.toContain('╭')
expect(output).not.toContain('╮')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('applies the personal overlay: config.yaml patches the tree and .env feeds its !!js', async () => {
// The whole personal-config chain in one boot: the personal .env supplies
// the variable, config.yaml patches the tui-agent entry with a `!!js`
// reference to it, and the banner renders the patched welcome verbatim.
const output = await smoke({
label: 'dsh personal overlay',
tempDirPrefix: 'dsh-personal-overlay-',
binScript: dshBinScript,
configArgs: [],
prepare: seedWorkspace({
personal: {
'.env': 'DSH_PERSONAL_WELCOME=PERSONAL OVERLAY READY.\n',
'config.yaml': [
'- id: tui-agent',
" name: '@deepseek-ai/dsh-tui-demo'",
' config:',
' provider: deepseek',
' model: deepseek-v4-flash',
' workspaceContext: false',
' welcome: !!js process.env.DSH_PERSONAL_WELCOME',
'',
].join('\n'),
},
}),
actions: [{ waitFor: 'PERSONAL OVERLAY READY.', send: '/exit\r' }],
})
expect(output).toContain('PERSONAL OVERLAY READY.')
expect(output).toContain('\u001B[?2004l')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('fails loud instead of booting when the personal config.yaml is invalid', async () => {
const output = await smoke({
label: 'dsh invalid personal config',
tempDirPrefix: 'dsh-invalid-personal-',
binScript: dshBinScript,
configArgs: [],
prepare: seedWorkspace({ personal: { 'config.yaml': 'id: not-a-list\n' } }),
expectedExitCode: 1,
})
expect(output).toContain('must be a top-level YAML array of loader patch entries')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('routes the --resume flag into the config resume intake, failing loud on a missing id', async () => {
// The flag path end to end: apps/cli parses `--resume missing-session` and
// provides the id on the boot context, the shipped config's `!!js` reads it
// as a bare identifier, and the resume fails loud — proving the printed
// `dsh --resume <id>` hint reaches the config resume intake with no env var.
const output = await smoke({
label: 'dsh resume flag failure',
tempDirPrefix: 'dsh-resume-flag-',
binScript: dshBinScript,
configArgs: ['--resume', 'missing-session'],
expectedExitCode: 1,
})
expect(output).toContain('ui-tui: session "missing-session" failed to start:')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('tells the model its source path and offers the bundled maintenance skills', async () => {
// The launcher resolves the checkout root three hops up from apps/cli/{src,lib};
// this test file sits an equal depth under the same root, so the same hop applies.
// The source-path line is a system-prompt section; the bundled skills reach the
// model through a durable user message, so each assertion targets its own field.
const sourceRoot = fileURLToPath(new URL('../../..', import.meta.url))
let context: LoggedRequestContext = { system: '', skillCatalog: '' }
await smoke({
label: 'dsh source-path prompt',
tempDirPrefix: 'dsh-source-path-',
binScript: dshBinScript,
configPath: scriptedConfigPath,
actions: [
...SELECT_PRO_MODEL,
{ waitFor: 'Model selected: tui-scripted/tui-scripted-model-pro.', send: 'exercise the TUI\r' },
{ waitFor: 'How should the scripted run proceed?', send: '\r' },
{ waitFor: 'Decision received. Scripted TUI run complete.', send: '/exit\r' },
],
inspect: async (cwd) => { context = await readLoggedRequestContext(cwd) },
})
expect(context.system).toContain(`Your own source code is the checkout at ${sourceRoot}; you can read it there to learn how dsh works and how to extend it.`)
expect(context.skillCatalog).toContain("- `dsh-customize`: Customize or maintain any dsh source checkout — the one powering the current DSH process, the installed `dsh` command, or a sibling dsh/deepseek-harness clone. Use before any requested action that alters such a checkout's files or git state. Read-only questions that only inspect the checkout do not trigger this. Do not edit the personal staging checkout directly.")
expect(context.skillCatalog).toContain('- `dsh-upgrade`: Upgrades a source-installed, personally customized DSH checkout to upstream master while preserving local changes and an unchanged rollback worktree. Use when the user asks to update or upgrade DSH.')
expect(context.skillCatalog).toContain('- `dsh-upstream-customization`: Classifies personal DSH customizations for upstream contribution and, after explicit per-feature approval, rebuilds one on upstream master and opens a draft pull request. Use when the user asks to contribute, publish, or upstream a local DSH change, or asks whether one is worth proposing.')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
})

View File

@@ -1,536 +0,0 @@
import { cp, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { basename, dirname, isAbsolute, join, relative, sep } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { scrubRequestHeaders, tokenizeSessionFixtureCwd } from '@deepseek-ai/dsh-acp-snapshot'
import type { Agent } from '@deepseek-ai/dsh-agent'
import * as AgentCore from '@deepseek-ai/dsh-agent-spine-demo'
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
import WorkerCodeRuntime from '@deepseek-ai/dsh-code-runtime-worker'
import CommandService from '@deepseek-ai/dsh-commands'
import LocalFileSystem from '@deepseek-ai/dsh-fs-local'
import * as FsPolicy from '@deepseek-ai/dsh-fs-policy'
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import { installLlmReplay, parseSessionLog } from '@deepseek-ai/dsh-llm-replay'
import PlanModeService from '@deepseek-ai/dsh-plan-mode'
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
import { packChunkRuns, SessionId, type Session, type SessionEvent } from '@deepseek-ai/dsh-session'
import SubagentService from '@deepseek-ai/dsh-subagent'
import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn'
import * as ToolSubagent from '@deepseek-ai/dsh-tool-subagent'
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
import * as ToolRalph from '@deepseek-ai/dsh-tool-ralph'
import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow'
import { createTuiChat, FILE_REFERENCE_PROMPT, TuiPromptService } from '@deepseek-ai/dsh-tui'
import LocalSpillStore from '@deepseek-ai/dsh-spill-local'
import * as SpillPolicy from '@deepseek-ai/dsh-spill-policy'
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
import WorkerWorkflowEngine from '@deepseek-ai/dsh-workflow-workerthread'
import { HeadlessTerminal } from '../../../packages/ui/tui/tests/headless-terminal.ts'
const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
// Keep pre-normalization layout widths identical across macOS and Linux.
const SNAPSHOT_TMP_ROOT = process.platform === 'win32' ? tmpdir() : '/tmp'
const PROVIDERS = [{ id: 'deepseek', models: [{ id: 'deepseek-v4-flash', contextWindow: 128_000 }] }]
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi
type SnapshotMode = 'replay' | 'record' | 'refresh'
type Composition = 'native' | 'code' | 'advanced'
type ScenarioInteraction = 'skill-invocation-policy'
interface Scenario {
name: string
composition: Composition
expectedTools: string[]
expectedEventCounts?: Record<string, number>
childSessions?: number
enterPlanMode?: boolean
leavePlanModeAfterFirstTurn?: boolean
recorded: boolean
seedWorkspace?: boolean
/**
* Load the opt-in `todo_write` tool for this scenario. The shipped tui-agent
* config omits it, so only the todo-plan scenario (the enabled-path proof)
* mounts it; the rest cover the default, todo-free composition.
*/
enableTodo?: boolean
/**
* Mount the spill stack (local backend + policy) with this inline cap, as the
* shipped configs do. The dispatch-spill scenario proves the durable
* `tool/code-dispatch` copy of an oversized sub-result is bounded to a
* preview + locator while the program value stays whole.
*/
spillMaxInlineBytes?: number
/** Run scenario-specific terminal input instead of replaying recorded user prompts. */
interaction?: ScenarioInteraction
}
const SCENARIOS: Scenario[] = [
{
name: 'multi-turn-conversation',
composition: 'native',
expectedTools: [],
expectedEventCounts: { 'plan/mode': 2 },
enterPlanMode: true,
leavePlanModeAfterFirstTurn: true,
recorded: true,
},
{
name: 'todo-plan',
composition: 'native',
expectedTools: ['todo_write'],
expectedEventCounts: { 'todo/write': 1 },
recorded: true,
enableTodo: true,
},
{
name: 'bash-terminal-card',
composition: 'native',
expectedTools: ['bash'],
recorded: true,
},
{
name: 'parallel-file-reads',
composition: 'native',
expectedTools: ['read', 'read'],
recorded: true,
seedWorkspace: true,
},
{
name: 'skill-invocation-policy',
composition: 'native',
expectedTools: [],
recorded: false,
seedWorkspace: true,
interaction: 'skill-invocation-policy',
},
{
name: 'code-mode',
composition: 'code',
expectedTools: ['run_code'],
expectedEventCounts: { 'tool/code-dispatch': 2 },
recorded: true,
},
{
name: 'code-mode-dispatch-spill',
composition: 'code',
expectedTools: ['run_code'],
expectedEventCounts: { 'tool/code-dispatch-start': 1, 'tool/code-dispatch': 1 },
recorded: true,
spillMaxInlineBytes: 600,
},
{
name: 'dynamic-workflow',
composition: 'native',
expectedTools: ['workflow'],
childSessions: 1,
recorded: true,
},
{
name: 'cordis-dynamic-toolchain',
composition: 'advanced',
expectedTools: ['cordis_mount', 'run_code', 'subagent', 'workflow', 'cordis_unmount'],
expectedEventCounts: { 'tool/code-dispatch': 1 },
childSessions: 2,
recorded: false,
},
]
function snapshotModeFromEnv(value: string | undefined): SnapshotMode {
if (value === undefined || value === '' || value === 'replay') return 'replay'
if (value === 'record' || value === 'refresh') return value
throw new Error(`DSH_SNAPSHOT must be replay, record, or refresh; got ${JSON.stringify(value)}`)
}
const MODE = snapshotModeFromEnv(process.env.DSH_SNAPSHOT)
const observedScenarios = new Set<string>()
function snapshotDisplayPath(displayPath: string, cwd: string, displayCwd: string): string {
const rel = relative(cwd, displayPath)
if (rel === '') return displayCwd
if (isAbsolute(rel) || rel === '..' || rel.startsWith(`..${sep}`)) return displayPath
return `${displayCwd}/${rel.split(sep).join('/')}`
}
function scenarioDir(scenario: Scenario): string {
return join(SNAPSHOTS_DIR, scenario.name)
}
function childFixturePaths(scenario: Scenario): string[] {
return Array.from(
{ length: scenario.childSessions ?? 0 },
(_, index) => join(scenarioDir(scenario), `session.${index + 1}.jsonl`),
)
}
function userPrompts(rawLog: string): string[] {
return parseSessionLog(rawLog).flatMap((event) => {
if (event.type !== 'user/message' || event.data.source.kind !== 'user') return []
const text = event.data.content
.filter(block => block.type === 'text')
.map(block => block.text)
.join('')
return text.length > 0 ? [text] : []
})
}
function rawSessionLog(session: Session): string {
return [
JSON.stringify({ type: 'session', ...session.header }),
...packChunkRuns(session.events).map(record => JSON.stringify(record)),
'',
].join('\n')
}
function normalizeTerminalSnapshot(snapshot: string, cwd: string, displayCwd: string): string {
return snapshot
.split(`/private${cwd}`).join('/workspace/project')
.split(displayCwd).join('/workspace/project')
.split(cwd).join('/workspace/project')
.replace(UUID_RE, '{{uuid}}')
}
async function settleTerminal(terminal: HeadlessTerminal): Promise<void> {
let stable = 0
for (let attempt = 0; attempt < 20 && stable < 3; attempt++) {
const before = terminal.frames
await new Promise(resolve => setTimeout(resolve, 10))
await terminal.flush()
stable = terminal.frames === before ? stable + 1 : 0
}
if (stable < 3) throw new Error('TUI frames did not quiesce within 200ms')
}
async function mountScenarioContext(
scenario: Scenario,
cwd: string,
displayCwd: string,
fixtureFile: string,
childFiles: string[],
): Promise<Context> {
class SnapshotLocalFileSystem extends LocalFileSystem {
override async resolve(
path: string,
opts?: { cwd?: string; signal?: AbortSignal },
): Promise<Awaited<ReturnType<LocalFileSystem['resolve']>>> {
const target = await super.resolve(path, opts)
return { ...target, displayPath: snapshotDisplayPath(target.displayPath, cwd, displayCwd) }
}
}
const ctx = new Context()
await ctx.plugin(AgentCore, {
agents: [],
dshHome: join(cwd, '.dsh'),
workspaceContext: false,
tools: { mode: scenario.composition === 'code' ? 'code' : scenario.composition === 'advanced' ? 'both' : 'native' },
skills: { local: { agentsHome: join(cwd, '.agents') } },
})
await ctx.plugin(TokenMeterService)
await ctx.plugin(LocalSubprocessService)
await ctx.plugin(LocalBashExecutor, { cwd, timeoutMs: 30_000 })
await ctx.plugin(SnapshotLocalFileSystem, { cwd: '/' })
await ctx.plugin(FsPolicy)
await ctx.plugin(ToolFs)
await ctx.plugin(UserInteractionService)
await ctx.plugin(TuiPromptService)
// todo_write is opt-in: only the todo-plan scenario mounts it, matching the shipped
// config that omits it. The other scenarios prove the default todo-free composition.
if (scenario.enableTodo === true) await ctx.plugin(ToolTodo)
await ctx.plugin(SubagentService)
await ctx.plugin(SubagentSpawn, { providerName: 'spawn' })
await ctx.plugin(ToolSubagent, { provider: 'spawn', toolName: 'subagent', enableRunInBackground: false })
await ctx.plugin(WorkerWorkflowEngine, { provider: 'spawn' })
await ctx.plugin(ToolWorkflow)
await ctx.plugin(ToolRalph)
await ctx.plugin(CommandService)
if (scenario.enterPlanMode === true) {
await ctx.plugin(PlanModeService, { section: 'Snapshot plan mode instructions.' })
}
if (scenario.composition === 'code' || scenario.composition === 'advanced') {
await ctx.plugin(WorkerCodeRuntime, {})
}
if (scenario.spillMaxInlineBytes !== undefined) {
await ctx.plugin(LocalSpillStore, { root: join(cwd, '.spill') })
await ctx.plugin(SpillPolicy, { maxInlineBytes: scenario.spillMaxInlineBytes })
}
if (scenario.composition === 'advanced') await ctx.plugin(ToolCordis, { vmTimeoutMs: 5_000 })
if (MODE === 'record' && scenario.recorded) {
await ctx.plugin(LlmDeepSeek)
} else {
installLlmReplay(ctx, { file: fixtureFile, childFiles, providers: PROVIDERS })
}
return ctx
}
interface ScenarioResult {
terminal: string
parent: Session
children: Session[]
workflowEvents: string[]
}
async function runScenario(scenario: Scenario): Promise<ScenarioResult> {
const clock = vi.spyOn(Date, 'now').mockReturnValue(new Date(2026, 6, 21, 12, 0, 0).getTime())
const dir = scenarioDir(scenario)
const fixtureFile = join(dir, 'session.jsonl')
const childFiles = childFixturePaths(scenario)
const prompts = userPrompts(await readFile(fixtureFile, 'utf8'))
if (scenario.interaction === undefined) {
expect(prompts.length, `${scenario.name} must carry at least one recorded user prompt`).toBeGreaterThan(0)
}
const cwd = await mkdtemp(join(SNAPSHOT_TMP_ROOT, `dsh-tui-snapshot-${scenario.name}-`))
const displayCwd = `/tmp/${basename(cwd)}`
let ctx: Context | undefined
let controller: ReturnType<typeof createTuiChat> | undefined
const terminal = new HeadlessTerminal(100, 36)
try {
if (scenario.seedWorkspace === true) {
const source = join(scenarioDir(scenario), 'workspace')
await cp(source, cwd, { recursive: true })
}
ctx = await mountScenarioContext(scenario, cwd, displayCwd, fixtureFile, childFiles)
const disposedSessions: Session[] = []
ctx.on('session/disposed', (session) => { disposedSessions.push(session) })
const workflowEvents: string[] = []
for (const name of ['workflow/start', 'workflow/phase', 'workflow/agent-start', 'workflow/agent-end', 'workflow/end'] as const) {
ctx.on(name, () => { workflowEvents.push(name) })
}
const handle = await ctx.agents.create({
sessionId: SessionId('main-session'),
meta: { cwd },
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-flash' },
})
const agent: Agent = handle.agent
controller = createTuiChat(ctx, {
sessionId: 'main-session',
theme: { color: true },
showReasoning: true,
title: 'DSH TUI snapshot',
welcome: `Recorded replay: ${scenario.name}`,
maxToolOutputLines: 8,
}, {
terminal,
exit: () => {},
formatCwd: () => displayCwd,
})
await settleTerminal(terminal)
let interactionSnapshot: string | undefined
if (scenario.interaction === 'skill-invocation-policy') {
terminal.send('/skill')
await settleTerminal(terminal)
const discovery = normalizeTerminalSnapshot(
await terminal.snapshot({ includeScrollback: true }),
cwd,
displayCwd,
)
expect(discovery).toContain('user-only-skill')
expect(discovery).not.toContain('model-only-skill')
terminal.send('\x03')
await settleTerminal(terminal)
const skillContext = ctx
const skillTurnEnded = new Promise<void>((resolve) => {
const detach = skillContext.on('session/event', (session, event) => {
if (session !== agent.session || event.type !== 'turn/end') return
detach()
resolve()
})
})
terminal.send('/skill:user-only-skill')
terminal.send('\r')
await skillTurnEnded
await agent.whenIdle()
await settleTerminal(terminal)
const loaded = normalizeTerminalSnapshot(
await terminal.snapshot({ includeScrollback: true }),
cwd,
displayCwd,
)
expect(loaded).toContain('USER-ONLY SKILL LOADED')
terminal.send('/skill:model-only-skill')
terminal.send('\r')
await settleTerminal(terminal)
const denied = normalizeTerminalSnapshot(
await terminal.snapshot({ includeScrollback: true }),
cwd,
displayCwd,
)
expect(denied).toContain('model-only-skill')
expect(denied).toContain('not available for user invocation.')
expect(denied).not.toContain('MODEL-ONLY BODY MUST NOT LOAD')
interactionSnapshot = [
'=== skill autocomplete ===',
discovery,
'',
'=== loaded exact invocation ===',
loaded,
'',
'=== denied exact invocation ===',
denied,
].join('\n')
}
let remainingPrompts = prompts
if (scenario.enterPlanMode === true) {
const firstPrompt = prompts[0]!
terminal.send(`/plan ${firstPrompt}`)
terminal.send('\r')
await agent.whenIdle()
await settleTerminal(terminal)
remainingPrompts = prompts.slice(1)
}
if (scenario.leavePlanModeAfterFirstTurn === true) {
terminal.send('/plan off')
terminal.send('\r')
await settleTerminal(terminal)
}
for (const prompt of remainingPrompts) {
terminal.send(prompt)
terminal.send('\r')
await agent.whenIdle()
await settleTerminal(terminal)
}
const events: SessionEvent[] = [...agent.session.events]
const firstHeader = events.find(event => event.type === 'request/header')
expect(firstHeader?.type === 'request/header' && firstHeader.data.header.system)
.toContain(FILE_REFERENCE_PROMPT)
expect(events.filter(event => event.type === 'tool/call').map(event => event.data.name)).toEqual(scenario.expectedTools)
for (const [type, count] of Object.entries(scenario.expectedEventCounts ?? {})) {
expect(events.filter(event => event.type === type), `${scenario.name} must emit ${type}`).toHaveLength(count)
}
if (scenario.enterPlanMode === true) {
expect(ctx.planMode.get(agent)).toEqual({
active: scenario.leavePlanModeAfterFirstTurn !== true,
})
const planMode = events.find(event => event.type === 'plan/mode')
if (planMode === undefined || firstHeader === undefined) {
throw new Error('plan-mode command snapshot needs plan/mode before its first request/header')
}
expect(planMode.seq).toBeLessThan(firstHeader.seq)
expect(firstHeader.data.header.system).toContain('Snapshot plan mode instructions.')
const firstMessage = events.find(event => event.type === 'user/message')
expect(firstMessage?.data.content).toEqual([{ type: 'text', text: prompts[0] }])
}
if (scenario.leavePlanModeAfterFirstTurn === true) {
const planModes = events.filter(event => event.type === 'plan/mode')
expect(planModes.map(event => event.data.active)).toEqual([true, false])
const headers = events.filter(event => event.type === 'request/header')
const exit = planModes[1]
const afterExit = headers[1]
if (exit === undefined || afterExit === undefined) {
throw new Error('active plan exit snapshot needs a committed exit and changed request header')
}
expect(exit.seq).toBeLessThan(afterExit.seq)
expect(afterExit.data.header.system).not.toContain('Snapshot plan mode instructions.')
expect(events.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin').map(event => (event.data as { content: unknown }).content))
.toContainEqual([{ type: 'text', text: 'The user switched this session back to the default mode.' }])
}
if (scenario.spillMaxInlineBytes !== undefined) {
// The REAL pipeline ran (tools execute on replay too): the durable
// dispatch copy is bounded to a preview + locator under the run cwd,
// while the outer result still carries the program's whole value.
const dispatch = events.find(event => (event.type as string) === 'tool/code-dispatch')
const content = (dispatch?.data as { content: { type: string; text?: string }[] }).content
const text = content.filter(block => block.type === 'text').map(block => block.text ?? '').join('')
expect(Buffer.byteLength(text, 'utf8')).toBeLessThanOrEqual(scenario.spillMaxInlineBytes)
expect(text).toContain('Full formatted result stored at:')
expect(text).toContain('.spill')
}
expect(events.filter(event => event.type === 'tool/result').every(event => !event.data.message.content[0].isError)).toBe(true)
expect(events.filter(event => event.type === 'turn/end').every(event => event.data.reason.kind !== 'error')).toBe(true)
if (scenario.name === 'dynamic-workflow' || scenario.name === 'cordis-dynamic-toolchain') {
expect(workflowEvents).toEqual([
'workflow/start',
'workflow/phase',
'workflow/agent-start',
'workflow/agent-end',
'workflow/end',
])
}
expect(terminal.themeViolations(), `${scenario.name} must remain theme-agnostic`).toEqual([])
const snapshot = interactionSnapshot ?? normalizeTerminalSnapshot(
await terminal.snapshot({ includeScrollback: true }),
cwd,
displayCwd,
)
await handle.dispose()
const children = disposedSessions
.filter(session => session !== agent.session)
.sort((a, b) => a.header.createdAt - b.header.createdAt)
expect(children).toHaveLength(scenario.childSessions ?? 0)
return { terminal: snapshot, parent: agent.session, children, workflowEvents }
} finally {
await controller?.dispose()
await ctx?.fiber.dispose()
await terminal.dispose()
await rm(cwd, { recursive: true, force: true })
clock.mockRestore()
}
}
async function writeRecording(scenario: Scenario, result: ScenarioResult): Promise<void> {
const dir = scenarioDir(scenario)
await mkdir(dir, { recursive: true })
await writeFile(
join(dir, 'session.jsonl'),
scrubRequestHeaders(tokenizeSessionFixtureCwd(rawSessionLog(result.parent))),
)
expect(result.children).toHaveLength(scenario.childSessions ?? 0)
for (const [index, child] of result.children.entries()) {
await writeFile(
join(dir, `session.${index + 1}.jsonl`),
scrubRequestHeaders(tokenizeSessionFixtureCwd(rawSessionLog(child))),
)
}
}
describe('TUI recorded-session terminal snapshots', () => {
for (const scenario of SCENARIOS) {
it(scenario.name, async () => {
observedScenarios.add(scenario.name)
const result = await runScenario(scenario)
const terminalFile = join(scenarioDir(scenario), 'terminal.expected.txt')
if (MODE === 'record' || MODE === 'refresh') {
await mkdir(scenarioDir(scenario), { recursive: true })
await writeFile(terminalFile, result.terminal)
}
if (MODE === 'record' && scenario.recorded) await writeRecording(scenario, result)
await expect(result.terminal).toMatchFileSnapshot(terminalFile)
}, 120_000)
}
})
afterAll(async () => {
expect([...observedScenarios].sort()).toEqual(SCENARIOS.map(scenario => scenario.name).sort())
const directories = (await readdir(SNAPSHOTS_DIR, { withFileTypes: true }))
.filter(entry => entry.isDirectory())
.map(entry => entry.name)
.sort()
expect(directories).toEqual(SCENARIOS.map(scenario => scenario.name).sort())
for (const scenario of SCENARIOS) {
const expected = [
'session.jsonl',
'terminal.expected.txt',
...scenario.seedWorkspace === true ? ['workspace'] : [],
...Array.from({ length: scenario.childSessions ?? 0 }, (_, index) => `session.${index + 1}.jsonl`),
].sort()
expect((await readdir(scenarioDir(scenario))).sort()).toEqual(expected)
for (const fixture of ['session.jsonl', ...childFixturePaths(scenario).map(path => basename(path))]) {
const content = await readFile(join(scenarioDir(scenario), fixture), 'utf8')
expect(scrubRequestHeaders(content), `${scenario.name}/${fixture} carries request-header bulk`).toBe(content)
}
}
})

View File

@@ -1,18 +1,19 @@
# Opt-in Web composition for inspecting the self-referential Cordis tools.
# Temporary Plugin code can reach every injected live capability; treat this
# deployment like shell access, not as a security boundary.
- id: base
name: '@cordisjs/plugin-include'
# This file is an OVERLAY over the shipped web composition (`base.cordis.yml` +
# `web.cordis.yml`), not a tree: `dsh web --config` applies it as one more
# sibling patch list at the same include level, so these patches reach base and
# overlay rows alike. A patch replaces the targeted row's whole `config`.
# AppCLIEntry normally injects the assembly-owned dist path before `dsh web`
# boots; pinning the port here keeps this demo off the default 3080.
- id: webserver
config:
path: ../../apps/cli/cordis.yml
patches:
# AppCLIEntry normally injects this assembly-owned path before `dsh web`
# boots; the standalone Cordis launcher needs the equivalent patch here.
- id: webserver
config:
host: 127.0.0.1
port: 3081
distIndex: !!js "new URL('./apps/web/dist/index.html', 'file://' + process.cwd() + '/').pathname"
- insert:
- id: tool-cordis
name: '@deepseek-ai/dsh-tool-cordis'
host: 127.0.0.1
port: 3081
distIndex: !!js "new URL('./apps/web/dist/index.html', 'file://' + process.cwd() + '/').pathname"
- insert:
- id: tool-cordis
name: '@deepseek-ai/dsh-tool-cordis'