mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Merge latest origin/master into worktree/agent-execution-context-rfc
# Conflicts: # docs/event-producer-consumer.md # docs/module-graph.md # packages/examples/README.md
This commit is contained in:
@@ -27,7 +27,7 @@ packages/ @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
|
||||
cordis/ self-referential toolset: the agent inspects/mounts plugins in its own runtime
|
||||
hooks/ Claude Code / Codex hook bridges + shared wire-protocol library
|
||||
session-persistence/ persistence seam + JSONL/SQLite backends
|
||||
ui/ ACP/stdio/JSON-RPC bridges; boot, approval, interaction plugins
|
||||
ui/ ACP/stdio/TUI/JSON-RPC bridges; boot, approval, interaction plugins
|
||||
examples/ demo bundles (agent-spine + stdio/ACP/JSON-RPC bins) leaves load
|
||||
support/ dev/test infrastructure packages
|
||||
util/ zero-dependency utilities
|
||||
@@ -58,6 +58,7 @@ pnpm run doc-sync # all documentation gates; see the doc-sync script in pa
|
||||
pnpm run website:build # VitePress build (doubles as the site's dead-link check)
|
||||
pnpm run demo:echo # mock-model REPL, no key needed
|
||||
pnpm run demo:repl # real REPL coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:tui # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:cordis # self-referential demo: the agent modifies its own runtime (needs key)
|
||||
pnpm run demo:acp # ACP server agent (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
+2
-2
@@ -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
|
||||
README.md: 53dd3896eb15800125673e7c44f7de02daca9376
|
||||
README.zh.md: ab826f62658248249ec18c57b35c0065c0f909d1
|
||||
README.md: aaa129272ee9346cebe2d59774d742fbe21af80a
|
||||
README.zh.md: 42f2ed9b57bf008210042083977b7adbb7f1ab0e
|
||||
|
||||
@@ -11,7 +11,10 @@ This monorepo is built on the [Cordis](https://github.com/cordiverse/cordis) fra
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run test # vitest
|
||||
pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:echo # keyless mock-model REPL
|
||||
pnpm run demo:repl # readline coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:tui # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:cordis # self-referential agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
|
||||
+4
-1
@@ -11,7 +11,10 @@
|
||||
```sh
|
||||
pnpm install
|
||||
pnpm run test # vitest
|
||||
pnpm run demo:repl # REPL agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:echo # keyless mock-model REPL
|
||||
pnpm run demo:repl # readline coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:tui # full-screen TUI coding agent (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:cordis # self-referential agent demo (needs DEEPSEEK_API_KEY)
|
||||
pnpm run demo:acp # ACP server agent demo (needs DEEPSEEK_API_KEY)
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ Placement: bugs → postmortems; rationale → RFCs; procedures → cookbooks; t
|
||||
- **Document current state, not change history.** Avoid "previously/now/no longer", PRs, commits, and stack positions in durable prose; name the live mechanism. Put change stories in commits, PRs, RFCs, or postmortems.
|
||||
- **Write an RFC in the same PR for decisions a maintainer may reasonably revisit.** Mechanical or self-evident changes need none ([when to write one](rfc/README.md)).
|
||||
- **One physical line per paragraph** (`verify-md-wrap`): use editor soft-wrap. Code blocks, tables, and list structure keep their formatting; code comments stay under the linter's column limit.
|
||||
- **Fenced `ts` blocks must compile** (`doc-typecheck`); a pasted type definition is fenced ` ```ts type-equiv ` and registered in the manifest so it cannot drift ([mechanics](development.md#documenting-types-verbatim-ts-type-equiv)).
|
||||
- **Fenced `ts` blocks must compile** (`doc-typecheck`); a pasted type declaration and its original JSDoc are fenced ` ```ts type-equiv ` and registered in the manifest so neither can drift ([mechanics](development.md#documenting-types-verbatim-ts-type-equiv)).
|
||||
- **The [core-data-structures catalog](core-data-structures/core.md) updates in the same change** that reshapes a documented type. `verify-type-equiv` catches drifted pastes, not never-documented new types ([what counts as core](core-data-structures/core.md#what-counts-as-core)).
|
||||
- **Bilingual pairs update together**: editing either side obligates the counterpart and a re-record in the same change ([i18n contract](i18n/README.md)).
|
||||
- **Comments and JSDoc state complete contracts, not reasoning transcripts.** Preserve behavior, conditions, timing, modality, exceptions, consequences, and non-obvious orientation; delete implementation narration, test walkthroughs, review analysis, and code restatement. Keep the local contract and link to its owning rationale. Use [dsh-prose-standard](../.agents/skills/dsh-prose-standard/SKILL.md) for required coverage, decision rules, and examples.
|
||||
|
||||
@@ -151,7 +151,7 @@ Some seams bend the template deliberately: LLM combines interface and consumer b
|
||||
|
||||
### Bundles And Apps
|
||||
|
||||
`dsh-agent-spine-demo` bundles the default spine ([README](../packages/examples/agent-spine-demo/README.md)). `dsh-stdio-demo` adds a terminal front door; `dsh-acp-demo` adds stdout-pure ACP over JSON-RPC ([ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies its default only without an explicit config channel and drives `dsh-jsonrpc` over line-delimited JSON-RPC ([Python SDK](../python/README.md)). Deployments remain thin leaves with swappable backends and optional product tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
|
||||
`dsh-agent-spine-demo` bundles the default spine ([README](../packages/examples/agent-spine-demo/README.md)). `dsh-stdio-demo` adds a terminal front door that selects `dsh-tui` for interactive terminals and line-oriented `dsh-stdio` for pipes; `dsh-acp-demo` adds stdout-pure ACP over JSON-RPC ([ui/](../packages/ui/README.md)). `dsh-jsonrpc-agent` boots external `cordis.yml`; the Python SDK supplies its default only without an explicit config channel and drives `dsh-jsonrpc` over line-delimited JSON-RPC ([Python SDK](../python/README.md)). Deployments remain thin leaves with swappable backends and optional product tools ([examples/](../examples/AGENTS.md), [runnable wirings](cookbook/extension-cookbook.md#runnable-wirings), [graph atlas](graph-atlas.md)).
|
||||
|
||||
### Where New Behavior Goes
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ flowchart LR
|
||||
pkg_subagent_spawn["subagent-spawn"]
|
||||
pkg_subagent_fork["subagent-fork"]
|
||||
pkg_subagent_acp["subagent-acp"]
|
||||
pkg_subagent_mock["subagent-mock"]
|
||||
pkg_tasks["tasks"]
|
||||
svc_tasks["ctx.tasks<br/>Background task registry"]
|
||||
pkg_tool_tasks["tool-tasks"]
|
||||
@@ -132,7 +131,6 @@ flowchart LR
|
||||
pkg_subagent --> svc_subagents
|
||||
pkg_subagent_acp --> svc_subagents
|
||||
pkg_subagent_fork --> svc_subagents
|
||||
pkg_subagent_mock --> svc_subagents
|
||||
pkg_subagent_spawn --> svc_subagents
|
||||
pkg_system_prompt --> svc_systemPrompt
|
||||
pkg_tasks --> svc_tasks
|
||||
@@ -229,7 +227,7 @@ flowchart LR
|
||||
| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). |
|
||||
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-policy contributes observed-state checks through the fs/* event gate. |
|
||||
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend currently consumes the pre-step event directly; a model-facing compact tool remains deferred. |
|
||||
| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-mock`](../packages/support/subagent-mock) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
|
||||
| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
|
||||
| `ctx.tasks` | `core` | [`tasks`](../packages/tasks/tasks) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-tasks`](../packages/tasks/tool-tasks) | - | Producers (tool-bash background commands, tool-subagent background delegations) register running work; tool-tasks is the model-facing control surface that reads, lists, and kills it. |
|
||||
| `ctx.web` | `seam` | [`web`](../packages/web/web) | [`web-search-exa`](../packages/web/web-search-exa), [`web-search-perplexity`](../packages/web/web-search-perplexity), [`web-search-deepseek`](../packages/web/web-search-deepseek), [`web-fetch-local`](../packages/web/web-fetch-local) | [`tool-web`](../packages/web/tool-web) | - | Search and fetch providers register into one ctx.web seam; tool-web owns the stable model-facing names. |
|
||||
| `ctx.spillStore` | `seam` | [`spill`](../packages/spill/spill) | [`spill-local`](../packages/spill/spill-local) | [`spill-policy`](../packages/spill/spill-policy) | - | The backend saves oversized tool text and returns a model-facing locator plus retrieval hint; spill-policy is the tools/post-execute consumer that decides when to spill. |
|
||||
|
||||
+52
-38
@@ -787,7 +787,7 @@ Source: [`packages/ui/stdio/src/index.ts:33`](../packages/ui/stdio/src/index.ts)
|
||||
* is the explicit model-facing tool order (forwarded to the system-prompt plugin);
|
||||
* fresh sessions use `process.cwd()` as their workspace cwd; resumed sessions
|
||||
* keep their persisted cwd. `persistenceRoot` is the JSONL backend's directory;
|
||||
* `welcome` is the UI banner.
|
||||
* `welcome` is the UI banner and `ui` configures terminal mode/presentation.
|
||||
*/
|
||||
export interface Config {
|
||||
/** Provider route for the `main` agent. */
|
||||
@@ -808,6 +808,8 @@ export interface Config {
|
||||
persistenceRoot?: string
|
||||
/** stdin-chat banner printed once on start. Defaults to `'ready.'`. */
|
||||
welcome?: string
|
||||
/** Terminal front-door selection and pi-tui presentation settings. */
|
||||
ui?: UiConfig
|
||||
/** Skill registry, local-provider, and model-facing consumer config forwarded to agent-spine-demo. */
|
||||
skills?: agentCore.SkillConfig
|
||||
/** Model-facing bash tool config forwarded through agent-core. */
|
||||
@@ -823,11 +825,22 @@ export interface Config {
|
||||
/** Controls automatic AGENTS.md/CLAUDE.md loading; configure a byte budget or set `false`. */
|
||||
workspaceContext: agentCore.Config['workspaceContext']
|
||||
}
|
||||
|
||||
/** App-level terminal selection with nested TUI presentation settings. */
|
||||
export interface UiConfig {
|
||||
/** Select a concrete front door or infer it from the process streams. */
|
||||
mode?: TerminalMode
|
||||
/** Settings forwarded only when the pi-tui front door is selected. */
|
||||
tui?: uiTui.TuiConfig
|
||||
}
|
||||
|
||||
/** Terminal front door selected by the app bundle. */
|
||||
export type TerminalMode = 'auto' | 'readline' | 'tui'
|
||||
```
|
||||
|
||||
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools)
|
||||
Depends on: [`agentCore`](../packages/examples/agent-spine-demo/src/index.ts) · [`ToolsConfig`](#deepseek-aidsh-tools) · [`uiTui`](../packages/ui/tui/src/index.ts)
|
||||
|
||||
Source: [`packages/examples/stdio-demo/src/index.ts:40`](../packages/examples/stdio-demo/src/index.ts)
|
||||
Source: [`packages/examples/stdio-demo/src/index.ts:75`](../packages/examples/stdio-demo/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-subagent-acp`
|
||||
|
||||
@@ -890,41 +903,6 @@ export interface Config {
|
||||
|
||||
Source: [`packages/subagent/subagent-fork/src/index.ts:25`](../packages/subagent/subagent-fork/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-subagent-mock`
|
||||
|
||||
Requires: `subagents`
|
||||
|
||||
```ts config-catalog
|
||||
/** Config for the mock provider; all optional with test-friendly defaults. */
|
||||
export interface Config {
|
||||
/** Registry name to register under. */
|
||||
name: string
|
||||
/** The text the scripted child "returns" as its final answer. */
|
||||
reply?: string
|
||||
/** The stop reason the run settles with. */
|
||||
stopReason?: SubagentStopReason
|
||||
/** Which start-time capabilities to advertise (default: all `true`). */
|
||||
capabilities?: Partial<SubagentCapabilities>
|
||||
/**
|
||||
* The conversation-history descriptor to declare
|
||||
* ({@link SubagentProvider.inheritsParentContext}); default `false` (fresh
|
||||
* conversation). Set `true` to exercise seeded/fork wording in consumer
|
||||
* tests. This flag says nothing about tool, service, scope, or authority
|
||||
* inheritance.
|
||||
*/
|
||||
inheritsParentContext?: boolean
|
||||
/**
|
||||
* Structured value surfaced when a request carries an `outputSchema` and the
|
||||
* `outputSchema` capability is on (default: `{ reply }`).
|
||||
*/
|
||||
structured?: unknown
|
||||
}
|
||||
```
|
||||
|
||||
Depends on: [`SubagentCapabilities`](../packages/subagent/subagent/src/index.ts) · [`SubagentStopReason`](../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
Source: [`packages/support/subagent-mock/src/index.ts:87`](../packages/support/subagent-mock/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-subagent-spawn`
|
||||
|
||||
Requires: `subagents`
|
||||
@@ -1206,6 +1184,42 @@ export type ToolPresentationMode = 'native' | 'code' | 'both'
|
||||
|
||||
Source: [`packages/core/tools/src/index.ts:382`](../packages/core/tools/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-tui`
|
||||
|
||||
Requires: `agents` · `userInteraction` · `tools`
|
||||
|
||||
```ts config-catalog
|
||||
/** Serializable plugin configuration. */
|
||||
export interface Config extends TuiConfig {
|
||||
/** Header subtitle. Defaults to `ready.`. */
|
||||
welcome?: string
|
||||
/** Exact shared agent/session identity driven by this terminal. Defaults to `main`. */
|
||||
sessionId?: string
|
||||
}
|
||||
|
||||
/** Presentation settings for the pi-tui terminal mode. */
|
||||
export interface TuiConfig {
|
||||
/** Render model reasoning blocks. */
|
||||
showReasoning?: boolean
|
||||
/** Maximum tool-output lines shown before the card is collapsed. */
|
||||
maxToolOutputLines?: number
|
||||
/** Maximum options visible at once in a user-question dialog. */
|
||||
maxQuestionOptions?: number
|
||||
/** User-question dialog width in terminal columns. */
|
||||
questionDialogWidth?: number
|
||||
/** User-question dialog maximum height in terminal rows. */
|
||||
questionDialogMaxHeight?: number
|
||||
/** Show the terminal's hardware cursor at the pi editor's IME marker. */
|
||||
showHardwareCursor?: boolean
|
||||
/** Apply the built-in ANSI color palette. */
|
||||
color?: boolean
|
||||
/** Terminal window title while the UI is mounted. */
|
||||
title?: string
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/ui/tui/src/index.ts:100`](../packages/ui/tui/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-user-approval`
|
||||
|
||||
```ts config-catalog
|
||||
|
||||
@@ -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
|
||||
extension-cookbook.md: 995ecd879faec02044b55ce3d53f7fe1b2aed270
|
||||
extension-cookbook.zh.md: e3fe3103e13c359e77e0202df223aae35bcdd525
|
||||
extension-cookbook.md: 1a1f0b200801a54f067faf9866b72ccadfdc62d3
|
||||
extension-cookbook.zh.md: 891a808b4ed87b1fd92d0b68c580895956f9ab8c
|
||||
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
## Runnable wirings
|
||||
|
||||
Three complete examples load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool — the all-mock skeleton check, `pnpm run demo:echo`), [`examples/coding-agent`](../../examples/coding-agent) (DeepSeek V4 + the bash tool suite behind a terminal REPL UI, `pnpm run demo:repl`), and [`examples/acp-agent`](../../examples/acp-agent) (an agent exposed as an ACP server over JSON-RPC stdio — the client-driver shape, `pnpm run demo:acp`). Each leaf is just its swappable backends plus an app-package entry: the stdio demos load [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo), the ACP demo loads [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), and both app packages share the spine via the [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) bundle.
|
||||
Five runnable leaves load their plugin trees from `cordis.yml`: [`examples/echo-agent`](../../examples/echo-agent) (mock model + echo tool, `pnpm run demo:echo`), [`examples/coding-agent`](../../examples/coding-agent) (DeepSeek V4 + coding tools through a line-oriented readline REPL, `pnpm run demo:repl`), [`examples/tui-agent`](../../examples/tui-agent) (the same coding composition through full-screen pi-tui, `pnpm run demo:tui`), [`examples/cordis-agent`](../../examples/cordis-agent) (self-inspection and dynamic plugin mounting, `pnpm run demo:cordis`), and [`examples/acp-agent`](../../examples/acp-agent) (an ACP server over JSON-RPC stdio, `pnpm run demo:acp`). The terminal leaves load [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo), the ACP leaf loads [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo), and both app packages share the spine through [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo).
|
||||
|
||||
## The feature → mechanism map
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
## 可运行的组装示例
|
||||
|
||||
三个完整示例从 `cordis.yml` 加载各自的插件树:[`examples/echo-agent`](../../examples/echo-agent)(mock 模型 + echo 工具——全 mock 骨架检查,`pnpm run demo:echo`)、[`examples/coding-agent`](../../examples/coding-agent)(DeepSeek V4 + bash 工具套件,配合终端 REPL UI,`pnpm run demo:repl`)、[`examples/acp-agent`](../../examples/acp-agent)(通过 JSON-RPC stdio 暴露为 ACP 服务器的 agent——客户端驱动形态,`pnpm run demo:acp`)。每个叶子只是其可替换后端加一个 app 包入口:stdio 演示加载 [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo),ACP 演示加载 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),两个 app 包通过 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) bundle 共享主干。
|
||||
五个可运行叶子从 `cordis.yml` 加载各自的插件树:[`examples/echo-agent`](../../examples/echo-agent)(mock 模型 + echo 工具,`pnpm run demo:echo`)、[`examples/coding-agent`](../../examples/coding-agent)(DeepSeek V4 + coding 工具,通过面向行的 readline REPL 交互,`pnpm run demo:repl`)、[`examples/tui-agent`](../../examples/tui-agent)(通过全屏 pi-tui 复用相同的 coding 组装,`pnpm run demo:tui`)、[`examples/cordis-agent`](../../examples/cordis-agent)(自我检查和动态插件挂载,`pnpm run demo:cordis`)与 [`examples/acp-agent`](../../examples/acp-agent)(通过 JSON-RPC stdio 暴露的 ACP 服务器,`pnpm run demo:acp`)。终端叶子加载 [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo),ACP 叶子加载 [`@deepseek-ai/dsh-acp-demo`](../../packages/examples/acp-demo),两个 app 包通过 [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) 共享主干。
|
||||
|
||||
## 功能→机制映射
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
|
||||
'session/created'(this: Scoped<Session>, session: Session): void
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:46`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:47`](../../packages/core/session/src/index.ts)
|
||||
|
||||
### `session/disposed` — emit
|
||||
|
||||
@@ -267,7 +267,7 @@ Emitted once when an announced session leaves the store, including publication r
|
||||
'session/disposed'(this: Scoped<Session>, session: Session): void
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:56`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:57`](../../packages/core/session/src/index.ts)
|
||||
|
||||
### `session/event` — emit
|
||||
|
||||
@@ -279,7 +279,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
|
||||
|
||||
Types: [SessionEvent](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:68`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:69`](../../packages/core/session/src/index.ts)
|
||||
|
||||
### `session/flush` — parallel
|
||||
|
||||
@@ -289,7 +289,7 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
|
||||
'session/flush'(this: Scoped<Session>, session: Session): Promise<void> | void
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:78`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:79`](../../packages/core/session/src/index.ts)
|
||||
|
||||
## `subagent/*`
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ list(): Session[]
|
||||
fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId): Session
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:585`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:577`](../../packages/core/session/src/index.ts)
|
||||
|
||||
## `ctx.skills` — `SkillService`
|
||||
|
||||
|
||||
@@ -9,12 +9,20 @@ Source: [`packages/ui/user-approval/src/index.ts`](../../packages/ui/user-approv
|
||||
Every request receives a fresh `ApprovalRequestId`. The brand pairs the `approval/asked` and `approval/decided` audit events without making approval ids interchangeable with tool-call or agent/session ids.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Pairs one `approval/asked` audit event with its `approval/decided`.
|
||||
* Service-issued (one fresh id per {@link ApprovalService.request} call).
|
||||
*/
|
||||
type ApprovalRequestId = Branded<'ApprovalRequestId'>
|
||||
```
|
||||
|
||||
`ApprovalOutcome` is closed and fail-closed. `allowed-once` grants only the asked-about action; callers deny on `rejected`, `cancelled`, and `unavailable`. A missing, non-owning, throwing, or non-conforming answerer becomes `unavailable` rather than opening the gate.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Closed approval outcomes: a one-shot grant, explicit rejection, withdrawn
|
||||
* request, or unavailable answerer. Callers fail closed on `unavailable`.
|
||||
*/
|
||||
type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
|
||||
```
|
||||
|
||||
@@ -23,6 +31,18 @@ type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
|
||||
`ApprovalPolicy` determines what happens before interactive answerers run. `ask` delegates to the composed answerer chain, whose no-answer default is `unavailable`; `never` deterministically returns `rejected` without dispatching any answerer. The effective value is the last `approval/policy` event in the session log, falling back to the service config. `setApprovalPolicy(session, policy)` is the single write path, so replay reconstructs the override.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A session's approval policy — what happens to an {@link ApprovalService}
|
||||
* ask BEFORE any interactive answerer sees it:
|
||||
*
|
||||
* - `'ask'` (the default) — delegate to the composed answerers; with none
|
||||
* composed the chain falls through to the fail-closed `'unavailable'`
|
||||
* (exactly today's behavior).
|
||||
* - `'never'` — never prompt anyone: every ask resolves `'rejected'`
|
||||
* deterministically. The strict headless stance (CI, unattended runs) and
|
||||
* the only policy value stated in the system prompt — unlike `'ask'`, its
|
||||
* outcome is knowable without asking, so stating it cannot overclaim.
|
||||
*/
|
||||
type ApprovalPolicy = 'ask' | 'never'
|
||||
```
|
||||
|
||||
@@ -33,6 +53,10 @@ The prompt section states the deterministic `never` behavior and records either
|
||||
`ApprovalRequest` identifies the agent and tool action closely enough to route and audit the question. It deliberately omits tool arguments: an answerer attaches the prompt to the already-streamed tool call through `callId` instead of rendering a second copy that could drift.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Readonly same-process permission question. `callId` links to an already
|
||||
* presented tool call, so arguments are not duplicated here.
|
||||
*/
|
||||
interface ApprovalRequest {
|
||||
/**
|
||||
* The agent on whose behalf the question is asked. Routes the question (a
|
||||
|
||||
@@ -9,10 +9,12 @@ Source: [`packages/bash/bash/src/types.ts`](../../packages/bash/bash/src/types.t
|
||||
`DSH_*` variables are Harness-owned child-process facts. The model-facing bash tool collects them through `ctx.bashEnv` and passes them through `BashExecRequest.dshEnv`; executors remove inherited `DSH_*` names before merging the current snapshot.
|
||||
|
||||
```ts type-equiv
|
||||
/** One environment key inside the managed {@link DSH_ENV_PREFIX} namespace. */
|
||||
type DshEnvironmentKey = `${typeof DSH_ENV_PREFIX}${string}`
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Trusted DeepSeek Harness variables for one bash execution. */
|
||||
type DshEnvironment = Readonly<Record<DshEnvironmentKey, string>>
|
||||
```
|
||||
|
||||
@@ -21,6 +23,12 @@ type DshEnvironment = Readonly<Record<DshEnvironmentKey, string>>
|
||||
The seam separates the **model-/plugin-facing request** (optional `workdir`/`timeoutMs`/`stdoutMaxBytes`, filled from config or request policy) from the **fully-resolved spec** the executor acts on (those fields required). The tool layer calls `ctx.bash.resolve(request)` between them — this is the repo's "explicit > implicit at package seams" rule made concrete: the reader of a `BashExecSpec` never wonders where the working directory or output budget came from.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A caller's execution REQUEST: `workdir` and `timeoutMs` are optional and
|
||||
* filled by {@link BashExecutor.resolve} from the implementation's config.
|
||||
* This is the model-/plugin-facing shape; pass it to `resolve()` to obtain a
|
||||
* fully-resolved {@link BashExecSpec}.
|
||||
*/
|
||||
interface BashExecRequest {
|
||||
command: string
|
||||
/** Working directory override (default: implementation-configured). */
|
||||
@@ -59,24 +67,17 @@ interface BashExecRequest {
|
||||
* reject non-`DSH_*` names supplied through this managed channel.
|
||||
*/
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/**
|
||||
* Explicit per-call sandbox-policy input, overriding the executor's
|
||||
* configured default mode for THIS call. Never a silent default: a
|
||||
* consumer sets it only from an explicit policy source — an
|
||||
* `'allowed-once'` grant a human just issued through `ctx.approval` (the
|
||||
* escalation flow in the sandbox RFC § Escalation, which outranks), or the
|
||||
* session's standing override folded from its own `bash/sandbox-mode`
|
||||
* events (the sandbox RFC § Per-session mode switching — the user's recorded per-session
|
||||
* choice). A sandboxing executor confines THIS call under the given mode;
|
||||
* a non-sandboxing executor carries the field and confines nothing (the
|
||||
* tool layer stamps neither escalation nor overrides without a sandboxing
|
||||
* executor — see {@link BashExecutor.sandboxMode}).
|
||||
*/
|
||||
/** Explicit per-call sandbox mode override. */
|
||||
sandboxMode?: SandboxMode | undefined
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A resolved execution spec. {@link BashExecutor.resolve} fills and caps the
|
||||
* required fields; {@link BashExecutor.start} ignores `timeoutMs` because
|
||||
* background processes have no executor timeout.
|
||||
*/
|
||||
interface BashExecSpec {
|
||||
command: string
|
||||
workdir: string
|
||||
@@ -88,31 +89,18 @@ interface BashExecSpec {
|
||||
stdoutMaxBytes: number
|
||||
/** Abort signal — implementations kill the command when it fires. */
|
||||
signal?: AbortSignal | undefined
|
||||
/**
|
||||
* Bytes to write to the command's stdin (then close it), carried through
|
||||
* verbatim from {@link BashExecRequest.stdin}. It has no config default, so
|
||||
* a missing value means "no stdin" and remains an ordinary optional.
|
||||
*/
|
||||
/** Bytes to write to stdin before closing it; absent means no stdin. */
|
||||
stdin?: string | undefined
|
||||
/**
|
||||
* Extra environment entries, carried through verbatim from
|
||||
* {@link BashExecRequest.env} and merged by the implementation AFTER its
|
||||
* credential scrub (an explicit entry wins even when its name matches the
|
||||
* scrub pattern). OPTIONAL on the spec for the same reason as `stdin` — no
|
||||
* config default, absent means "no extra env".
|
||||
* Ordinary environment entries carried through from
|
||||
* {@link BashExecRequest.env}. `DSH_*` remains reserved for {@link dshEnv}.
|
||||
* OPTIONAL on the spec for the same reason as `stdin`: absent means no
|
||||
* ordinary extra environment.
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/** Managed `DSH_*` snapshot; implementations reject ordinary names. */
|
||||
dshEnv?: DshEnvironment | undefined
|
||||
/**
|
||||
* The sandbox mode this call executes under, required-but-nullable so every
|
||||
* resolved spec states its policy. A sandboxing executor's `resolve()` stamps
|
||||
* the effective mode (the request's explicit override, else its configured
|
||||
* default) so `run()`/`start()` read the spec, never the config;
|
||||
* a non-sandboxing executor carries the request value through verbatim and
|
||||
* ignores it (`undefined` under such an executor means what its README says:
|
||||
* unconfined execution).
|
||||
*/
|
||||
/** Resolved sandbox mode; ignored by executors that do not confine. */
|
||||
sandboxMode: SandboxMode | undefined
|
||||
}
|
||||
```
|
||||
@@ -126,24 +114,31 @@ interface BashExecSpec {
|
||||
The outcome of one completed (or killed) foreground run. Orthogonal outcomes are reported **independently** — a process can both time out AND exit 0 because it trapped the signal — so `timedOut`, `aborted`, `signal`, and `exitCode` are each their own field; a caller never reads a cut-short run as a clean success.
|
||||
|
||||
```ts type-equiv
|
||||
/** The outcome of one completed (or killed) foreground run. */
|
||||
interface BashRunResult {
|
||||
/** Exit code; null when the process died from a signal. */
|
||||
exitCode: number | null
|
||||
/** Terminating signal (e.g. 'SIGTERM'); null on normal exit. */
|
||||
signal: NodeJS.Signals | null
|
||||
/** True when the executor's own timeout killed the command. */
|
||||
/**
|
||||
* True when the executor's own timeout was the FIRST cause to cut the command
|
||||
* short. Mutually exclusive with {@link aborted}: one fused deadline drives
|
||||
* both the timeout and the caller's cancellation, so a timeout and an abort
|
||||
* racing before process close report the single first-abort cause, not both
|
||||
* (see the [timeout-library RFC](../../../../docs/rfc/implemented/architecture/2026-07-06-timeout-deadline-library.md)).
|
||||
*/
|
||||
timedOut: boolean
|
||||
/** True when the caller's AbortSignal killed the command. */
|
||||
/**
|
||||
* True when the caller's `AbortSignal` was the FIRST cause to kill the command
|
||||
* (and it was not the executor's own timeout). Mutually exclusive with
|
||||
* {@link timedOut} — see there for the first-cause classification.
|
||||
*/
|
||||
aborted: boolean
|
||||
/** The effective timeout applied to this run (after defaulting/capping). */
|
||||
timeoutMs: number
|
||||
stdout: CollectedOutput
|
||||
stderr: CollectedOutput
|
||||
/**
|
||||
* Sandbox facts, present iff a sandboxing executor ran the command — an
|
||||
* unsandboxed executor (e.g. `dsh-bash-local`) never sets it. See
|
||||
* {@link BashSandboxInfo} for the `denied` classification semantics.
|
||||
*/
|
||||
/** Sandbox execution facts, absent for an unsandboxed executor. */
|
||||
sandbox?: BashSandboxInfo
|
||||
}
|
||||
```
|
||||
@@ -151,6 +146,7 @@ interface BashRunResult {
|
||||
Each stream is a `CollectedOutput` — the (possibly truncated) text plus recovery info. When truncated, `text` is the **tail** and the complete stream spills to a private file:
|
||||
|
||||
```ts type-equiv
|
||||
/** One captured stream: the (possibly truncated) text plus recovery info. */
|
||||
interface CollectedOutput {
|
||||
/** Collected text — the TAIL of the stream when truncated. */
|
||||
text: string
|
||||
@@ -168,36 +164,19 @@ A sandbox-consuming executor exposes its configured fallback through `BashExecut
|
||||
A sandboxed run reports its mode, conservative denial classification, and enforcement completeness. `runnerFailed` marks a sandbox runner failure before the command ran; foreground execution throws `SANDBOX_UNAVAILABLE`, while a settled background process has only its facts channel.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Sandbox facts for one run, present iff a sandboxing executor handled it.
|
||||
* Facts are reported independently of process exit status so callers can
|
||||
* distinguish command failures from policy denials and runner failures.
|
||||
*/
|
||||
interface BashSandboxInfo {
|
||||
/** The mode the command actually ran under. */
|
||||
mode: SandboxMode
|
||||
/**
|
||||
* True when the executor classifies this run's failure as the sandbox
|
||||
* denying a file operation. The classification is CONSERVATIVE (a failed
|
||||
* exit whose stderr carries a filesystem-permission signature) and reads
|
||||
* the COLLECTED stderr — the bounded in-memory tail per
|
||||
* {@link CollectedOutput} semantics, so a signature that survives only in a
|
||||
* spill file is missed toward `denied: false`. A plain command failure
|
||||
* keeps `denied: false` even under a sandboxed mode.
|
||||
*/
|
||||
/** Whether the sandbox denied a file operation. */
|
||||
denied: boolean
|
||||
/**
|
||||
* How completely the runner enforced `mode`'s file effects — see
|
||||
* {@link SandboxEnforcement}. Absent exactly when `mode` is
|
||||
* `danger-full-access`: nothing is confined, so there is no enforcement to
|
||||
* report.
|
||||
*/
|
||||
/** How completely the selected runner enforced the requested mode. */
|
||||
enforcement?: SandboxEnforcement
|
||||
/**
|
||||
* True when the executor classifies this failure as the SANDBOX RUNNER
|
||||
* itself failing (missing binary, refused profile, fail-closed refusal
|
||||
* before exec) — the command NEVER RAN; this is a sandbox failure, not a
|
||||
* task failure, and it outranks `denied` (a runner's own error text can
|
||||
* contain denial words). Only ever stamped on settled BACKGROUND tasks: a
|
||||
* foreground run surfaces the same condition as the thrown
|
||||
* `SANDBOX_UNAVAILABLE` error instead (the foreground path has an error
|
||||
* channel; a settled task's facts are its only channel).
|
||||
*/
|
||||
/** Whether the sandbox runner failed before the command could run. */
|
||||
runnerFailed?: boolean
|
||||
}
|
||||
```
|
||||
@@ -209,6 +188,11 @@ One more piece completes the vocabulary: the `SANDBOX_UNAVAILABLE` error code (o
|
||||
`start()` returns a handle with no id or owner. `dsh-tool-bash` adapts it into `ctx.tasks.start()` hooks; the generic runtime then owns task identity and lifecycle. `done` resolves when the process closes and never rejects, reads remain valid after settlement, and sandbox facts are stamped before `done` resolves.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A background process handle returned by {@link BashExecutor.start}. It is the
|
||||
* only access path; buffered output remains readable after exit. Executor
|
||||
* disposal kills running processes and awaits {@link done}.
|
||||
*/
|
||||
interface BashProcess {
|
||||
/** Process lifecycle state (settled exactly once). */
|
||||
status: BashProcessStatus
|
||||
@@ -237,6 +221,7 @@ interface BashProcess {
|
||||
`readOutput()` returns the incremental delta and spill recovery facts:
|
||||
|
||||
```ts type-equiv
|
||||
/** One incremental {@link BashProcess.readOutput} read. */
|
||||
interface BashProcessRead {
|
||||
/** Output produced since the previous read (stderr in a marked section). */
|
||||
delta: string
|
||||
|
||||
@@ -9,6 +9,12 @@ Source: [`packages/code-runtime/code-runtime/src/types.ts`](../../packages/code-
|
||||
A `CodeRunRequest` carries **everything the runtime acts on** — per the "explicit > implicit at package seams" rule, defaulting (time budgets, output caps) is the implementation's validated config, never a hidden `??` inside `run()`:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One run: the program source plus everything the runtime acts on. Per the
|
||||
* explicit-over-implicit convention, defaulting (time budgets, output caps)
|
||||
* is the implementation's validated config — a request carries no optional
|
||||
* tuning knobs for a hidden `??` to fill in.
|
||||
*/
|
||||
interface CodeRunRequest {
|
||||
/**
|
||||
* The program source, in the runtime's {@link ../index.ts | language}. It
|
||||
@@ -31,6 +37,11 @@ interface CodeRunRequest {
|
||||
The result reports an error as a **field**, never a rejection of `run()` — reporting a failed program is the caller's job, not an exception path (mirroring `BashExecutor.run`'s resolve-on-failure contract):
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The outcome of one run. An error is a FIELD on a resolved result, never a
|
||||
* rejection of `run()` — reporting a failed program is the caller's job, not
|
||||
* an exception path.
|
||||
*/
|
||||
interface CodeRunResult {
|
||||
/**
|
||||
* The program's completion value (its top-level `return`), when it ran to
|
||||
@@ -51,6 +62,13 @@ interface CodeRunResult {
|
||||
Each `CodeBindingNamespace` becomes one global object of async callables inside the program (the Code Mode consumer passes one: `tools`). Arguments and resolutions must be structured-cloneable — a runtime may bridge calls across a serialization boundary — and a runtime treats binding names as hostile input (`__proto__` is an ordinary own property, never a prototype collision):
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A named group of {@link CodeBindingFunction}s the runtime exposes to the
|
||||
* program as one global object (e.g. `tools`). Function names are arbitrary
|
||||
* strings — a runtime must treat names like `__proto__` or `constructor` as
|
||||
* ordinary own properties (null-prototype construction), never as prototype
|
||||
* collisions.
|
||||
*/
|
||||
interface CodeBindingNamespace {
|
||||
/** The global identifier the program sees (must be a valid JS identifier). */
|
||||
global: string
|
||||
@@ -60,6 +78,14 @@ interface CodeBindingNamespace {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One host-side function exposed to the program as an async callable. The
|
||||
* runtime bridges calls to it (possibly across a serialization boundary), so
|
||||
* `args` and the resolution value MUST be structured-cloneable; a runtime
|
||||
* rejects a non-cloneable value with a descriptive error rather than
|
||||
* corrupting the run. A rejection of this function surfaces inside the
|
||||
* program as a rejection of the corresponding call.
|
||||
*/
|
||||
type CodeBindingFunction = (args: unknown) => Promise<unknown>
|
||||
```
|
||||
|
||||
@@ -70,6 +96,16 @@ Logs are plain strings in emission order. The runtime captures the program's con
|
||||
Failure kinds are **orthogonal outcomes reported independently** (per [defensive-patterns](../defensive-patterns.md)): a budget expiry is not an exception, an abort is not a timeout, and a substrate death (e.g. OOM) is neither:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Why a run failed. The kinds are orthogonal outcomes reported independently
|
||||
* (per docs/defensive-patterns.md): a budget expiry is not an exception, an
|
||||
* abort is not a timeout, and a substrate death is neither.
|
||||
*
|
||||
* - `'exception'` — the program threw or failed to parse/transform.
|
||||
* - `'timeout'` — an implementation-owned budget expired; the message says which.
|
||||
* - `'abort'` — {@link CodeRunRequest.signal} fired.
|
||||
* - `'worker-exit'` — the execution substrate died without settling (e.g. OOM).
|
||||
*/
|
||||
interface CodeRunFailure {
|
||||
/** The failure class (see the interface doc for each kind's meaning). */
|
||||
kind: 'exception' | 'timeout' | 'abort' | 'worker-exit'
|
||||
|
||||
@@ -23,6 +23,7 @@ These variants are merged inside a `declare module '@deepseek-ai/dsh-session'` b
|
||||
What a successful compaction returns to its caller: the bookkeeping-event seqs, raw summary, shadowed range and seqs, and estimated token count.
|
||||
|
||||
```ts type-equiv
|
||||
/** Result of a successful compaction operation. */
|
||||
interface CompactionResult {
|
||||
/** The seq of the appended `compact/start` event. */
|
||||
startSeq: number
|
||||
|
||||
@@ -36,7 +36,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
|
||||
| [spill.md](spill.md) | the spill storage seam: `SaveTextSpill`, `SpillOwner`/`SpillSource`, `SpillRef`, the branded `SpillLocator` |
|
||||
| [workflow.md](workflow.md) | the workflow seam: `WorkflowStartRequest`, `WorkflowMeta`, `WorkflowRun`/`Result`, the `workflow/*` event payloads, `WorkflowError` fatality |
|
||||
|
||||
> Type definitions on this page are pasted **verbatim** from source and drift-checked by `pnpm run verify-type-equiv` (see [development.md](../development.md#documenting-types-verbatim-ts-type-equiv)). Inline JSDoc is omitted for readability; follow the source link for the full contracts.
|
||||
> Type declarations and their JSDoc on this page are pasted **verbatim** from source and drift-checked by `pnpm run verify-type-equiv` (see [development.md](../development.md#documenting-types-verbatim-ts-type-equiv)).
|
||||
|
||||
FIXME(catalog-verbs): the drift gate covers only the nouns (the pasted type shapes); every method surface on these pages is hand-written prose. core-data-structures should probably also generate the *verbs* — the public methods of the cataloged classes — so a signature change cannot silently outdate the catalog.
|
||||
|
||||
@@ -83,6 +83,7 @@ The `Branded<B>` primitive lives in its own type-only package, [dsh-brand](../..
|
||||
Source: [`packages/util/brand/src/index.ts`](../../packages/util/brand/src/index.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** A string carrying a compile-time-only brand `B`. */
|
||||
type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
```
|
||||
|
||||
@@ -95,6 +96,10 @@ A conversation is `Message`s; a message is an array of typed **content blocks**.
|
||||
Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Merge-extensible content blocks keyed by `type`. New core blocks must land
|
||||
* with adapter, UI, and compaction support.
|
||||
*/
|
||||
interface ContentBlockMap {
|
||||
'text': TextBlock
|
||||
'reasoning': ReasoningBlock
|
||||
@@ -108,6 +113,7 @@ The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBl
|
||||
A `Message` is a role plus blocks. Loop-derived assistant messages carry their durable provider/model identity and optional adapter-private replay metadata:
|
||||
|
||||
```ts type-equiv
|
||||
/** Provider ownership and adapter-private replay data for an assistant message. */
|
||||
interface AssistantProvenance {
|
||||
/** Provider route that produced the message. */
|
||||
provider: string
|
||||
@@ -123,6 +129,10 @@ interface AssistantProvenance {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A single message in a conversation history. Loop-derived assistant messages
|
||||
* always carry provenance; callers may omit it on hand-built foreign history.
|
||||
*/
|
||||
interface Message {
|
||||
role: 'system' | 'user' | 'assistant'
|
||||
content: ContentBlock[]
|
||||
@@ -134,6 +144,10 @@ interface Message {
|
||||
Where a message came from is itself a merge-extensible sum type:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Where a message (or injected content) came from.
|
||||
* Merge-extensible sum type — plugins add their own `kind`s.
|
||||
*/
|
||||
interface MessageSourceMap {
|
||||
user: { kind: 'user' }
|
||||
plugin: { kind: 'plugin'; plugin: string }
|
||||
@@ -155,6 +169,7 @@ Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts)
|
||||
Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider, and an adapter may accept unlisted model ids.
|
||||
|
||||
```ts type-equiv
|
||||
/** Display metadata for one registered provider route. */
|
||||
interface LlmProviderInfo {
|
||||
/** Provider route key used by {@link GenerateOptions.provider}. */
|
||||
id: string
|
||||
@@ -164,6 +179,7 @@ interface LlmProviderInfo {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** One adapter-discovered model; catalog membership is advisory, not request validation. */
|
||||
interface LlmModelInfo {
|
||||
/** Provider route that owns this model entry. */
|
||||
provider: string
|
||||
@@ -177,6 +193,7 @@ interface LlmModelInfo {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A single model request, fully assembled. */
|
||||
interface GenerateOptions {
|
||||
/** Registered provider route selecting the adapter instance. */
|
||||
provider: string
|
||||
@@ -212,6 +229,10 @@ interface GenerateOptions {
|
||||
Why a model response stopped is a merge-extensible reason:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Why a model response stopped.
|
||||
* Merge-extensible so adapters can surface provider-specific reasons.
|
||||
*/
|
||||
interface FinishReasonMap {
|
||||
'stop': { kind: 'stop' }
|
||||
'tool-calls': { kind: 'tool-calls' }
|
||||
@@ -226,6 +247,13 @@ interface FinishReasonMap {
|
||||
`GenerateOptions.tools` carries `ToolSchema` — the JSON-schema description of a tool, as sent to the model. It is declared in dsh-llm (not dsh-tools) precisely because it is part of the request the loop assembles every step:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* JSON-schema description of a tool, as sent to the model.
|
||||
*
|
||||
* Declared here (not in dsh-tools) because it is part of {@link GenerateOptions};
|
||||
* dsh-tools' ToolDefinition and dsh-system-prompt's PromptAssembly both import
|
||||
* it from this package.
|
||||
*/
|
||||
interface ToolSchema {
|
||||
name: string
|
||||
description: string
|
||||
@@ -247,6 +275,11 @@ On the wire, a loop-built request reads in this order: the `system` slot (the re
|
||||
FIXME(call-config-shape): revisit the exact definition of this type — which fields are genuinely epoch-level for cache purposes (`model` certainly; the sampling scalars sit here out of caution), and where provider-specific extras (reasoning options, extra body params) belong when an adapter needs them.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Provider + model + sampling scalars of one conversation's requests. Every field maps
|
||||
* 1:1 onto the same-named `GenerateOptions` field; the loop builds requests
|
||||
* from the logged header rather than accepting these per call.
|
||||
*/
|
||||
interface LlmCallConfig {
|
||||
provider: string
|
||||
model: string
|
||||
@@ -263,6 +296,19 @@ A `Session` is an **append-only log** of typed `SessionEvent`s — the single so
|
||||
Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One immutable entry in the session log.
|
||||
*
|
||||
* A proper discriminated union over `type` (not independent `type`/`data`
|
||||
* unions), so `switch (event.type)` narrows `event.data` without casts.
|
||||
*
|
||||
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
||||
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
||||
* `assistant/message`, `tool/result`, `context/message`, `steering/message`).
|
||||
* Non-surface events (boundary markers, chunks, usage, errors) never carry
|
||||
* surface metadata — the compiler enforces this at `Session.append()`
|
||||
* call sites.
|
||||
*/
|
||||
type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
[K in SessionEventType]: {
|
||||
type: K
|
||||
@@ -275,7 +321,9 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
/**
|
||||
* Seq numbers of events that are provenance sources of this event
|
||||
* (e.g. the `assistant/chunk` seqs that built an `assistant/message`,
|
||||
* or the surface nodes shadowed by a compaction replace node).
|
||||
* or the surface nodes shadowed by a compaction replace node). An
|
||||
* `assistant/message` may carry a present empty array for a known empty
|
||||
* provider stream; omission means unrecorded provenance.
|
||||
*/
|
||||
sourceEventSeqs?: number[]
|
||||
/** How this event entered the surface; absent for non-surface events. */
|
||||
@@ -295,35 +343,29 @@ Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types
|
||||
`InjectOptions` extends ordinary message attribution with context-only framing and durable model-hidden JSON metadata:
|
||||
|
||||
```ts type-equiv
|
||||
/** Options specific to durable synthetic context injection. */
|
||||
interface InjectOptions extends SendOptions {
|
||||
/** Keep the canonical context tag, or send caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Public agent handle; its concrete implementation is internal to `@deepseek-ai/dsh-agent-loop`. */
|
||||
interface Agent {
|
||||
/** The single identity shared with {@link session}. */
|
||||
readonly id: SessionId
|
||||
readonly options: AgentOptions
|
||||
readonly session: Session
|
||||
readonly status: AgentStatus
|
||||
|
||||
/**
|
||||
* The agent's scope context (`@deepseek-ai/dsh-scope`, key = this agent):
|
||||
* registrations through it — tools, prompt sections/variables, listeners,
|
||||
* restrictions — are visible to this agent only and unwind when it is
|
||||
* disposed; `agent.ctx.on('agent/…')` listeners fire only for this agent.
|
||||
*/
|
||||
/** Agent-scoped context; its contributions are agent-local, unwind on disposal, and reject registration afterward. */
|
||||
readonly ctx: Context
|
||||
|
||||
/**
|
||||
* Queue a user message. Starts a turn when idle; otherwise waits for the next
|
||||
* turn. Content and the resolved source are accepted as one detached,
|
||||
* deeply-frozen lossless-JSON record before notification or enqueue, so
|
||||
* caller or `agent/queued` listener in-place mutation cannot change later
|
||||
* log/model input. Throws synchronously when either value is not losslessly
|
||||
* JSON-serializable; `agent/prompt-submit` may still return an explicit
|
||||
* replacement.
|
||||
* Queue detached, frozen lossless-JSON input; starts a turn when idle.
|
||||
* Invalid input throws synchronously before notification or enqueue.
|
||||
*/
|
||||
send(content: ContentBlock[], options?: SendOptions): void
|
||||
|
||||
@@ -335,77 +377,25 @@ interface Agent {
|
||||
steer(content: ContentBlock[], options?: SendOptions): void
|
||||
|
||||
/**
|
||||
* Inject in-session context (file-change notices, skill content, cron
|
||||
* notifications, …): appends a `context/message` session event the next model
|
||||
* request sees at its chronological position, rendered as synthetic context
|
||||
* rather than a user prompt. The default uses the canonical context tag;
|
||||
* `options.envelope: 'raw'` preserves caller-owned framing. Does not run the
|
||||
* model.
|
||||
*
|
||||
* In an open turn, inject appends at the current log position except while
|
||||
* the current tool-call batch executes: accepted context waits FIFO until the
|
||||
* batch settles, then appends after every recorded result and before turn
|
||||
* close even when execution is interrupted.
|
||||
*
|
||||
* Turn-enclosure (the turn-enclosure RFC): an inject while a turn is open joins that turn;
|
||||
* an inject while idle wraps its `context/message` in a one-shot `injection`
|
||||
* turn (`turn/start` → `context/message` → `turn/end`) and checkpoints it for
|
||||
* durability, so every event stays inside a turn and a persistence backend
|
||||
* never loses a between-turn notice. The idle checkpoint is fire-and-forget
|
||||
* (inject is synchronous): a failing flush is reported via `agent/error`
|
||||
* (step `0`) and the logger, never thrown into the caller.
|
||||
*
|
||||
* Live-adapter review has validated the canonical tagged-envelope rendering
|
||||
* against current DeepSeek behavior; provider-specific mismatches belong in
|
||||
* that adapter, not in the canonical session vocabulary.
|
||||
* Append detached model-facing context without running the model. An open-turn
|
||||
* injection joins at the current log position unless the current tool batch is
|
||||
* executing; then it waits FIFO until that batch settles and drains before turn
|
||||
* close even when interrupted. Idle injection uses a one-shot turn and durability
|
||||
* checkpoint. Disposal awaits idle checkpoints; flush failures report through `agent/error`.
|
||||
*/
|
||||
inject(content: ContentBlock[], options?: InjectOptions): void
|
||||
|
||||
/**
|
||||
* Cancel ALL pending work for the agent. `cancel()`:
|
||||
*
|
||||
* - clears the queued FIFO (un-started prompts never run) and the steering
|
||||
* FIFO (steering for the cancelled turn is dropped, not re-enqueued);
|
||||
* - aborts the in-flight step if one is running (the turn ends `aborted`);
|
||||
* - drops a turn that is about to start (a `cancel()` landing in the
|
||||
* pre-step window — after a `send()` queued but before the loop flips to
|
||||
* `running`, or after `running` is emitted but before the first step) so
|
||||
* that queued prompt does not run and cannot be batched into the cancelled
|
||||
* turn.
|
||||
*
|
||||
* After `cancel()`, `whenIdle()` resolves on the post-cancel quiescent state.
|
||||
* `cancel()` on an idle agent with nothing queued or running is a safe no-op
|
||||
* — it does NOT arm anything that would drop a later legitimate prompt.
|
||||
* Clear queued and steering work, including work waiting to start, and abort
|
||||
* the active step. The supplied reason is preserved across pre-step and active
|
||||
* cancellation windows, and `whenIdle()` resolves after cancellation reaches
|
||||
* quiescence. Idle cancellation is a no-op and does not arm a later cancel.
|
||||
*/
|
||||
cancel(reason?: string): void
|
||||
|
||||
/**
|
||||
* Resolve once the agent has reached quiescence after settling out of
|
||||
* `running`, or immediately if it is already idle with no queued work. A
|
||||
* non-owner's quiescence-observation hook: a consumer that does NOT own the
|
||||
* agent's lifecycle awaits this to proceed only after queued/running work has
|
||||
* fully stopped, rather than returning while the driver is still streaming or
|
||||
* about to start a queued turn — without itself tearing the agent down. (A
|
||||
* lifecycle OWNER does not need it: `AgentHandle.dispose()` already awaits the
|
||||
* loop-exit promise directly as part of stopping and unregistering. So this is
|
||||
* for a non-owning observer — e.g. a test awaiting a turn to settle, or a
|
||||
* monitor — that wants the settle signal but must not dispose the agent.)
|
||||
*
|
||||
* "Quiescence", not merely "status changed": a disposed agent emits
|
||||
* `agent/status('disposed')` from inside its disposer, BEFORE the driver loop
|
||||
* has unwound — so `whenIdle()` resolving on `disposed` must wait for the loop
|
||||
* to actually exit (the implementation chains the loop-exit promise), not just
|
||||
* observe the status flip. A mid-step disposal that never reaches `idle` still
|
||||
* unblocks the await this way.
|
||||
*/
|
||||
/** Resolve at idle quiescence; disposal waits for driver exit rather than only the status transition. */
|
||||
whenIdle(): Promise<void>
|
||||
|
||||
// Subagent delegation is realized on top of this interface by the
|
||||
// `@deepseek-ai/dsh-subagent` seam, not by a method here: a backend creates
|
||||
// the child through `ctx.agents.create` (fork seeds the child Session with a
|
||||
// balanced prefix of the parent's log via `CreateAgentOptions.seed`; spawn
|
||||
// starts fresh) and drives it as an ordinary Agent handle, so steer() and
|
||||
// event subscription work uniformly. See docs/core-data-structures/subagent.md.
|
||||
}
|
||||
```
|
||||
|
||||
@@ -464,10 +454,13 @@ Each `agent/*` interception waterfall returns a small, seam-specific typed union
|
||||
Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** Model-facing context injected by a listener; `source` prevents plugin text from being labeled as user input. */
|
||||
interface HookContext {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
/** Keep the canonical context tag, or use caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
@@ -475,6 +468,11 @@ interface HookContext {
|
||||
`agent/prompt-submit` returns a `PromptDecision` (allow a drained queued message — optionally rewriting its `content` or attaching `additionalContexts` — or block it; a batch whose every prompt is blocked opens a zero-step turn that ends `rejected`):
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Prompt interception result. `allow.content` replaces the prompt and each
|
||||
* `additionalContexts` entry becomes a separate context message. `block` records a
|
||||
* durable `prompt/blocked`; an all-blocked batch ends a zero-step rejected turn.
|
||||
*/
|
||||
type PromptDecision =
|
||||
| { kind: 'allow'; content?: ContentBlock[]; additionalContexts?: HookContext[] }
|
||||
| { kind: 'block'; reason: string }
|
||||
@@ -483,6 +481,7 @@ type PromptDecision =
|
||||
`agent/turn-continuation` returns a `ContinuationDecision` (the loop's default is `continue` when the step had tool calls or steering was injected, else `stop`; a `continue` `reason` is recorded as next-step steering in the same turn and therefore carries no context envelope or metadata — the typed `/goal` pattern):
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn continuation override; a continue reason is recorded as next-step steering in the same turn. */
|
||||
type ContinuationDecision =
|
||||
| { action: 'stop' }
|
||||
| { action: 'continue'; reason?: { content: ContentBlock[]; source: MessageSource } }
|
||||
@@ -491,12 +490,18 @@ type ContinuationDecision =
|
||||
`agent/turn-stop` returns the stop-only `ContinuationStop` subset or `undefined`. The loop calls this serial checkpoint after folding the ordinary decision, its reason, and pending steering; a stop is terminal and discards pending steering.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The terminal subset of {@link ContinuationDecision}. A listener on
|
||||
* `agent/turn-stop` returns this to make the already-composed continuation
|
||||
* outcome terminal; `undefined` abstains.
|
||||
*/
|
||||
type ContinuationStop = Extract<ContinuationDecision, { action: 'stop' }>
|
||||
```
|
||||
|
||||
`agent/session-start` carries a `SessionStartSource` (why the session lifecycle began; a bridge keys its SessionStart matcher on it):
|
||||
|
||||
```ts type-equiv
|
||||
/** Why a session lifecycle began; seeded creates are `startup`, while persisted loads are `resume`. */
|
||||
type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
|
||||
```
|
||||
|
||||
|
||||
@@ -11,8 +11,17 @@ Provider source: [`packages/fs/fs/src/types.ts`](../../packages/fs/fs/src/types.
|
||||
Every operation resolves a user-supplied path to an opaque backend target first. Consumers may display `displayPath`, but must not parse `targetKey` (a branded opaque id) or assume it is a local absolute path.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A path resolved by a backend into a stable identity. `resolve()` produces
|
||||
* this; every other operation takes it.
|
||||
*/
|
||||
interface FsTarget {
|
||||
/** Opaque key for stale guards and target lookup. */
|
||||
targetKey: FsTargetKey
|
||||
/**
|
||||
* Path for model/UI-facing output. May be a local absolute path,
|
||||
* workspace-relative path, or remote URI depending on the backend.
|
||||
*/
|
||||
displayPath: string
|
||||
}
|
||||
```
|
||||
@@ -20,19 +29,40 @@ interface FsTarget {
|
||||
The backend owns file-version tokens — the freshness token a write/edit guards against. The policy plugin stores them for stale checks; consumers do not interpret them. Both ids are branded opaque strings.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Opaque key for stale guards and target lookup. The local backend uses a
|
||||
* realpath-like string; a remote backend might use a workspace URI or file id.
|
||||
* Consumers MUST NOT parse it or assume it is a local absolute path.
|
||||
*/
|
||||
type FsTargetKey = Branded<'FsTargetKey'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Opaque file-version token — the freshness token a write/edit guards against.
|
||||
* The local backend derives it from high-resolution stat identity and freshness
|
||||
* fields; a remote backend might use a revision id. The policy layer records it
|
||||
* for stale checks; consumers may display related metadata but MUST NOT
|
||||
* interpret this token.
|
||||
*/
|
||||
type FsVersion = Branded<'FsVersion'>
|
||||
```
|
||||
|
||||
`stat` returns metadata (never content), or `undefined` when the target is absent. `type` lets the tool reject directories/special files before reading, and `size` lets it choose `readText` vs `streamText` without probing by failure.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Metadata about a target — what {@link FileSystem.stat} returns. Lets the
|
||||
* policy layer reject directories/special files before reading and choose
|
||||
* `readText` vs `streamText` from `size` without probing by failure. `version`
|
||||
* is the freshness token. `undefined` from `stat` means the target is absent.
|
||||
*/
|
||||
interface FsInfo {
|
||||
/** Opaque freshness token of the target right now. */
|
||||
version: FsVersion
|
||||
/** Whether the target is a regular file, a directory, or something else. */
|
||||
type: 'file' | 'directory' | 'other'
|
||||
/** Byte size of a regular file, when the backend can report it. */
|
||||
size?: number
|
||||
}
|
||||
```
|
||||
@@ -40,9 +70,18 @@ interface FsInfo {
|
||||
`lstat` is the path-level no-follow metadata primitive. It takes a path instead of an `FsTarget` because `resolve` intentionally follows symlinks to produce stable identity; consumers that need trust-boundary checks can call `lstat` first and reject `symlink` before resolving.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Metadata about a path without following the final path component when it is a
|
||||
* symbolic link. Unlike {@link FsInfo}, this path-level probe can report
|
||||
* `symlink` so consumers with trust-boundary rules can reject repository-owned
|
||||
* links before resolving a target.
|
||||
*/
|
||||
interface FsPathInfo {
|
||||
/** Opaque freshness token of the path entry right now. */
|
||||
version: FsVersion
|
||||
/** Whether the path entry is a regular file, directory, symlink, or other. */
|
||||
type: 'file' | 'directory' | 'symlink' | 'other'
|
||||
/** Byte size of the path entry, when the backend can report it. */
|
||||
size?: number
|
||||
}
|
||||
```
|
||||
@@ -50,11 +89,20 @@ interface FsPathInfo {
|
||||
`listDir` returns direct child entries in stable name order. Each entry carries the child basename, type, resolved target, and cheap metadata when the backend can report it. It must not read file contents, so `size` is only for regular files and `version` is metadata-derived. Broken or disappeared children may be returned as `other` without metadata; permission or backend I/O failures while listing or resolving child metadata fail the whole listing with `FS_PERMISSION_DENIED` or `FS_IO_ERROR`.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One direct child returned by {@link FileSystem.listDir}. Listing returns
|
||||
* metadata and resolved targets only; it must not read file contents.
|
||||
*/
|
||||
interface FsDirEntry {
|
||||
/** Basename of the child inside the listed directory. */
|
||||
name: string
|
||||
/** Whether the child is a regular file, a directory, or something else. */
|
||||
type: 'file' | 'directory' | 'other'
|
||||
/** Resolved child target for follow-up operations. */
|
||||
target: FsTarget
|
||||
/** Opaque freshness token when the backend can report metadata cheaply. */
|
||||
version?: FsVersion
|
||||
/** Byte size of a regular file, when the backend can report it. */
|
||||
size?: number
|
||||
}
|
||||
```
|
||||
@@ -64,16 +112,33 @@ interface FsDirEntry {
|
||||
Both `writeText` and `editText` take their version guard OPTIONALLY: omit it for an unconditional (bare-provider) mutation, supply it to guard. `writeText`'s guard is an `FsWriteIntent` — `createIfAbsent` creates a missing target and rejects an existing one with `FS_NOT_OBSERVED`; `replaceIfVersion` replaces only when the target exists at the observed version, else `FS_STALE_VERSION`. Omitting `expected` unconditionally creates-or-overwrites. The union itself carries only the two guarded intents; "no guard" is expressed by omission, so write and edit share one symmetric `expected?` shape.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Guarded write intent. `createIfAbsent` rejects an existing target with
|
||||
* `FS_NOT_OBSERVED`; `replaceIfVersion` rejects absence or mismatch with
|
||||
* `FS_STALE_VERSION`. Omitting the intent from `writeText` means unconditional
|
||||
* create-or-overwrite, not a third union arm.
|
||||
*/
|
||||
type FsWriteIntent =
|
||||
| { kind: 'createIfAbsent' }
|
||||
| { kind: 'replaceIfVersion'; version: FsVersion }
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Outcome of a full-file write. */
|
||||
interface FsWriteOutcome {
|
||||
/** Whether the write created a new file or replaced an existing one. */
|
||||
operation: 'create' | 'update'
|
||||
/** Opaque version of the file after the write. */
|
||||
version: FsVersion
|
||||
/**
|
||||
* The file's content BEFORE the write, or `null` when the file did not exist
|
||||
* (a create) or was undiffable (binary/non-UTF-8). LF-normalized storage text
|
||||
* (the diff basis), never a diff — a consumer computes the result-time
|
||||
* contextual diff from `before`/`after` when `before` is present, else falls
|
||||
* back to a whole-file diff.
|
||||
*/
|
||||
before: string | null
|
||||
/** The file's content AFTER the write, LF-normalized to share `before`'s diff basis. */
|
||||
after: string
|
||||
}
|
||||
```
|
||||
@@ -81,17 +146,29 @@ interface FsWriteOutcome {
|
||||
`editText` is a provider-level mutation, not a `read` plus `write` composed elsewhere. When guarded it verifies the expected version BEFORE literal matching (so a stale edit reports `FS_STALE_VERSION`, not a match failure against newer content); unguarded it edits the current content. Either way it applies the replacement and writes atomically — keeping matching, line-ending handling, the stale check, and atomic replacement inside one mutation critical section — and a missing target reports `FS_STALE_VERSION` on both paths.
|
||||
|
||||
```ts type-equiv
|
||||
/** A literal-replacement edit request. */
|
||||
interface FsEditRequest {
|
||||
/** Literal non-empty text to replace. Must match exactly (after line-ending normalization). */
|
||||
oldString: string
|
||||
/** Literal replacement text. An empty string deletes the matched text. */
|
||||
newString: string
|
||||
/** Replace every match instead of requiring exactly one. */
|
||||
replaceAll: boolean
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Outcome of a literal edit. */
|
||||
interface FsEditOutcome {
|
||||
/** Opaque version of the file after the edit. */
|
||||
version: FsVersion
|
||||
/**
|
||||
* The file's content BEFORE the edit. Raw storage text (LF-normalized by the
|
||||
* backend), never a diff — a consumer computes the result-time contextual diff
|
||||
* (the applied hunk with context) from `before`/`after`.
|
||||
*/
|
||||
before: string
|
||||
/** The file's content AFTER the edit. */
|
||||
after: string
|
||||
}
|
||||
```
|
||||
@@ -107,8 +184,20 @@ interface FsEditOutcome {
|
||||
The policy plugin needs just enough execution context to derive the observed-state owner by narrowing the opaque `object` actor the `fs/*` events carry. `ToolExecution` satisfies this shape, so `dsh-tool-fs` passes its execution object through as the actor without making `dsh-fs-policy` import the tool, agent, or session packages.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Minimal structural view of a tool execution the policy plugin needs to derive
|
||||
* an observed-state owner. `@deepseek-ai/dsh-tools`' `ToolExecution` satisfies
|
||||
* this shape, so the tool passes its `exec` straight through as the opaque
|
||||
* `object` actor on the `fs/*` events; this plugin narrows that actor to this
|
||||
* shape without importing `dsh-tools`, `dsh-agent`, or `dsh-session`.
|
||||
*
|
||||
* The owner is `agent.session` when present. It is treated as an opaque object
|
||||
* identity (a `WeakMap` key); this package never reads any of its fields.
|
||||
*/
|
||||
interface FsPolicyExec {
|
||||
/** The agent on whose behalf the call runs, when there is one. */
|
||||
agent?: {
|
||||
/** The session that owns observed-file state, used as an opaque key. */
|
||||
session?: object
|
||||
}
|
||||
}
|
||||
@@ -119,10 +208,15 @@ interface FsPolicyExec {
|
||||
A text read is bounded by line window, byte cap, and backend limits. The outcome the model-facing `read` tool renders is purely presentational; there is no `full`/`partial` view — authorization is freshness-based (the tool emits `fs/observed` with the stat's version directly), so any windowed read can authorize a later write/edit when the file is unchanged. Read windowing and this outcome shape live in `dsh-tool-fs` (the executor that owns the read), not in the policy plugin.
|
||||
|
||||
```ts type-equiv
|
||||
/** Outcome of a bounded text read — what {@link formatReadOutput} renders. */
|
||||
interface FileReadOutcome {
|
||||
/** 1-based first line requested. */
|
||||
offset: number
|
||||
/** Returned lines, already numbered. */
|
||||
lines: FileTextLine[]
|
||||
/** Total line count in the file, unless `truncatedByBytes` stopped scanning early. */
|
||||
totalLines: number
|
||||
/** Whether selected output hit the byte cap before EOF or the requested limit. */
|
||||
truncatedByBytes?: true
|
||||
}
|
||||
```
|
||||
@@ -136,6 +230,11 @@ Observed state is a `WeakMap<owner, Map<targetKey, { version }>>` held inside th
|
||||
Filesystem failures use stable `FsErrorCode` strings carried by `FsError` (`HarnessError`). The tool registry preserves `{ name, code }` on error results, so retry, permission, and UI layers can branch without parsing text.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Stable, machine-routable codes for filesystem failures. Carried on
|
||||
* {@link FsError}; the tool registry surfaces `{ name, code }` on `isError`
|
||||
* results so retry/permission/UI layers can branch without parsing messages.
|
||||
*/
|
||||
type FsErrorCode =
|
||||
| 'FS_NOT_FOUND'
|
||||
| 'FS_NOT_DIRECTORY'
|
||||
|
||||
@@ -9,6 +9,13 @@ Source: [`packages/llm/llm/src/types.ts`](../../packages/llm/llm/src/types.ts)
|
||||
A streaming response interleaves several typed blocks (text, reasoning, multiple tool calls). `index` ties each delta to its block; `block-end` carries the fully-assembled `ContentBlock` so consumers don't have to re-assemble deltas themselves. It is a **closed** discriminated union — a `switch` over `type` ends with `assertNever`, so adding a variant breaks compilation at every consumer that must handle it.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Raw streaming protocol emitted by adapters.
|
||||
* Block indexes correlate interleaved deltas, and `block-end` carries the
|
||||
* assembled block. Adapters emit usage before the terminal finish and nothing
|
||||
* afterward; tool arguments remain raw JSON strings. Failures either throw or
|
||||
* end with `error`/`aborted`, and consumers must handle both paths.
|
||||
*/
|
||||
type StreamChunk =
|
||||
| { type: 'block-start'; index: number; blockType: ContentBlockType }
|
||||
| { type: 'text-delta'; index: number; text: string }
|
||||
@@ -41,9 +48,19 @@ This contract was pinned down by two deliberately independent implementations: `
|
||||
The static public application identity every adapter sends to providers ([`packages/llm/llm/src/attribution.ts`](../../packages/llm/llm/src/attribution.ts)). `attributionHeaders(identity?)` maps it to the standard `User-Agent` header only; OpenRouter-specific app attribution headers are intentionally not supported by this contract. The default `APP_IDENTITY` sources its version from the package manifest; every field is a public product fact - no secrets, paths, session ids, or per-user identifiers, and nothing per-request may influence the values. Rationale: [Mandatory `User-Agent` attribution](../rfc/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md).
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Static public application identity sent to LLM providers.
|
||||
*
|
||||
* Every field is a public product fact, safe on every request: no secrets,
|
||||
* local paths, session ids, prompt text, or per-user identifiers belong here,
|
||||
* and nothing per-request may influence the values.
|
||||
*/
|
||||
interface AppIdentity {
|
||||
/** `User-Agent` product token (lowercase, hyphenated). */
|
||||
product: string
|
||||
/** Product version; sourced from package metadata, never hand-copied. */
|
||||
version: string
|
||||
/** Public home URL of the app, used as the `User-Agent` comment. */
|
||||
url: string
|
||||
}
|
||||
```
|
||||
@@ -53,6 +70,14 @@ interface AppIdentity {
|
||||
Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached input only; cached input is reported separately, and billed input is the sum of the three. Adapters whose providers fold cache hits into a single prompt total (DeepSeek's `prompt_tokens`) subtract them back out.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Token accounting for one model call (cache fields are optional).
|
||||
*
|
||||
* Counts are DISJOINT: `inputTokens` is uncached input only; cached input is
|
||||
* reported separately as `cacheReadTokens`/`cacheWriteTokens` (billed input =
|
||||
* sum of the three). Adapters whose providers fold cache hits into a total
|
||||
* prompt count (DeepSeek's `prompt_tokens`) subtract them out.
|
||||
*/
|
||||
interface TokenUsage {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
@@ -73,6 +98,10 @@ interface TokenUsage {
|
||||
`ContentBlockType` (the key set the `index`-correlated blocks carry) derives from `ContentBlockMap`:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Merge-extensible content blocks keyed by `type`. New core blocks must land
|
||||
* with adapter, UI, and compaction support.
|
||||
*/
|
||||
interface ContentBlockMap {
|
||||
'text': TextBlock
|
||||
'reasoning': ReasoningBlock
|
||||
|
||||
@@ -17,6 +17,11 @@ A backend that reloads a log crashed mid-turn finds an open `turn/start` with no
|
||||
`SessionPersistence.locate(meta)` synchronously resolves a backend-owned independent artifact without reading, creating, or flushing it. JSONL returns its absolute target path; SQLite returns `undefined` because sessions share one database. A returned path can therefore name a file that does not yet exist or lacks the current unflushed turn; it is a location hint, not authorization or a freshness guarantee.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A backend-resolved, per-session local artifact location. The path is an
|
||||
* absolute target path and can name an artifact that has not materialized yet.
|
||||
* Consumers must treat it as a location hint, never as an authorization token.
|
||||
*/
|
||||
interface SessionLocation {
|
||||
/** Backend-specific artifact kind, for example `jsonl`. */
|
||||
readonly kind: string
|
||||
@@ -32,6 +37,9 @@ Per-session metadata travels **separately** from the event log: format version,
|
||||
Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Immutable validated storage metadata, kept outside the conversation event log.
|
||||
*/
|
||||
interface SessionHeader {
|
||||
/**
|
||||
* On-disk format version, stamped from {@link SESSION_FORMAT_VERSION} when the
|
||||
@@ -48,13 +56,8 @@ interface SessionHeader {
|
||||
/** The session this one was forked from (seed lineage), if any. */
|
||||
readonly parentSession?: SessionId
|
||||
/**
|
||||
* How many leading events were INHERITED via a seed rather than produced by
|
||||
* this session — the seed boundary. Set when a fork seeds a child with a
|
||||
* prefix of the parent's log (= the seeded prefix length); absent/0 means the
|
||||
* session produced all its own events. Persisted so a reload reconstructs the
|
||||
* boundary instead of re-deriving it from the full stored log, and so a replay
|
||||
* harness can skip the inherited prefix when deriving the child's OWN script
|
||||
* (the seeded events are the parent's, not this child's model calls).
|
||||
* How many leading events were inherited through a seed. Persisting this
|
||||
* boundary lets resume and replay distinguish parent history from child work.
|
||||
*/
|
||||
readonly seedLength?: number
|
||||
}
|
||||
@@ -65,20 +68,17 @@ interface SessionHeader {
|
||||
Creating a `Session` through the store takes a `seed` (replay/fork an existing event log) and `meta` (the storage-level fields the store folds into a `SessionHeader`). The store fills in `version`/`id` and defaults `createdAt`; the caller supplies the validated absolute `cwd`, the `parentSession` lineage, the `seedLength` seed boundary, and — only when reconstructing a persisted session — the original `createdAt` to preserve it.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Options for creating a {@link Session} via the store. `seed` replays/forks
|
||||
* an existing event log; `meta` carries the caller-supplied storage fields the
|
||||
* store folds into a {@link SessionHeader}.
|
||||
*/
|
||||
interface CreateSessionOptions {
|
||||
/** Events to seed the new session with (replay/fork). */
|
||||
readonly seed?: readonly SessionEvent[]
|
||||
/**
|
||||
* Creation metadata. The store fills in `version`/`id` and defaults
|
||||
* `createdAt` to now; the caller supplies the storage-level fields (validated
|
||||
* absolute `cwd`, `parentSession` lineage, the seed boundary `seedLength`, and
|
||||
* — when reconstructing a persisted session — the original `createdAt` to
|
||||
* preserve it).
|
||||
*
|
||||
* `seedLength` is EXPLICIT, not inferred from `seed.length`: a reconstruction
|
||||
* (resume/load) seeds the WHOLE stored log, so its `seed.length` is the full
|
||||
* length, not the original boundary — the caller must pass the persisted
|
||||
* boundary back. A fresh fork passes its actual seeded-prefix length.
|
||||
* Storage metadata read once before publication. `seedLength` is explicit
|
||||
* because a resumed seed contains the full stored log, not only its inherited prefix.
|
||||
*/
|
||||
readonly meta?: {
|
||||
readonly cwd?: string
|
||||
|
||||
@@ -9,18 +9,30 @@ Source: [`packages/sandbox/sandbox/src/index.ts`](../../packages/sandbox/sandbox
|
||||
`SandboxMode` governs filesystem effects only. `read-only` denies writes except the required `/dev/null` sink; `workspace-write` permits writes under the workspace root and the backend's promised temp area; `danger-full-access` bypasses confinement. Network and process visibility are outside this vocabulary.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* File-effect policy for confined processes. `read-only` permits only required
|
||||
* sinks such as `/dev/null`; `workspace-write` also permits the workspace and a
|
||||
* backend-defined temp area; `danger-full-access` bypasses confinement. Network
|
||||
* and process visibility are outside this vocabulary.
|
||||
*/
|
||||
type SandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access'
|
||||
```
|
||||
|
||||
Only the first two modes can be sent to a provider. A `danger-full-access` consumer spawns its original argv and does not call `ctx.sandbox`.
|
||||
|
||||
```ts type-equiv
|
||||
/** A confining (non-`danger-full-access`) mode — the modes a {@link SandboxPolicy} can carry. */
|
||||
type ConfinedSandboxMode = Exclude<SandboxMode, 'danger-full-access'>
|
||||
```
|
||||
|
||||
Enforcement is a reported fact. `full` means the backend governs every file effect promised by the mode; `partial` means an active backend or older kernel ABI governs only a subset, so consumers that require the absolute promise must reject or surface that distinction.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Enforcement completeness for this host. `partial` means an active backend or
|
||||
* older kernel ABI cannot govern every promised file effect; callers requiring
|
||||
* an absolute boundary must not treat it as `full`.
|
||||
*/
|
||||
type SandboxEnforcement = 'full' | 'partial'
|
||||
```
|
||||
|
||||
@@ -29,6 +41,15 @@ type SandboxEnforcement = 'full' | 'partial'
|
||||
The policy is fully resolved and carried per call. This permits concurrent consumers and one-shot escalated retries to ask the same provider for different boundaries without mutating provider state.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What one confined execution is allowed to touch — carried PER CALL, not
|
||||
* fixed on the provider: two consumers may confine under different policies
|
||||
* at the same instant (bash under `read-only` while a confined child agent
|
||||
* needs its state directory writable), and an approved escalated retry is a
|
||||
* new call with a wider policy. Defaulting/resolution is the consumer's
|
||||
* explicit step (its config owns the fallback chain); the provider treats
|
||||
* the policy as fully specified.
|
||||
*/
|
||||
interface SandboxPolicy {
|
||||
/** The file-effect mode this execution runs under. */
|
||||
mode: ConfinedSandboxMode
|
||||
@@ -42,6 +63,11 @@ interface SandboxPolicy {
|
||||
`ConfinedArgv` is what the consumer spawns. Besides the replacement argv, it carries the backend's enforcement fact and two orthogonal stderr dialects. `denialSignatures` identify the confined command being blocked while the sandbox works correctly. `runnerFailureSignatures` identify the sandbox runner refusing or failing before it executes the command; consumers check these first and surface a sandbox infrastructure failure, never an ordinary task failure.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A {@link SandboxProvider.confine} result: the argv to spawn in place of
|
||||
* the caller's own, plus the enforcement completeness the selected backend
|
||||
* achieves for it.
|
||||
*/
|
||||
interface ConfinedArgv {
|
||||
/** The wrapped argv (runner, profile, separator, then the caller's argv). */
|
||||
argv: string[]
|
||||
@@ -57,17 +83,9 @@ interface ConfinedArgv {
|
||||
*/
|
||||
denialSignatures: readonly string[]
|
||||
/**
|
||||
* How the RUNNER ITSELF failing identifies itself: case-insensitive stderr
|
||||
* substrings produced when the sandbox binary is missing, refuses its
|
||||
* profile, or fails closed before exec'ing the command (`bwrap: `,
|
||||
* `landlock-run: `, `sandbox-exec: ` — each covers both the runner's own
|
||||
* error prefix and the shell's runner-not-found message). ORTHOGONAL to
|
||||
* {@link denialSignatures}: a denial is the confined COMMAND being blocked
|
||||
* (the sandbox working as designed); a runner failure means the command
|
||||
* NEVER RAN and must surface as a sandbox failure, not a task failure —
|
||||
* consumers check these signatures FIRST (a runner's own error text may
|
||||
* contain denial words, e.g. an unopenable grant root reporting
|
||||
* `Permission denied`).
|
||||
* Case-insensitive signatures for runner failure before command execution.
|
||||
* Consumers check these before denial signatures: runner failure means the
|
||||
* command never ran, while denial means confinement worked and blocked it.
|
||||
*/
|
||||
runnerFailureSignatures: readonly string[]
|
||||
}
|
||||
|
||||
@@ -9,12 +9,18 @@ Source: [`packages/core/scope/src/index.ts`](../../packages/core/scope/src/index
|
||||
`ScopeKey` is an opaque object identity. The shipped loop uses the live `Agent` object as its own key, but the primitive never inspects the object.
|
||||
|
||||
```ts type-equiv
|
||||
/** An opaque, identity-compared scope key. */
|
||||
type ScopeKey = object
|
||||
```
|
||||
|
||||
`Scoped<T>` is the compile-time brand on the opaque routing receiver returned by `scopeTarget(base, key)`. Scope-filtered event declarations require this carrier as their `this` type, while the real event subject remains an explicit argument.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A routing-only event receiver built by {@link scopeTarget}. The type
|
||||
* parameter records the subject type for dispatch checking; the carrier does
|
||||
* not expose the subject's properties. Event payloads carry the real subject.
|
||||
*/
|
||||
type Scoped<T extends object> = object & { readonly [ScopedBrand]: T }
|
||||
```
|
||||
|
||||
@@ -23,9 +29,13 @@ type Scoped<T extends object> = object & { readonly [ScopedBrand]: T }
|
||||
`Scope` pairs the tagged registration context with two teardown surfaces. `rawDispose` preserves the exact Cordis disposer identity needed by an ordered composite effect; `dispose()` is the public shared quiescence boundary for direct and racing callers.
|
||||
|
||||
```ts type-equiv
|
||||
/** A minted registration scope and its quiescent disposal boundaries. */
|
||||
interface Scope {
|
||||
/** Context through which scope-owned registrations are made. */
|
||||
ctx: Context
|
||||
/** Exact Cordis disposer, used when nesting this scope in an ordered composite effect. */
|
||||
rawDispose: () => Promise<void> | void
|
||||
/** Dispose every scope-owned registration; racing calls await the same completion. */
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -9,23 +9,34 @@ Source: [`packages/session-query/session-query/src/types.ts`](../../packages/ses
|
||||
`SessionRecord` is returned by the cross-corpus list. It exposes source availability independently from the cloned live-preferred header. `SessionEventRecord` is a lightweight raw-log projection; classification uses the same `foldSurface()` transitions as model-history derivation.
|
||||
|
||||
```ts type-equiv
|
||||
export type SessionEventSurface = 'current' | 'shadowed' | 'log-only'
|
||||
/** Whether an event is current model context, replaced context, or raw-log-only. */
|
||||
type SessionEventSurface = 'current' | 'shadowed' | 'log-only'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionRecord {
|
||||
/** Lightweight identity and source availability for one logical session. */
|
||||
interface SessionRecord {
|
||||
/** Cloned session header selected from the live-preferred corpus. */
|
||||
header: SessionHeader
|
||||
/** Whether the id currently exists in `ctx.sessions`. */
|
||||
live: boolean
|
||||
/** Whether the active persistence backend currently materializes the id. */
|
||||
persisted: boolean
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionEventRecord {
|
||||
/** Lightweight metadata for one event within a logical session. */
|
||||
interface SessionEventRecord {
|
||||
/** Session that owns the event. */
|
||||
sessionId: SessionId
|
||||
/** Monotonic event seq within the session. */
|
||||
seq: number
|
||||
/** Discriminant of the session event. */
|
||||
type: SessionEventType
|
||||
/** Event timestamp in Unix epoch milliseconds. */
|
||||
time: number
|
||||
/** Event placement in the folded session surface. */
|
||||
surface: SessionEventSurface
|
||||
}
|
||||
```
|
||||
@@ -35,24 +46,35 @@ export interface SessionEventRecord {
|
||||
`SessionLineageTrace` carries known parents in immediate-to-outward order and a forest of recursively nested direct descendants. The completeness discriminant makes a known root and a missing parent mutually exclusive.
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionLineageNode {
|
||||
/** Recursive descendant node in a session-lineage trace. */
|
||||
interface SessionLineageNode {
|
||||
/** Detached logical-corpus record for this descendant. */
|
||||
session: SessionRecord
|
||||
/** Direct children, each carrying its own recursive descendants. */
|
||||
descendants: SessionLineageNode[]
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export type SessionLineageTrace = {
|
||||
/** Known ancestry and descendants for one logical session. */
|
||||
type SessionLineageTrace = {
|
||||
/** Detached record for the session that was traced. */
|
||||
target: SessionRecord
|
||||
/** Known parents from the immediate parent outward. */
|
||||
ancestors: SessionRecord[]
|
||||
/** Complete known descendant trees rooted at the target's direct children. */
|
||||
descendants: SessionLineageNode[]
|
||||
} & (
|
||||
| {
|
||||
/** The complete parent chain is present in the logical corpus. */
|
||||
complete: true
|
||||
/** Detached record at the top of the complete lineage. */
|
||||
root: SessionRecord
|
||||
}
|
||||
| {
|
||||
/** The parent chain leaves the visible logical corpus. */
|
||||
complete: false
|
||||
/** First parent id that is not present in the logical corpus. */
|
||||
unresolvedParentId: SessionId
|
||||
}
|
||||
)
|
||||
@@ -63,20 +85,31 @@ export type SessionLineageTrace = {
|
||||
The request addresses one raw seq and optional neighboring counts. The result carries a `SessionHeader` rather than availability flags so a known live target can remain independent of persistence health.
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionEventReadRequest {
|
||||
/** Request for one event plus raw neighboring log context. */
|
||||
interface SessionEventReadRequest {
|
||||
/** Session that owns the target event. */
|
||||
sessionId: SessionId
|
||||
/** Target event seq. */
|
||||
seq: number
|
||||
/** Number of preceding raw events to include. */
|
||||
before?: number
|
||||
/** Number of following raw events to include. */
|
||||
after?: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionEventWindow {
|
||||
/** Full target event and a bounded raw-log window. */
|
||||
interface SessionEventWindow {
|
||||
/** Cloned header for the live-preferred source read. */
|
||||
session: SessionHeader
|
||||
/** Full cloned target event. */
|
||||
target: SessionEvent
|
||||
/** Full cloned events from `startSeq` through `endSeq`. */
|
||||
events: SessionEvent[]
|
||||
/** First seq included in `events`. */
|
||||
startSeq: number
|
||||
/** Last seq included in `events`. */
|
||||
endSeq: number
|
||||
}
|
||||
```
|
||||
@@ -86,19 +119,29 @@ export interface SessionEventWindow {
|
||||
Event traces distinguish positional surface replacement from logged provenance. Every seq list contains direct links except `replacementChain`, which follows immediate replacers from the target to the final positional replacement.
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionEventTraceRequest {
|
||||
/** Request for direct surface and provenance relationships around one event. */
|
||||
interface SessionEventTraceRequest {
|
||||
/** Session that owns the target event. */
|
||||
sessionId: SessionId
|
||||
/** Target event seq. */
|
||||
seq: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export interface SessionEventTrace {
|
||||
/** Direct surface and provenance relationships for one event. */
|
||||
interface SessionEventTrace {
|
||||
/** Lightweight target record. */
|
||||
target: SessionEventRecord
|
||||
/** Immediate positional replacement event, when the target was shadowed. */
|
||||
replacedBy?: number
|
||||
/** Positional replacers from the immediate replacement to the final replacement. */
|
||||
replacementChain: number[]
|
||||
/** Surface nodes directly removed when the target itself performed a replacement. */
|
||||
replacedEventSeqs: number[]
|
||||
/** Direct logged provenance sources in their recorded order. */
|
||||
sourceEventSeqs: number[]
|
||||
/** Later events that directly name the target as a provenance source, in log order. */
|
||||
derivedEventSeqs: number[]
|
||||
}
|
||||
```
|
||||
@@ -108,7 +151,8 @@ export interface SessionEventTrace {
|
||||
The closed code union distinguishes request validation, missing targets, malformed surface logs, optional-backend failure, and contradictory source metadata.
|
||||
|
||||
```ts type-equiv
|
||||
export type SessionQueryErrorCode =
|
||||
/** Stable machine-routable failure taxonomy for exact session reads and traces. */
|
||||
type SessionQueryErrorCode =
|
||||
| 'SESSION_QUERY_EVENT_NOT_FOUND'
|
||||
| 'SESSION_QUERY_INVALID_CONFIG'
|
||||
| 'SESSION_QUERY_INVALID_LINEAGE'
|
||||
|
||||
@@ -9,6 +9,7 @@ Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/t
|
||||
`ContextEnvelope` selects the standard tagged projection or preserves a producer-owned complete frame. The latter changes framing only; the event remains a user-role `context/message` in chronological history.
|
||||
|
||||
```ts type-equiv
|
||||
/** Canonical context-tag framing, or caller-owned framing rendered verbatim. */
|
||||
type ContextEnvelope = 'context' | 'raw'
|
||||
```
|
||||
|
||||
@@ -17,30 +18,43 @@ type ContextEnvelope = 'context' | 'raw'
|
||||
The append-only event types. Merge-extensible: a plugin declares extra event types via declaration merging — e.g. the [compaction seam](compaction.md) adds `compact/start` / `compact/summary` / `compact/end`, and `@deepseek-ai/dsh-hook-protocol` adds log-only `hook/invoked` / `hook/result` provenance for a hook bridge. Like `compact/*`, these are NOT `SurfaceEventType`s (no `surfaceOp`). The generated [persistence log event catalog](../persistence-catalog.md) enumerates every member — core and merged — with its payload, surface badge, and declaration site.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The merge-extensible, append-only source of truth for an agent interaction.
|
||||
* Message history is derived from this log. Every event is lossless JSON and
|
||||
* sequence numbers stay contiguous, including raw chunks, so persistence can
|
||||
* store the canonical log verbatim.
|
||||
*/
|
||||
interface SessionEventMap {
|
||||
/**
|
||||
* Opens turn `turn`. `trigger` records what started it — a drained message
|
||||
* batch or an idle-time injection. The turn is the durability/replay
|
||||
* boundary: every event sits between a `turn/start` and its matching
|
||||
* `turn/end` (the turn-enclosure invariant).
|
||||
*/
|
||||
'turn/start': { turn: number; trigger: TurnTrigger }
|
||||
/**
|
||||
* Closes turn `turn` with the {@link TurnEndReason} that ended it. The loop
|
||||
* fires the awaited `session/flush` checkpoint at every turn end, so the turn
|
||||
* boundary is also the durable-commit boundary.
|
||||
*/
|
||||
'turn/end': { turn: number; reason: TurnEndReason }
|
||||
/** Opens step `step` of turn `turn` — one model call plus the tool executions it requested. */
|
||||
'step/start': { turn: number; step: number }
|
||||
/** Closes step `step` of turn `turn`. */
|
||||
'step/end': { turn: number; step: number }
|
||||
/** A user-visible prompt (queued message drained at turn start). */
|
||||
'user/message': { content: ContentBlock[]; source: MessageSource }
|
||||
/**
|
||||
* A queued prompt an `agent/prompt-submit` listener VETOED — the durable
|
||||
* record of a blocked prompt and why. Appended in place of the `user/message`
|
||||
* the prompt would have become, so the block survives replay even in a MIXED
|
||||
* batch where another queued prompt is allowed (there the turn does not end
|
||||
* `rejected`, so the boundary reason alone would not preserve it). `content`
|
||||
* is the original prompt the listener rejected; `reason` is the veto text
|
||||
* ({@link PromptDecision} `block.reason`). NOT a {@link SurfaceEventType}: a
|
||||
* blocked prompt produces no LLM message and never reaches `deriveMessages()`.
|
||||
* Durable record of a prompt veto and its reason. It is log-only: the blocked
|
||||
* prompt never enters the model-visible surface, including in a mixed batch.
|
||||
*/
|
||||
'prompt/blocked': { content: ContentBlock[]; source: MessageSource; reason: string }
|
||||
/**
|
||||
* In-session context injection (file-change notices, subdir AGENTS.md,
|
||||
* skill content, cron notifications, …). Rendered into the derived history
|
||||
* as synthetic context — NOT a user prompt. `envelope: 'raw'` lets a caller
|
||||
* supply its own complete framing; `meta` is persisted JSON hidden from the
|
||||
* model.
|
||||
* own the complete model-facing frame; `meta` is durable JSON state omitted
|
||||
* from the model projection.
|
||||
*/
|
||||
'context/message': {
|
||||
content: ContentBlock[]
|
||||
@@ -57,34 +71,29 @@ interface SessionEventMap {
|
||||
* usage record). `usage` is absent when the adapter reported none.
|
||||
*/
|
||||
'assistant/message': { turn: number; step: number; content: ContentBlock[]; provenance: AssistantProvenance; usage?: TokenUsage }
|
||||
/**
|
||||
* The model requested one tool invocation: `name` with the raw `arguments`
|
||||
* JSON string exactly as the model produced it (unparsed). `callId` pairs the
|
||||
* call with its `tool/result`.
|
||||
*/
|
||||
'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
|
||||
/**
|
||||
* A completed tool call's model-facing result, plus an optional tool-private
|
||||
* `meta` presentation payload. `meta` is opaque to the core (`unknown` — the
|
||||
* producing tool owns its shape and reads it back in `presentResult`) but MUST
|
||||
* be JSON-serializable: `Session.append` runtime-validates all event data with
|
||||
* `isJsonValue`, so a non-serializable `meta` is rejected at the source, and the
|
||||
* durable log reproduces the identical card on replay. Absent unless the tool
|
||||
* attaches one (e.g. `dsh-tool-fs` carries its result-time contextual diff here).
|
||||
*/
|
||||
'tool/result': { turn: number; step: number; callId: CallId; content: ContentBlock[]; isError: boolean; error?: { name: string; code: string }; meta?: unknown }
|
||||
/** Steering content injected between steps of a running turn. */
|
||||
'steering/message': { turn: number; content: ContentBlock[]; source: MessageSource }
|
||||
/**
|
||||
* The agent's whole todo list, carried as a full snapshot and replaced
|
||||
* wholesale on each write — the current list is the most recent `todo/write`
|
||||
* (last-write-wins on replay, no fold). Appended by an owning agent via
|
||||
* `session.append('todo/write', { todos })`.
|
||||
*
|
||||
* NOT a {@link SurfaceEventType}: it produces no LLM message and never reaches
|
||||
* `deriveMessages()`, so it carries no `surfaceOp` and stays off the surface —
|
||||
* it is durable, replayable UI state, distinct from the conversation history.
|
||||
* It is a `SessionEventMap` member riding the existing `session/event` emit,
|
||||
* not a first-class Cordis `interface Events` notification, so it has no
|
||||
* cordis-catalog row.
|
||||
*/
|
||||
/** Whole-list snapshot; latest write wins on replay. Log-only UI state; never derived history. */
|
||||
'todo/write': { todos: TodoItem[] }
|
||||
/**
|
||||
* Full snapshot of the {@link EpochHeader} the NEXT request is built under,
|
||||
* with the {@link RequestHeaderReason} it was recorded whole. Appended by
|
||||
* the loop inside the step, before dispatch, on a loop instance's first
|
||||
* request-building step (`'initial'`/`'resume'`) or when a later request's
|
||||
* header changes (`'change'`); always records what the request actually
|
||||
* used, post-`agent/request`. Reconstruction reads the latest snapshot. NOT a
|
||||
* {@link SurfaceEventType}: it produces no LLM message — it is the request
|
||||
* envelope, logged so every request is a pure function of the session log
|
||||
* (the reconstructability RFC).
|
||||
* Full header for the next request, appended inside its step before dispatch.
|
||||
* It is log-only; the latest snapshot reconstructs the request header.
|
||||
*/
|
||||
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
|
||||
}
|
||||
@@ -95,8 +104,21 @@ interface SessionEventMap {
|
||||
The unit of the `todo/write` event's whole-list snapshot. Deliberately minimal — a `content` line and a three-state `status` (no id, priority, or `activeForm`): the list is replaced wholesale on every write, so entries need no stable identity, and the status triple is exactly the ACP `PlanEntryStatus`, so a UI bridge can map a todo list onto an ACP `plan` 1:1 (synthesizing the priority ACP additionally requires). See the [todo_write RFC](../rfc/implemented/feature/2026-06-29-todo-write-tool.md).
|
||||
|
||||
```ts type-equiv
|
||||
export interface TodoItem {
|
||||
/**
|
||||
* One entry in an agent's todo list — the unit of the `todo/write`
|
||||
* {@link SessionEventMap} event's whole-list snapshot.
|
||||
*
|
||||
* Deliberately minimal: a human-readable `content` line and a three-state
|
||||
* `status`. No id, priority, or `activeForm` — the list is replaced wholesale
|
||||
* on every write (last-write-wins), so entries need no stable identity, and the
|
||||
* status triple is exactly the ACP `PlanEntryStatus`, so a UI bridge can map a
|
||||
* todo list onto an ACP `plan` 1:1 (synthesizing the priority ACP additionally
|
||||
* requires).
|
||||
*/
|
||||
interface TodoItem {
|
||||
/** What this task is — a short imperative line shown in the UI. */
|
||||
content: string
|
||||
/** Lifecycle state. `in_progress` marks the single task being worked now. */
|
||||
status: 'pending' | 'in_progress' | 'completed'
|
||||
}
|
||||
```
|
||||
@@ -106,8 +128,13 @@ export interface TodoItem {
|
||||
The request envelope — the `EpochHeader` (call config + rendered system prompt + assembled tool schemas + the session prefix) — is logged session state, so every conversation request is a pure function of the log (the reconstructability RFC). A full `request/header` snapshot with reason `'initial'` or `'resume'` records each loop-instance boundary; a later changed request records another full snapshot with reason `'change'`. `foldRequestHeader(events)` reconstructs the header by selecting the latest snapshot. The event is not a `SurfaceEventType`: it produces no LLM message.
|
||||
|
||||
```ts type-equiv
|
||||
export interface EpochHeader {
|
||||
/** The conversation's call configuration (provider + model + sampling scalars). */
|
||||
/**
|
||||
* Logged request state outside derived history: call config, system prompt,
|
||||
* tools, and prefix. The latest full `request/header` snapshot reconstructs it;
|
||||
* canonical empty optional fields are absent.
|
||||
*/
|
||||
interface EpochHeader {
|
||||
/** The conversation's call configuration (provider, model, and sampling scalars). */
|
||||
config: LlmCallConfig
|
||||
/** Rendered system prompt text; absent for a system-less request. */
|
||||
system?: string
|
||||
@@ -131,6 +158,19 @@ Canonical form: an empty system prompt, an empty tool list, and an empty session
|
||||
A proper discriminated union over `type` (not independent `type`/`data` unions), so `switch (event.type)` narrows `event.data` without casts. `seq` is the monotonic position in the log (`seq = log.length`); `time` is epoch ms.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One immutable entry in the session log.
|
||||
*
|
||||
* A proper discriminated union over `type` (not independent `type`/`data`
|
||||
* unions), so `switch (event.type)` narrows `event.data` without casts.
|
||||
*
|
||||
* The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
|
||||
* they only exist on {@link SurfaceEventType} variants (`user/message`,
|
||||
* `assistant/message`, `tool/result`, `context/message`, `steering/message`).
|
||||
* Non-surface events (boundary markers, chunks, usage, errors) never carry
|
||||
* surface metadata — the compiler enforces this at `Session.append()`
|
||||
* call sites.
|
||||
*/
|
||||
type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
[K in SessionEventType]: {
|
||||
type: K
|
||||
@@ -143,7 +183,9 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
/**
|
||||
* Seq numbers of events that are provenance sources of this event
|
||||
* (e.g. the `assistant/chunk` seqs that built an `assistant/message`,
|
||||
* or the surface nodes shadowed by a compaction replace node).
|
||||
* or the surface nodes shadowed by a compaction replace node). An
|
||||
* `assistant/message` may carry a present empty array for a known empty
|
||||
* provider stream; omission means unrecorded provenance.
|
||||
*/
|
||||
sourceEventSeqs?: number[]
|
||||
/** How this event entered the surface; absent for non-surface events. */
|
||||
@@ -163,7 +205,12 @@ The five message-producing types (`SurfaceEventType` — `user/message`, `assist
|
||||
### `SurfaceEventType` — the message-producing subset of event types
|
||||
|
||||
```ts type-equiv
|
||||
export type SurfaceEventType =
|
||||
/**
|
||||
* The subset of {@link SessionEventType} values whose events produce LLM
|
||||
* messages and are eligible to appear on the ordered surface. Only these
|
||||
* event types may carry {@link SurfaceOp} and {@link SessionEvent.sourceEventSeqs}.
|
||||
*/
|
||||
type SurfaceEventType =
|
||||
| 'user/message'
|
||||
| 'assistant/message'
|
||||
| 'tool/result'
|
||||
@@ -174,7 +221,19 @@ export type SurfaceEventType =
|
||||
### `SurfaceOp` — how an event entered the surface
|
||||
|
||||
```ts type-equiv
|
||||
export type SurfaceOp =
|
||||
/**
|
||||
* How a session event entered the ordered surface. Only valid on
|
||||
* {@link SurfaceEventType} events.
|
||||
*
|
||||
* - `'append'`: added to the tail — normal path for user/assistant/tool/context
|
||||
* messages.
|
||||
* - `{ op: 'replace', start, end }`: replaces surface nodes from `start`
|
||||
* (inclusive) through `end` (inclusive) with this node. Both must exist as
|
||||
* surface nodes in the current surface. `start === end` replaces a single
|
||||
* node. The node's {@link SessionEvent.sourceEventSeqs} must include every
|
||||
* shadowed surface node. Used by compaction and possible other manipulations.
|
||||
*/
|
||||
type SurfaceOp =
|
||||
| 'append'
|
||||
| { op: 'replace'; start: number; end: number }
|
||||
```
|
||||
@@ -184,8 +243,18 @@ export type SurfaceOp =
|
||||
### `SurfaceIntent` — the parameter to `session.append()`
|
||||
|
||||
```ts type-equiv
|
||||
export interface SurfaceIntent {
|
||||
/**
|
||||
* Surface placement and provenance for {@link Session.append}. Required on
|
||||
* message-producing events and forbidden on log-only events.
|
||||
*/
|
||||
interface SurfaceIntent {
|
||||
surfaceOp: SurfaceOp
|
||||
/**
|
||||
* Complete known provenance source set. `assistant/message` may use a
|
||||
* present empty array for a known empty provider stream; omission means its
|
||||
* provenance was not recorded. Other surface events require a non-empty set
|
||||
* when this field is present.
|
||||
*/
|
||||
sourceEventSeqs?: number[]
|
||||
}
|
||||
```
|
||||
@@ -194,22 +263,44 @@ Required for `SurfaceEventType` events — every message-producing event must de
|
||||
|
||||
The same provenance distinction applies here: only `assistant/message` may carry a present empty `sourceEventSeqs`; omission does not assert that its source stream was empty.
|
||||
|
||||
### `SurfaceFoldReplacement` and `SurfaceFoldResult` — a complete surface replay
|
||||
### `SessionSurface` — the live readonly surface projection
|
||||
|
||||
`foldSurface(events)` returns detached current event sequences together with the actual sequences shadowed by each declared replacement range. `SurfaceManager` uses the same transitions for its incremental cache without retaining replacement history. Its `replaceGeneration` increments for each replacement so incremental consumers can distinguish pure tail growth from a rewrite.
|
||||
`Session.surface` returns the session's stable `SessionSurface` view. The same incremental manager validates append candidates before commit and advances this projection from committed events; callers can observe membership and replacement generation but cannot invoke validation.
|
||||
|
||||
```ts type-equiv
|
||||
export interface SurfaceFoldReplacement {
|
||||
/** Readonly live projection of the message-producing session events. */
|
||||
interface SessionSurface {
|
||||
/** Current surface event sequences in model-visible order. */
|
||||
readonly nodes: readonly number[]
|
||||
/** Monotonic count of committed positional replacements. */
|
||||
readonly replaceGeneration: number
|
||||
}
|
||||
```
|
||||
|
||||
### `SurfaceFoldReplacement` and `SurfaceFoldResult` — a complete surface replay
|
||||
|
||||
`foldSurface(events)` returns detached current event sequences together with the actual sequences shadowed by each declared replacement range. The live manager uses the same transitions without retaining replacement history. Its `replaceGeneration` increments for each committed replacement so incremental consumers can distinguish pure tail growth from a rewrite.
|
||||
|
||||
```ts type-equiv
|
||||
/** One replacement operation observed while folding a session surface. */
|
||||
interface SurfaceFoldReplacement {
|
||||
/** Seq of the event that replaced the prior surface range. */
|
||||
seq: number
|
||||
/** Declared inclusive start seq of the replaced surface range. */
|
||||
start: number
|
||||
/** Declared inclusive end seq of the replaced surface range. */
|
||||
end: number
|
||||
/** Actual surface entries removed by the operation, in surface order. */
|
||||
shadowedSeqs: number[]
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
export interface SurfaceFoldResult {
|
||||
/** Complete result of replaying the surface operations in a session log. */
|
||||
interface SurfaceFoldResult {
|
||||
/** Current surface event sequences in model-visible order. */
|
||||
nodes: number[]
|
||||
/** Replacement operations in event order. */
|
||||
replacements: SurfaceFoldReplacement[]
|
||||
}
|
||||
```
|
||||
@@ -237,6 +328,10 @@ An explicit `boundary` lets callers fork from a previous completed turn even if
|
||||
## What started a turn: `TurnTriggerMap`
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What started a turn.
|
||||
* Merge-extensible sum type (same pattern as MessageSourceMap).
|
||||
*/
|
||||
interface TurnTriggerMap {
|
||||
message: { kind: 'message'; source: MessageSource }
|
||||
/**
|
||||
@@ -254,6 +349,9 @@ interface TurnTriggerMap {
|
||||
## Why a turn ended: `TurnEndReasonMap`
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Why a turn ended. Merge-extensible sum type.
|
||||
*/
|
||||
interface TurnEndReasonMap {
|
||||
completed: { kind: 'completed' }
|
||||
aborted: { kind: 'aborted'; reason?: string }
|
||||
@@ -265,26 +363,16 @@ interface TurnEndReasonMap {
|
||||
*/
|
||||
error: { kind: 'error'; step: number; message: string; code?: string }
|
||||
disposed: { kind: 'disposed' }
|
||||
/** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
|
||||
'max-tokens': { kind: 'max-tokens' }
|
||||
/**
|
||||
* The turn's entire prompt batch was BLOCKED before any step ran — every
|
||||
* drained queued message was vetoed by an `agent/prompt-submit` listener (a
|
||||
* hook). The turn still opened (so the boundary stays balanced and the block
|
||||
* is a durable in-turn fact), but ran zero steps. `reason` carries the block
|
||||
* message from the vetoing decision. Distinct from `aborted` (a user-driven
|
||||
* cancel) and `error` (a failure): the prompt was rejected by policy, not
|
||||
* interrupted or broken. A UI renders it as "prompt blocked by hook".
|
||||
* Policy blocked every prompt before the first step. The zero-step turn still
|
||||
* records a balanced durable boundary and the veto reason.
|
||||
*/
|
||||
rejected: { kind: 'rejected'; reason: string }
|
||||
/**
|
||||
* The turn never ended on its own: the process crashed mid-turn and a
|
||||
* persistence backend later closed the orphaned (open) turn on reload so the
|
||||
* log stays balanced. SYNTHESIZED by the backend's crash-recovery repair — no
|
||||
* loop ever emits this. Its events are real (they were durably appended before
|
||||
* the crash) and are PRESERVED, not discarded: a single turn can be huge in a
|
||||
* long-horizon task (many steps, large tool output), so truncating it would
|
||||
* lose real work. The marker records that the turn was cut short, not that the
|
||||
* model completed it. See the session-persistence RFC.
|
||||
* A persistence backend closed a crash-orphaned turn on reload. The loop never
|
||||
* emits this marker, and the events recorded before the crash remain intact.
|
||||
*/
|
||||
interrupted: { kind: 'interrupted' }
|
||||
}
|
||||
|
||||
@@ -11,9 +11,25 @@ Source: [`packages/skill/skill/src/index.ts`](../../packages/skill/skill/src/ind
|
||||
Duplicate names resolve by rank, provider order, then local order; summaries sort by name. A rejected `list()` is logged and skipped without caching the degraded catalog, while malformed candidates fail fast.
|
||||
|
||||
```ts type-equiv
|
||||
/** Provider interface for one source of skills, such as local directories or a remote registry. */
|
||||
interface SkillProvider {
|
||||
/** Unique provider name in the `ctx.skills` registry. */
|
||||
readonly name: string
|
||||
/**
|
||||
* List available skill candidates for the current lookup context. Provider
|
||||
* plugins register synchronously during `apply()`; remote initialization,
|
||||
* authentication, and discovery are awaited inside this method. Implementations
|
||||
* should settle promptly when `options.signal` aborts.
|
||||
* @param options - lookup options; `cwd` selects workspace-sensitive skills and `signal` cancels work.
|
||||
* @returns provider candidates with precedence ranks and opaque locators.
|
||||
*/
|
||||
readonly list: (options: SkillLookupOptions) => Promise<readonly SkillCandidate[]>
|
||||
/**
|
||||
* Load a complete skill body for a previously listed candidate.
|
||||
* @param candidate - the winning candidate originally returned by this provider.
|
||||
* @param options - lookup options; `cwd` selects workspace-sensitive skills and `signal` cancels work.
|
||||
* @returns the full skill body, or `undefined` if it is no longer loadable.
|
||||
*/
|
||||
readonly get: (candidate: SkillCandidate, options: SkillLookupOptions) => Promise<SkillDefinition | undefined>
|
||||
}
|
||||
```
|
||||
@@ -37,6 +53,7 @@ The project root is the nearest ancestor containing `.git`; without one, the cur
|
||||
Skill names are kebab-case (`^[a-z0-9]+(?:-[a-z0-9]+)*$`). The local provider accepts directory bundles (`<name>/SKILL.md`) and flat Markdown files (`<name>.md`). Nested recursive `**/SKILL.md` discovery is intentionally outside v1.
|
||||
|
||||
```ts type-equiv
|
||||
/** Origin bucket for a skill contribution. The value is prompt-visible metadata, not precedence by itself. */
|
||||
type SkillSource = 'project-dsh' | 'project-agents' | 'runtime' | 'user-dsh' | 'user-agents' | 'custom' | (string & {})
|
||||
```
|
||||
|
||||
@@ -45,13 +62,21 @@ type SkillSource = 'project-dsh' | 'project-agents' | 'runtime' | 'user-dsh' | '
|
||||
`SkillSummary` is the registry's model-invocable summary shape. Consumers choose which fields to render; the session catalog uses only `name` and `description`, never the body or absolute file path. `disableModelInvocation` hides a skill from model listings while allowing trusted code to load it by name.
|
||||
|
||||
```ts type-equiv
|
||||
/** Model-visible skill metadata returned by `ctx.skills.list()` and rendered into request guidance. */
|
||||
interface SkillSummary {
|
||||
/** Kebab-case identifier used with the `skill` tool. */
|
||||
readonly name: string
|
||||
/** Short routing description shown to the model. */
|
||||
readonly description: string
|
||||
/** Optional extra routing guidance shown to the model. */
|
||||
readonly whenToUse?: string
|
||||
/** Whether the skill is hidden from model listings while remaining loadable by trusted callers. */
|
||||
readonly disableModelInvocation?: boolean
|
||||
/** Discovery source that produced this winning skill. */
|
||||
readonly source: SkillSource
|
||||
/** Provider that owns this skill body. */
|
||||
readonly provider: string
|
||||
/** Provider-specific base for relative resources. */
|
||||
readonly resourceBase?: SkillResourceBase
|
||||
}
|
||||
```
|
||||
@@ -59,10 +84,15 @@ interface SkillSummary {
|
||||
`SkillCandidate` is the provider-to-registry shape. `locator` is opaque provider state; the registry only stores it and gives it back to the winning provider's `get()`.
|
||||
|
||||
```ts type-equiv
|
||||
/** Provider catalog entry used by the registry to merge and later load skills. */
|
||||
interface SkillCandidate extends SkillSummary {
|
||||
/** Lower ranks win duplicate skill names before provider registration order is considered. */
|
||||
readonly rank: number
|
||||
/** Opaque provider-owned handle passed back to `provider.get()`. */
|
||||
readonly locator: unknown
|
||||
/** Absolute file path when the provider has one. */
|
||||
readonly path?: string
|
||||
/** Parsed optional metadata object from provider-specific skill frontmatter. */
|
||||
readonly metadata?: Readonly<Record<string, unknown>>
|
||||
}
|
||||
```
|
||||
@@ -70,6 +100,7 @@ interface SkillCandidate extends SkillSummary {
|
||||
`SkillDefinition` is the complete parsed result returned by `ctx.skills.get()` and used by the `skill` tool. `resourceBase` tells the tool how to render relative-resource guidance for local, URL, or provider-managed skills.
|
||||
|
||||
```ts type-equiv
|
||||
/** Optional provider-specific base used by loaded skill bodies to resolve relative resources. */
|
||||
type SkillResourceBase =
|
||||
| { readonly kind: 'directory'; readonly path: string }
|
||||
| { readonly kind: 'url'; readonly url: string }
|
||||
@@ -77,9 +108,13 @@ type SkillResourceBase =
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Complete parsed skill definition, including the body loaded by `ctx.skills.get()`. */
|
||||
interface SkillDefinition extends SkillSummary {
|
||||
/** Markdown instruction body after any provider-specific metadata removal. */
|
||||
readonly content: string
|
||||
/** Absolute file path when the skill came from disk. */
|
||||
readonly path?: string
|
||||
/** Parsed optional metadata object from frontmatter. */
|
||||
readonly metadata?: Readonly<Record<string, unknown>>
|
||||
}
|
||||
```
|
||||
@@ -87,9 +122,8 @@ interface SkillDefinition extends SkillSummary {
|
||||
Runtime skills use the same complete shape and participate in the same first-wins collection order. The returned disposer removes the contribution and invalidates discovery caches.
|
||||
|
||||
```ts type-equiv
|
||||
type SkillRegistration = Omit<SkillDefinition, 'provider'> & {
|
||||
readonly provider?: string
|
||||
}
|
||||
/** Runtime skill contribution accepted by `ctx.skills.register()`. */
|
||||
type SkillRegistration = Omit<SkillDefinition, 'provider'> & { readonly provider?: string }
|
||||
```
|
||||
|
||||
## Lookup and configuration
|
||||
@@ -97,8 +131,11 @@ type SkillRegistration = Omit<SkillDefinition, 'provider'> & {
|
||||
Skill lookup is cwd-sensitive because providers may expose workspace-local skills, and its optional signal cancels provider work for the caller. Providers receive the same readonly options object used for cache identity and loading. Cancellation is checked before and after catalog selection, including cache hits, and races both discovery and full-definition loading. If no git root is found, the local provider treats the supplied cwd itself as the project root.
|
||||
|
||||
```ts type-equiv
|
||||
/** Caller context used for cwd-sensitive and abortable provider work. */
|
||||
interface SkillLookupOptions {
|
||||
/** Workspace selector for the current lookup. */
|
||||
readonly cwd?: string | undefined
|
||||
/** Abort discovery or loading work for the current caller. */
|
||||
readonly signal?: AbortSignal | undefined
|
||||
}
|
||||
```
|
||||
@@ -106,7 +143,9 @@ interface SkillLookupOptions {
|
||||
The registry owns only its discovery-cache bound. The local provider owns filesystem roots (`dshHome`, `agentsHome`, and `customSkillDirs`). The consumer owns its catalog description bound.
|
||||
|
||||
```ts type-equiv
|
||||
/** Skill registry configuration. */
|
||||
interface Config {
|
||||
/** Maximum number of completed cwd/provider catalogs kept in memory. */
|
||||
readonly collectCacheMaxEntries?: number
|
||||
}
|
||||
```
|
||||
|
||||
@@ -9,15 +9,28 @@ Source: [`packages/spill/spill/src/types.ts`](../../packages/spill/spill/src/typ
|
||||
`saveText` is the whole seam: persist `content` verbatim, return an opaque locator, a backend-supplied retrieval hint, and the exact byte count. The request carries the save-time storage namespace (`owner`), WHERE it came from (`source`, descriptive provenance for naming and inspection — not access control), and a `suggestedName` the backend may use as a naming hint (it is not a path).
|
||||
|
||||
```ts type-equiv
|
||||
/** One request to persist text to a spill artifact. */
|
||||
interface SaveTextSpill {
|
||||
owner: SpillOwner
|
||||
source: SpillSource
|
||||
/**
|
||||
* A caller-suggested base name (e.g. `web_fetch.txt`). The backend sanitizes
|
||||
* it to a single safe path segment before use — it is a hint, never a path.
|
||||
*/
|
||||
suggestedName: string
|
||||
/** The full text to persist (UTF-8). */
|
||||
content: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Save-time storage namespace for a spilled artifact. The session id lets a
|
||||
* backend group storage under the producing session, but the returned
|
||||
* {@link SpillLocator} is the model-facing handle. Forked sessions inherit
|
||||
* locators already present in the seeded log; those artifacts are not copied or
|
||||
* re-owned, and spills produced after the fork use the child session id.
|
||||
*/
|
||||
interface SpillOwner {
|
||||
sessionId: SessionId
|
||||
}
|
||||
@@ -26,9 +39,17 @@ interface SpillOwner {
|
||||
`SpillOwner.sessionId` is the save-time storage namespace. Forked sessions inherit existing spill locators from the seeded log; those artifacts are not copied or re-owned, and spills produced after the fork use the child session id. A retention-period cleanup may expire old locators with other old session artifacts; the spill seam does not define a per-session cleanup policy.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Provenance of one spilled artifact — recorded by the backend for a readable
|
||||
* filename and inspection. Not interpreted for access control; purely
|
||||
* descriptive.
|
||||
*/
|
||||
interface SpillSource {
|
||||
/** The tool whose result was spilled (e.g. `web_fetch`). */
|
||||
toolName: string
|
||||
/** The model-issued call id the result belongs to. */
|
||||
callId: CallId
|
||||
/** A short human label for the artifact (e.g. `result`). */
|
||||
label: string
|
||||
}
|
||||
```
|
||||
@@ -36,6 +57,7 @@ interface SpillSource {
|
||||
## The result
|
||||
|
||||
```ts type-equiv
|
||||
/** A saved spill artifact: its locator, byte length, and backend-specific retrieval guidance. */
|
||||
interface SpillRef {
|
||||
locator: SpillLocator
|
||||
bytes: number
|
||||
@@ -46,6 +68,11 @@ interface SpillRef {
|
||||
`SpillLocator` is a [branded](core.md#branded-ids) model-facing handle returned by the backend. The local backend renders it as a filesystem path; a remote or database backend can render a URI, key, or command token. Consumers treat it as opaque and render it with `retrievalHint` instead of assuming `read` is always the right retrieval mechanism.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Opaque model-facing handle for one spilled artifact. A local backend may use a
|
||||
* filesystem path; a remote or database backend may use a URI or key. Consumers
|
||||
* render it with {@link SpillRef.retrievalHint}, but do not parse it.
|
||||
*/
|
||||
type SpillLocator = Branded<'SpillLocator'>
|
||||
```
|
||||
|
||||
|
||||
@@ -11,10 +11,22 @@ Source: [`packages/subagent/subagent/src/types.ts`](../../packages/subagent/suba
|
||||
A provider advertises its **start-time** features on a static descriptor the service checks BEFORE a run exists; a request that needs one the provider lacks is rejected loud (`SubagentError('UNSUPPORTED_CAPABILITY')`), never accepted-then-ignored. **Runtime** features (steering, resume) are instead optional methods on [`SubagentRun`](#a-live-run-subagentrun) — the method's presence IS the capability, and TS narrowing is the discovery mechanism.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Which START-TIME features a provider supports. Checked by the service before delegating to
|
||||
* {@link SubagentProvider.start}: a request that needs a capability the chosen provider lacks
|
||||
* is rejected with a typed error rather than accepted-then-ignored (the "fail loud, no silent
|
||||
* degradation" rule). These static flags cover features needed before a run exists; runtime
|
||||
* capabilities such as steering and resume are optional {@link SubagentRun} methods whose presence
|
||||
* is the capability.
|
||||
*/
|
||||
interface SubagentCapabilities {
|
||||
/** Honor {@link SubagentStartRequest.outputSchema} (structured final output). */
|
||||
readonly outputSchema: boolean
|
||||
/** Enforce {@link SubagentStartRequest.maxDepth} (recursion cap). */
|
||||
readonly depthLimit: boolean
|
||||
/** Enforce {@link SubagentStartRequest.toolFilter} (child tool scoping). */
|
||||
readonly toolFilter: boolean
|
||||
/** Honor {@link SubagentStartRequest.persona} (a per-child persona). */
|
||||
readonly persona: boolean
|
||||
}
|
||||
```
|
||||
@@ -24,14 +36,60 @@ interface SubagentCapabilities {
|
||||
The tool layer builds this request from the model input and its own config; the service validates it against the named provider before `start`. Required `parent` supplies the session cwd, lineage, and delegation depth. Optional output schema, depth, tool filter, and persona require matching capability flags. Unsupported schemas fail at start; in-process backends scope filters and personas to child creation and implement the supported object-rooted schema with a forced capture tool.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What a caller asks for when starting a subagent. The tool layer builds this
|
||||
* from the model's `{ description, prompt }` plus its own config; the service
|
||||
* validates {@link SubagentCapabilities} against the named provider, then
|
||||
* passes it to {@link SubagentProvider.start}.
|
||||
*/
|
||||
interface SubagentStartRequest {
|
||||
/** The task/prompt for the child agent (a user message in the child session). */
|
||||
readonly prompt: ContentBlock[]
|
||||
/**
|
||||
* The spawning ("parent") agent — the one whose tool call started this
|
||||
* subagent. REQUIRED: in-process backends read `parent.session.header` for
|
||||
* the working directory, the `parentSession` lineage to stamp on the child,
|
||||
* and the parent's delegation depth. Out-of-process backends (ACP) ignore it.
|
||||
*/
|
||||
readonly parent: Agent
|
||||
/**
|
||||
* Cancellation signal from the spawning context (the tool's `exec.signal`).
|
||||
* This is the canonical cancellation channel both before and after startup:
|
||||
* a provider rejects `start()` after cleaning partial resources when it
|
||||
* fires before publication, and cancels a published child when it fires
|
||||
* afterward.
|
||||
*/
|
||||
readonly signal: AbortSignal
|
||||
/** Per-child agent options (model and plugin-defined extension fields). */
|
||||
readonly agentOptions?: AgentOptions
|
||||
/**
|
||||
* Object-rooted JSON Schema within `assertSupportedOutputSchema`'s enforced subset. Start rejects
|
||||
* unsupported schemas or providers without the capability. Data must be plain host-realm JSON;
|
||||
* a successful child returns the matching value as {@link SubagentResult.structured}.
|
||||
*/
|
||||
readonly outputSchema?: StructuredOutputSchema
|
||||
/**
|
||||
* Optional absolute delegation-depth cap for the child being started: its
|
||||
* computed depth must be less than or equal to this non-negative safe
|
||||
* integer. Requires {@link SubagentCapabilities.depthLimit}; rejected at
|
||||
* start otherwise.
|
||||
*/
|
||||
readonly maxDepth?: number
|
||||
/**
|
||||
* Optional child tool scoping. Requires {@link SubagentCapabilities.toolFilter};
|
||||
* rejected at start otherwise. In-process backends apply it as a scoped
|
||||
* `tools.restrict()` in the child's creation window: the named tools vanish
|
||||
* from the child's prompt AND refuse to execute (one visibility), with loud
|
||||
* unknown-name validation.
|
||||
*/
|
||||
readonly toolFilter?: ToolRestriction
|
||||
/**
|
||||
* Optional per-child persona. Requires {@link SubagentCapabilities.persona};
|
||||
* rejected at start otherwise. In-process backends register it as a scoped
|
||||
* `deployment:persona` section on the child, SHADOWING the deployment's
|
||||
* persona for this child alone — same template semantics as the deployment
|
||||
* persona (strict `{{…}}` interpolation against the registered variables).
|
||||
*/
|
||||
readonly persona?: string
|
||||
}
|
||||
```
|
||||
@@ -43,9 +101,21 @@ interface SubagentStartRequest {
|
||||
The outcome of a run, resolved by `SubagentRun.result`. `structured` is present only after a requested `outputSchema` was successfully satisfied; requesting a schema does not guarantee it, and a provider may return `stopReason: 'error'` when the child fails or finishes without a valid capture. A non-`completed` `stopReason` means `output` may be partial — the consumer maps it to an `isError` tool result rather than reporting partial output as success.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The terminal outcome of a subagent run, resolved by {@link SubagentRun.result}.
|
||||
*/
|
||||
interface SubagentResult {
|
||||
/** The child's final assistant output (the last assistant message's content). */
|
||||
readonly output: ContentBlock[]
|
||||
/**
|
||||
* The structured result after a requested `outputSchema` was successfully
|
||||
* satisfied. Requesting a schema does not guarantee presence: a provider can
|
||||
* end with `stopReason: 'error'` when the child fails or finishes without a
|
||||
* valid capture. Shape is validated against the request schema by the
|
||||
* provider; `unknown` here because the seam is schema-agnostic.
|
||||
*/
|
||||
readonly structured?: unknown
|
||||
/** Why the run ended. A non-`completed` reason means `output` may be partial. */
|
||||
readonly stopReason: SubagentStopReason
|
||||
}
|
||||
```
|
||||
@@ -53,11 +123,22 @@ interface SubagentResult {
|
||||
`SubagentStopReason` is a [merge-extensible derived union](core.md#the-map--derived-union-pattern) — a backend may add variants, so consumers branch on the known cases and treat an unknown terminal reason as a failure:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Why a subagent run ended. Merge-extensible (a backend may add variants);
|
||||
* consumers branch on the known cases and fall through `default`. The known
|
||||
* cases mirror the harness turn-end vocabulary so the tool layer can map a
|
||||
* non-`completed` result to an `isError` tool result.
|
||||
*/
|
||||
interface SubagentStopReasonMap {
|
||||
/** The child finished its turn normally. */
|
||||
completed: 'completed'
|
||||
/** The run was cancelled by its request signal or by disposal. */
|
||||
aborted: 'aborted'
|
||||
/** The child failed (model error, transport error). */
|
||||
error: 'error'
|
||||
/** The child hit its token ceiling before finishing. */
|
||||
'max-tokens': 'max-tokens'
|
||||
/** The child declined the task. */
|
||||
refusal: 'refusal'
|
||||
}
|
||||
```
|
||||
@@ -67,12 +148,47 @@ interface SubagentStopReasonMap {
|
||||
`SubagentRun` is the consumer-owned handle for a ready child. Consumers await `result` and always dispose the run to reach quiescence. Child failures resolve with a non-completed stop reason; only unrepresentable infrastructure faults reject. Optional `sendMessage` and `resume` methods advertise their runtime capabilities by presence.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Child handle returned only after readiness. Consumers await {@link result} and must always
|
||||
* {@link dispose} to cancel remaining work and reach quiescence. Optional methods are runtime
|
||||
* capability discovery; narrow their presence before calling.
|
||||
*/
|
||||
interface SubagentRun {
|
||||
/**
|
||||
* Parent-scoped run id. For a local run, this MUST equal the published child
|
||||
* session id, whose `parentSession` records `request.parent.session.id`; a
|
||||
* remote provider mints an id unique in the parent namespace.
|
||||
*/
|
||||
readonly id: SessionId
|
||||
/**
|
||||
* The exact published in-process child, or `undefined` for a remote run.
|
||||
* When present, its id is {@link id}; the provider retains no ownership
|
||||
* implication beyond the run's ordinary {@link dispose} contract.
|
||||
*/
|
||||
readonly localAgent: Agent | undefined
|
||||
/**
|
||||
* Resolves with the child's terminal {@link SubagentResult} when the run
|
||||
* settles. Does NOT reject on a child-level failure — a model/transport
|
||||
* failure resolves with `stopReason: 'error'` so the consumer maps it to an
|
||||
* `isError` tool result. Rejects only on an infrastructure fault the seam
|
||||
* cannot represent as a stop reason.
|
||||
*/
|
||||
readonly result: Promise<SubagentResult>
|
||||
/**
|
||||
* Cancel remaining work, reach child quiescence, and release the run's
|
||||
* resources (in-process: dispose the owned agent and remove its session;
|
||||
* ACP: kill and reap the subprocess). Idempotent.
|
||||
*/
|
||||
dispose(): Promise<void>
|
||||
/**
|
||||
* OPTIONAL (steering capability): send additional content to the running
|
||||
* child between steps. Present only on providers that support live steering.
|
||||
*/
|
||||
sendMessage?(content: ContentBlock[]): void
|
||||
/**
|
||||
* OPTIONAL (resume capability): send a follow-up task to a settled child,
|
||||
* continuing its session, and return a fresh run for the continuation.
|
||||
*/
|
||||
resume?(content: ContentBlock[]): Promise<SubagentRun>
|
||||
}
|
||||
```
|
||||
@@ -84,10 +200,33 @@ A local run MUST publish an ordinary child agent/session before `start()` fulfil
|
||||
Each provider is a named child-agent transport, and multiple providers may coexist. The service validates requested start-time capabilities before `start()`. `inheritsParentContext` describes only conversation seeding (`fork`: true; `spawn` and `acp`: false), allowing consumers to generate accurate model-facing wording without implying inherited tools, services, or authority.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A subagent backend: one transport for running a child agent (in-process
|
||||
* spawn/fork, ACP to another process, …). Implementations register under a
|
||||
* unique name via {@link SubagentService.registerProvider}; multiple providers
|
||||
* coexist in one context (unlike the single-implementation bash seam). The
|
||||
* Providers are trusted same-process implementations; callers treat their
|
||||
* descriptors and returned values as borrowed immutable data.
|
||||
*/
|
||||
interface SubagentProvider {
|
||||
/** Unique registry name (e.g. `spawn`, `fork`, `acp`). */
|
||||
readonly name: string
|
||||
/** The start-time features this provider supports (see {@link SubagentCapabilities}). */
|
||||
readonly capabilities: SubagentCapabilities
|
||||
/**
|
||||
* Whether the child sees the parent's completed-turn prefix. This is descriptive, not a
|
||||
* service-validated start capability: the model-facing tool derives truthful wording from it.
|
||||
* It says nothing about tool registration, injected services, or authority inheritance.
|
||||
*/
|
||||
readonly inheritsParentContext: boolean
|
||||
/**
|
||||
* Establish a child and return its handle only after publication. The
|
||||
* service has already validated that every requested start-time capability
|
||||
* is supported, so an implementation may assume e.g. `request.maxDepth` is
|
||||
* honorable when present. If setup fails or `request.signal` aborts before
|
||||
* fulfillment, the provider owns and cleans all partial resources before this
|
||||
* promise rejects. Ownership transfers to the caller only on fulfillment.
|
||||
*/
|
||||
start(request: SubagentStartRequest): Promise<SubagentRun>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -9,7 +9,12 @@ Source: [`packages/core/system-prompt/src/index.ts`](../../packages/core/system-
|
||||
`AssembleContext` identifies the scope layer one assembly resolves. It is merge-extensible: `dsh-agent` adds the optional live `agent` field, and `assembleContextFor(agent)` sets that field and `scope` together.
|
||||
|
||||
```ts type-equiv
|
||||
/** Merge-extensible context for one prompt assembly. */
|
||||
interface AssembleContext {
|
||||
/**
|
||||
* Scope whose providers and waterfall listeners participate. When absent,
|
||||
* only global providers and subject-less listeners participate.
|
||||
*/
|
||||
scope?: ScopeKey
|
||||
}
|
||||
```
|
||||
@@ -19,8 +24,11 @@ interface AssembleContext {
|
||||
`ToolProviderResult.schemas` is the model-visible set for the current assembly. `knownNames` is the provider's pre-restriction name universe used to distinguish a configured-name typo from a known tool that is deliberately hidden in this scope.
|
||||
|
||||
```ts type-equiv
|
||||
/** Tool schemas visible in one assembly and their pre-restriction name set. */
|
||||
interface ToolProviderResult {
|
||||
/** The schemas this provider contributes to THIS assembly. */
|
||||
readonly schemas: readonly ToolSchema[]
|
||||
/** The pre-restriction name universe for config validation (defaults to `schemas`' names). */
|
||||
readonly knownNames?: readonly string[]
|
||||
}
|
||||
```
|
||||
@@ -30,9 +38,21 @@ interface ToolProviderResult {
|
||||
`PromptSection` is a readonly same-process registration contract. Its text may be static or resolved from the current assembly context.
|
||||
|
||||
```ts type-equiv
|
||||
/** One contributed section of the system prompt (registry input). */
|
||||
interface PromptSection {
|
||||
/** Unique name — a duplicate registration throws (see {@link SystemPrompt.section}). */
|
||||
readonly name: string
|
||||
/**
|
||||
* Sections are concatenated in ascending order. Convention: `-100` is the
|
||||
* harness identity, `0` the deployment persona, tool guidance uses 100–199;
|
||||
* other negative orders also render before the persona.
|
||||
*/
|
||||
readonly order: number
|
||||
/**
|
||||
* Static text or a provider evaluated at each assembly with that assembly's
|
||||
* {@link AssembleContext}. The text may reference `{{variable}}`s — they are
|
||||
* interpolated later, by {@link renderPrompt}.
|
||||
*/
|
||||
readonly text: string | ((context: AssembleContext) => string)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -7,6 +7,10 @@ Types shared by long-running producers, `ctx.tasks`, and task control surfaces.
|
||||
`TaskId` is a [branded id](core.md#branded-ids) generated as `<kind>-N`. Access control relies on owner authorization, not id secrecy. `TaskKind` derives from a merge-extensible map; the registry treats kinds as opaque id namespaces.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Producer-defined task kinds. Plugins extend this map by declaration merging;
|
||||
* the registry treats every value as an opaque id namespace.
|
||||
*/
|
||||
interface TaskKindMap {
|
||||
bash: 'bash'
|
||||
subagent: 'subagent'
|
||||
@@ -20,6 +24,11 @@ interface TaskKindMap {
|
||||
`TaskStart` declares identity and a starter. The runtime finishes preflight before calling `run()` and commits without a later failable step. Producers own execution resources; the runtime owns identity, access, and lifecycle state.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Producer declaration passed to {@link TaskService.start}. The runtime
|
||||
* preflights access and cleanup before invoking {@link run}; the producer owns
|
||||
* execution resources while the runtime owns identity and lifecycle state.
|
||||
*/
|
||||
interface TaskStart {
|
||||
/** Producer kind — also the id prefix (`bash`, `subagent`, …). */
|
||||
kind: TaskKind
|
||||
@@ -44,6 +53,7 @@ interface TaskStart {
|
||||
`TaskHooks.done` is the quiescence boundary. Optional `readOutput` distinguishes consuming stream tasks from final-output-only tasks.
|
||||
|
||||
```ts type-equiv
|
||||
/** Hooks through which the runtime controls and observes producer work. */
|
||||
interface TaskHooks {
|
||||
/**
|
||||
* Request termination. Must be synchronous, idempotent, and eventually settle
|
||||
@@ -67,6 +77,7 @@ interface TaskHooks {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Terminal result supplied by a producer through {@link TaskHooks.done}. */
|
||||
interface TaskOutcome {
|
||||
/** How the task ended: finished (`completed`), cancelled (`killed`), or broke (`failed`). */
|
||||
status: 'completed' | 'killed' | 'failed'
|
||||
@@ -82,6 +93,10 @@ interface TaskOutcome {
|
||||
Snapshots are fresh read-only projections. `ownerSession` carries the shared `SessionId` used for authorization; completion listeners separately receive the exact owner object used for lifecycle cleanup. `reported` suppresses a completion notice after another surface has delivered or committed to deliver the terminal state.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A read-only projection of one task, safe to hand to listeners and tools —
|
||||
* a fresh object per call, never live registry state.
|
||||
*/
|
||||
interface TaskSnapshot {
|
||||
/** The registry-issued id (`<kind>-N`). */
|
||||
id: TaskId
|
||||
@@ -112,6 +127,7 @@ interface TaskSnapshot {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Output and post-read state returned by {@link TaskService.read}. */
|
||||
interface TaskRead {
|
||||
/**
|
||||
* Stream kinds: the consuming delta since the previous read. Final-output
|
||||
|
||||
@@ -7,6 +7,7 @@ Source: [`packages/llm/token-meter/src/types.ts`](../../packages/llm/token-meter
|
||||
## `TokenMeasurement`
|
||||
|
||||
```ts type-equiv
|
||||
/** Detached immutable request-pressure and surface snapshot at one consumed log revision. */
|
||||
interface TokenMeasurement {
|
||||
/** Number of durable events consumed; equal to the next unread event seq. */
|
||||
readonly logRevision: number
|
||||
@@ -28,6 +29,7 @@ interface TokenMeasurement {
|
||||
## `TokenSurfaceNode`
|
||||
|
||||
```ts type-equiv
|
||||
/** One token-priced node in the current ordered session surface. */
|
||||
interface TokenSurfaceNode {
|
||||
/** Durable sequence number of the surface event. */
|
||||
readonly seq: number
|
||||
|
||||
@@ -9,6 +9,7 @@ Source: [`packages/core/tools/src/index.ts`](../../packages/core/tools/src/index
|
||||
A `ToolSchema` (the model-facing fields) plus the `execute` function, host-only scheduler metadata, and optional UI presenters. The registry holds these; the loop dispatches calls through them. The registry's `schemas()` builds the model-facing `ToolSchema[]` by an explicit allowlist — `execute`/`timeoutMs`/`isConcurrencySafe`/`presentCall`/`presentResult` must never leak into a model request.
|
||||
|
||||
```ts type-equiv
|
||||
/** A registered tool: its schema plus the execution function. */
|
||||
interface ToolDefinition extends ToolSchema {
|
||||
execute(args: unknown, exec: ToolRunContext): Promise<ToolExecuteReturn>
|
||||
/**
|
||||
@@ -26,7 +27,9 @@ interface ToolDefinition extends ToolSchema {
|
||||
*
|
||||
* Opted-in executions must not mutate parent-owned state. Shared state must
|
||||
* tolerate concurrent dispatch; recorder races are permitted only when they
|
||||
* commute or fail closed. See the parallel-tool-call RFC for the full contract.
|
||||
* commute or fail closed. See the
|
||||
* [parallel-tool-call RFC](../../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md)
|
||||
* for the full contract.
|
||||
* @param args - parsed arguments; `defineTool` validates before calling.
|
||||
* @returns Whether this call may join a parallel group.
|
||||
*/
|
||||
@@ -61,6 +64,7 @@ Plugin authors write per-property specs with a boolean `required: true`, and a t
|
||||
Source: [`packages/core/tools/src/schema.ts`](../../packages/core/tools/src/schema.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** One schema-spec property entry. */
|
||||
interface SchemaProp {
|
||||
type: SchemaType
|
||||
/** Per-property required flag (NOT the JSON Schema top-level required array). */
|
||||
@@ -69,7 +73,10 @@ interface SchemaProp {
|
||||
description?: string
|
||||
/** Enum of allowed values (strings only). */
|
||||
enum?: string[]
|
||||
/** Default value. */
|
||||
/**
|
||||
* Model-visible JSON Schema default annotation. Validation does not apply it;
|
||||
* dynamic tool mounts may supply it even though first-party definitions do not.
|
||||
*/
|
||||
default?: unknown
|
||||
/** Nested properties for type: 'object'. */
|
||||
properties?: SchemaSpec
|
||||
@@ -79,12 +86,29 @@ interface SchemaProp {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The author-facing parameter schema: a shallow map of property name to
|
||||
* {@link SchemaProp}. Required-ness is a per-property boolean (`required:
|
||||
* true`), not a separate array.
|
||||
*/
|
||||
type SchemaSpec = Record<string, SchemaProp>
|
||||
```
|
||||
|
||||
`SchemaType` is the primitive union `'string' | 'number' | 'boolean' | 'object' | 'array'`. `InferArgs<S>` maps a `SchemaSpec` to the TS argument type — `required: true` props become required keys, everything else genuinely optional:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Infer the TS argument type for a complete {@link SchemaSpec}.
|
||||
*
|
||||
* Properties marked `required: true` are required keys; all others are
|
||||
* genuinely optional keys (`?`), so callers may omit them entirely.
|
||||
*
|
||||
* Example:
|
||||
* ```ts
|
||||
* type Args = InferArgs<{ path: { type: 'string'; required: true }; limit: { type: 'number' } }>
|
||||
* // → { path: string; limit?: number }
|
||||
* ```
|
||||
*/
|
||||
type InferArgs<S extends SchemaSpec> = Simplify<
|
||||
& { [K in RequiredKeys<S>]: InferPropValue<S[K]> }
|
||||
& { [K in Exclude<keyof S, RequiredKeys<S>>]?: InferPropValue<S[K]> }
|
||||
@@ -100,8 +124,14 @@ Registration is a trusted same-process contract. The registry borrows the typed
|
||||
`ToolRestriction` applies only to the live deployment-global tool layer. The registry compiles readonly names into private sets, intersects multiple restrictions, then overlays scope-local tools. A deny-only filter admits later unlisted globals, while an allow-list excludes them.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Per-scope filter over global tools. Restrictions intersect and do not affect
|
||||
* scoped registrations or the reserved Code Mode transport.
|
||||
*/
|
||||
interface ToolRestriction {
|
||||
/** Global tool names that stay visible; everything else is removed. */
|
||||
readonly allow?: readonly string[]
|
||||
/** Global tool names removed from visibility. */
|
||||
readonly deny?: readonly string[]
|
||||
}
|
||||
```
|
||||
@@ -111,14 +141,20 @@ interface ToolRestriction {
|
||||
`ctx.tools.execute()` accepts a caller-owned `ToolExecutionInput`, materializes its parsed JSON arguments once into a pipeline-owned `ToolExecution`, and runs that call through `tools/pre-execute` (the reorderable allow/deny/ask waterfall) → registered monotonic guards → `tools/execute` (around-dispatch wrappers) → `tools/post-execute` (inspect/replace the result) → `tools/result` (the immutable authoritative outcome). The outcome is a `ToolExecutionResult`.
|
||||
|
||||
```ts type-equiv
|
||||
/** Opaque call identity that permits correlation without exposing mutable execution state. */
|
||||
type ToolExecutionToken = symbol & { readonly [toolExecutionTokenBrand]: true }
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Caller-supplied description of one tool call. {@link ToolRegistry.execute}
|
||||
* adds the registry-owned token to form a pipeline {@link ToolExecution};
|
||||
* callers do not choose that token.
|
||||
*/
|
||||
interface ToolExecutionInput {
|
||||
readonly callId: CallId
|
||||
readonly name: string
|
||||
/** Parsed JSON arguments (unknown — tools validate their own input). */
|
||||
/** Losslessly JSON-serializable parsed arguments (tools validate their own schema). */
|
||||
readonly arguments: unknown
|
||||
/** The agent on whose behalf the call runs (set by the agent loop). */
|
||||
readonly agent?: Agent
|
||||
@@ -135,6 +171,12 @@ interface ToolExecutionInput {
|
||||
A tool body receives the runtime extension. `deferContext()` is the composite-tool channel: it records nested-dispatch context without injecting inside the still-open outer call.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Runtime context handed to a tool implementation after the registry has
|
||||
* accepted a {@link ToolExecution}. A composite tool uses
|
||||
* {@link deferContext} to ferry context produced by nested dispatches back to
|
||||
* the outer result; the loop appends it only after the outer `tool/result`.
|
||||
*/
|
||||
interface ToolRunContext extends ToolExecution {
|
||||
/**
|
||||
* Defer one nested-dispatch context until this tool's final result reaches
|
||||
@@ -148,12 +190,23 @@ interface ToolRunContext extends ToolExecution {
|
||||
The agent loop asks the registry for each pending call's execution mode and uses it to form exclusive barriers and rolling-pool parallel runs:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Scheduling mode for one pending call. `parallel` may overlap with siblings;
|
||||
* `exclusive` runs alone and forms an ordering barrier.
|
||||
*/
|
||||
type ToolExecutionMode =
|
||||
| { kind: 'parallel' }
|
||||
| { kind: 'exclusive' }
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One pending tool call inside the registry pipeline. Parsed arguments cross
|
||||
* one lossless-JSON materialization boundary before policy and are deep-frozen;
|
||||
* call identity and the registry-assigned {@link token} are readonly. An
|
||||
* around-dispatch wrapper may set, replace, or remove `signal`. The registry
|
||||
* freezes the complete object before `tools/result` observers run.
|
||||
*/
|
||||
interface ToolExecution extends ToolExecutionInput {
|
||||
/** Registry-assigned identity shared with nested calls only as their opaque `parent` token. */
|
||||
readonly token: ToolExecutionToken
|
||||
@@ -165,10 +218,19 @@ interface ToolExecution extends ToolExecutionInput {
|
||||
A `ToolGuard` is scope-aware final pre-dispatch policy. Its shape deliberately has no allow result: `undefined` preserves the waterfall decision, while a returned reason can only reduce permission, so a later listener cannot undo it.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* A monotonic execution guard evaluated after every `tools/pre-execute`
|
||||
* listener and before the tool body. Returning a reason denies the call;
|
||||
* returning `undefined` leaves it unchanged. Because guards have no allow
|
||||
* result, listener ordering cannot turn a denial back into permission.
|
||||
* @param execution - the identity-protected call after extensible pre-execute policy completed.
|
||||
* @returns a final denial reason, or `undefined` to leave the call allowed.
|
||||
*/
|
||||
type ToolGuard = (execution: Readonly<ToolExecution>) => string | undefined
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The outcome of one tool call. */
|
||||
interface ToolExecutionResult {
|
||||
content: ContentBlock[]
|
||||
isError: boolean
|
||||
@@ -179,14 +241,8 @@ interface ToolExecutionResult {
|
||||
*/
|
||||
error?: ToolErrorInfo
|
||||
/**
|
||||
* Extra model-facing contexts deferred by a composite tool or attached by
|
||||
* `tools/post-execute` listeners for the NEXT request. They are not part of
|
||||
* this call's `content`: the loop accepts them into the active-batch FIFO and
|
||||
* appends them after every recorded `tool/result` when the batch settles, even
|
||||
* when execution is interrupted. The array preserves each context's source,
|
||||
* envelope, metadata, and production order. An accepted outer call keeps
|
||||
* deferred contexts before decision contexts; a block retains only contexts
|
||||
* supplied by the blocking decision.
|
||||
* Model-facing context for the next request, separate from this tool result. The loop
|
||||
* accepts it into the active-batch FIFO, then appends after recorded results even if interrupted.
|
||||
*/
|
||||
additionalContexts?: HookContext[]
|
||||
/**
|
||||
@@ -206,6 +262,12 @@ The registry materializes and freezes the final accepted result immediately befo
|
||||
Each interception waterfall returns a typed **Decision** (the idiom shared with the `agent/*` seams). `tools/pre-execute` listeners receive `(exec, next)` and return a `PreToolDecision`; `tools/execute` wrappers return a `ToolExecutionResult`; `tools/post-execute` listeners receive `(exec, result, next)` and return a `PostToolDecision`:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Pre-dispatch decision. `allow` runs the call; `deny` materializes an error;
|
||||
* `ask` runs only after an approval service returns `allowed-once` and otherwise
|
||||
* denies. Input rewriting is excluded because arguments are already logged and
|
||||
* presented.
|
||||
*/
|
||||
type PreToolDecision =
|
||||
| { kind: 'allow' }
|
||||
| { kind: 'deny'; reason: string }
|
||||
@@ -213,6 +275,10 @@ type PreToolDecision =
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Post-dispatch decision: accept or replace content, attach context for the next
|
||||
* request, or block by turning corrective feedback into an error result.
|
||||
*/
|
||||
type PostToolDecision =
|
||||
| { kind: 'accept'; content?: ContentBlock[]; additionalContexts?: HookContext[] }
|
||||
| { kind: 'block'; feedback: ContentBlock[]; additionalContexts?: HookContext[] }
|
||||
@@ -227,25 +293,41 @@ Post-policy may replace content; a block becomes an `isError` result containing
|
||||
The vocabulary a caller uses to demand a machine-readable result from a subagent (`SubagentStartRequest.outputSchema`, [subagent.md](subagent.md#the-start-request)) or a workflow `agent()` call. It is deliberately NOT full JSON Schema: the schema travels verbatim to the model as a forced tool's `parameters`, and the produced value is validated client-side by `validateStructuredValue` — so every accepted keyword must be one the validator actually enforces, and `assertSupportedOutputSchema` rejects anything else loud (`OutputSchemaError`, listing every violation). Both walkers reason over own enumerable properties only (JSON carries nothing else) and reject non-plain objects (`Date`, `Map`) that would serialize lossily.
|
||||
|
||||
```ts type-equiv
|
||||
/** The scalar values `enum`/`const` may carry (finite numbers only). */
|
||||
type StructuredScalar = string | number | boolean | null
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The `type` keywords the subset accepts. */
|
||||
type StructuredSchemaType = 'object' | 'array' | 'string' | 'number' | 'integer' | 'boolean' | 'null'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One node of the structured-output schema subset. Recursive via `properties`
|
||||
* and `items`; see the module doc for the exact keyword semantics.
|
||||
*/
|
||||
interface StructuredSchemaNode {
|
||||
type: StructuredSchemaType
|
||||
/** Nested property schemas (`type: 'object'` only). */
|
||||
properties?: Record<string, StructuredSchemaNode>
|
||||
/** Required property names; each must appear in `properties`. */
|
||||
required?: string[]
|
||||
/** `false` rejects undeclared keys; absent/`true` allows them (JSON Schema default). */
|
||||
additionalProperties?: boolean
|
||||
/** Item schema (`type: 'array'` only); absent ⇒ any JSON items. */
|
||||
items?: StructuredSchemaNode
|
||||
/** Allowed values (scalar types only). */
|
||||
enum?: StructuredScalar[]
|
||||
/** The single allowed value (scalar types only). */
|
||||
const?: StructuredScalar
|
||||
/** Annotation, ignored for validation. */
|
||||
description?: string
|
||||
/** Annotation, ignored for validation. */
|
||||
title?: string
|
||||
/** Annotation, ignored for validation (must still be JSON data). */
|
||||
default?: unknown
|
||||
/** Annotation, ignored for validation (must still be JSON data). */
|
||||
examples?: unknown
|
||||
}
|
||||
```
|
||||
@@ -253,6 +335,7 @@ interface StructuredSchemaNode {
|
||||
A schema is an object-rooted node (`enum`/`const` are scalar-only; `description`/`title`/`default`/`examples` are annotations, allowed and ignored but still required to be JSON data — they ride the wire):
|
||||
|
||||
```ts type-equiv
|
||||
/** A structured-output schema: an OBJECT-rooted {@link StructuredSchemaNode}. */
|
||||
type StructuredOutputSchema = StructuredSchemaNode & { type: 'object' }
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# User Interaction
|
||||
|
||||
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`: `dsh-stdio-demo` renders questions in readline, and `dsh-acp` maps them to ACP form elicitations.
|
||||
The user-interaction seam of [dsh-user-interaction](../../packages/ui/user-interaction). It is the provider-neutral vocabulary a tool or permission plugin uses when it needs the human to answer before the agent can continue. UI surfaces provide the active `UserInteractionProvider`: `dsh-stdio-demo` selects keyboard-driven `dsh-tui` overlays or `dsh-stdio` readline prompts, and `dsh-acp` maps questions to ACP form elicitations.
|
||||
|
||||
Source: [`packages/ui/user-interaction/src/index.ts`](../../packages/ui/user-interaction/src/index.ts)
|
||||
|
||||
@@ -9,6 +9,7 @@ Source: [`packages/ui/user-interaction/src/index.ts`](../../packages/ui/user-int
|
||||
`AskUserQuestionOption` is the selectable-choice shape. `label` is the user-facing option text and also the model-facing selected value; `description` is optional UI help text.
|
||||
|
||||
```ts type-equiv
|
||||
/** One selectable answer offered to the user. */
|
||||
interface AskUserQuestionOption {
|
||||
/** User-facing label. */
|
||||
label: string
|
||||
@@ -22,6 +23,7 @@ interface AskUserQuestionOption {
|
||||
`AskUserQuestionItem` is one question in a request. The model supplies a stable `id`, which is echoed back with the answer so batched questions remain routable.
|
||||
|
||||
```ts type-equiv
|
||||
/** One question in an ask_user_question request. */
|
||||
interface AskUserQuestionItem {
|
||||
/** Stable model-provided question id, echoed in the answer. */
|
||||
id: string
|
||||
@@ -41,6 +43,7 @@ interface AskUserQuestionItem {
|
||||
`AskUserQuestionRequest` is the cross-package request. `questions` is an array so a UI can present related prompts in one flow while preserving a stable id per answer.
|
||||
|
||||
```ts type-equiv
|
||||
/** Request for a human answer. */
|
||||
interface AskUserQuestionRequest {
|
||||
/** Questions to display. */
|
||||
questions: AskUserQuestionItem[]
|
||||
@@ -56,6 +59,7 @@ interface AskUserQuestionRequest {
|
||||
Providers return one answer per answered question id. `selected` contains selected option labels, and `custom` carries a free-form "Other" answer when the user typed one. When `custom` is present, `selected` is empty; custom text is an answer override, not a supplement to selected choices.
|
||||
|
||||
```ts type-equiv
|
||||
/** Answer to one question. */
|
||||
interface AskUserQuestionAnswerItem {
|
||||
/** The answered question id. */
|
||||
id: string
|
||||
@@ -67,6 +71,7 @@ interface AskUserQuestionAnswerItem {
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The human's answer. */
|
||||
interface AskUserQuestionAnswer {
|
||||
/** Structured answers keyed by question id. */
|
||||
answers: AskUserQuestionAnswerItem[]
|
||||
@@ -78,6 +83,7 @@ interface AskUserQuestionAnswer {
|
||||
Only one provider may be active in a context. Provider registration is effect-bound so HMR/disposal removes the active UI.
|
||||
|
||||
```ts type-equiv
|
||||
/** UI-side provider for user questions. */
|
||||
interface UserInteractionProvider {
|
||||
ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>
|
||||
}
|
||||
@@ -88,6 +94,7 @@ interface UserInteractionProvider {
|
||||
`UserInteractionError` extends `HarnessError`, so `ctx.tools.execute()` preserves `{ name, code }` for model-facing tool failures such as `EMPTY_QUESTIONS`, `NO_PROVIDER`, `ASK_ABORTED`, or ACP-side cancellation.
|
||||
|
||||
```ts type-equiv
|
||||
/** Stable error taxonomy for user-interaction failures. */
|
||||
class UserInteractionError extends HarnessError {
|
||||
constructor(message: string, code: string, options?: ErrorOptions) {
|
||||
super(message, code, options)
|
||||
|
||||
@@ -13,20 +13,37 @@ Search and fetch share no request schema and no business logic, but they are del
|
||||
The model-facing tool argument is just a `query`; `maxResults` is a consumer-owned bound (`dsh-tool-web`'s `searchMaxResults` config, default `8`) passed through the seam and enforced on the way back — if a provider over-returns, the seam truncates `sources[]` and sets `truncated`.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What one search-capable backend can return. The model-facing argument is just
|
||||
* a query; `maxResults` is a `dsh-tool-web`-layer bound passed through unchanged
|
||||
* and enforced on the way back by the seam (see {@link WebSearchResult}).
|
||||
*/
|
||||
interface WebSearchRequest {
|
||||
readonly query: string
|
||||
/**
|
||||
* Upper bound on returned sources; the seam truncates to it. Omitted = no
|
||||
* bound. `dsh-tool-web` always sets it.
|
||||
* bound. `dsh-tool-web` always sets it. A provider whose API supports a
|
||||
* result-count control (Exa's `numResults`) should apply it at the request
|
||||
* layer as a cost/latency optimization; the seam enforces the bound
|
||||
* regardless.
|
||||
*/
|
||||
readonly maxResults?: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Normalized search outcome. `content` is optional provider-generated answer
|
||||
* text or summary (Exa returns none; Perplexity returns a generated answer).
|
||||
* `sources[]` is the portable citation surface. `truncated` is set by the seam
|
||||
* when it cut `sources[]` down to `maxResults`.
|
||||
*/
|
||||
interface WebSearchResult {
|
||||
/** Optional provider-generated answer text, search context, or summary. */
|
||||
readonly content?: string
|
||||
/** Citeable sources, already truncated to the request's `maxResults`. */
|
||||
readonly sources: readonly WebSearchSource[]
|
||||
/** True when the seam dropped sources to honor `maxResults`. */
|
||||
readonly truncated: boolean
|
||||
}
|
||||
```
|
||||
@@ -34,10 +51,17 @@ interface WebSearchResult {
|
||||
`content` is optional provider-generated answer text (Exa and DeepSeek return none; Perplexity returns a generated answer). `sources[]` is the portable citation surface. A source always has a `url`; `title`/`snippet`/`publishedAt` are optional because not every provider returns them — Perplexity citations may be URL-only, and forcing adapters to invent the rest would make the seam lie. `dsh-tool-web` renders `title ?? hostname(url)`.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* One citeable source. A source always has a URL; `title`, `snippet`, and
|
||||
* `publishedAt` are optional because not every provider returns them — forcing
|
||||
* adapters to invent them would make the seam lie (Perplexity citations may be
|
||||
* URL-only). `dsh-tool-web` renders `title ?? hostname(url)` for display.
|
||||
*/
|
||||
interface WebSearchSource {
|
||||
readonly url: string
|
||||
readonly title?: string
|
||||
readonly snippet?: string
|
||||
/** Publication/crawl timestamp as a provider-supplied ISO-8601 string. */
|
||||
readonly publishedAt?: string
|
||||
}
|
||||
```
|
||||
@@ -45,6 +69,12 @@ interface WebSearchSource {
|
||||
## Fetch request and result
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What one fetch-capable backend is asked to retrieve. The request deliberately
|
||||
* omits timeout, format, prompt, and extraction controls: cancellation is a
|
||||
* direct execution argument, while presentation and higher-level LLM concerns
|
||||
* belong outside safe retrieval.
|
||||
*/
|
||||
interface WebFetchRequest {
|
||||
readonly url: string
|
||||
}
|
||||
@@ -53,10 +83,20 @@ interface WebFetchRequest {
|
||||
HTTP status is part of the fetched resource state, not automatically a failure: a successful network fetch of a `404`/`500` returns a `WebFetchResult` with the status code and a bounded decoded body. `url` is the final URL after allowed redirects. `WebError` is reserved for failures to safely retrieve or represent the resource.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Normalized fetch outcome. A successful network fetch of a non-2xx response is
|
||||
* a result, not an error: the status code is part of the fetched resource
|
||||
* state. {@link WebError} is reserved for failures to safely retrieve or
|
||||
* represent the resource.
|
||||
*/
|
||||
interface WebFetchResult {
|
||||
/** The final URL after allowed redirects (the request URL is in the request). */
|
||||
readonly url: string
|
||||
/** HTTP status code of the fetched response. */
|
||||
readonly statusCode: number
|
||||
/** Decoded body, classified by content kind. */
|
||||
readonly body: WebFetchBody
|
||||
/** True when the provider capped the decoded body. */
|
||||
readonly truncated: boolean
|
||||
}
|
||||
```
|
||||
@@ -64,6 +104,15 @@ interface WebFetchResult {
|
||||
`WebFetchBody` is a **closed** discriminated union owned by `dsh-web` (not a merge-extensible map): the provider decodes the kind and `dsh-tool-web` renders it, so a new kind is a coordinated change across known packages, not a plugin extension. Consumers `switch` on `kind` ending in `default: assertNever(...)`, so adding a kind breaks compilation at every consumer until handled. Each arm stays its own object literal even where fields coincide today, leaving room for arm-specific fields later (a future `pdf` body's `pageCount`).
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The decoded body of a fetched resource. A CLOSED discriminated union owned by
|
||||
* `dsh-web`: the provider decodes the kind and `dsh-tool-web` renders it, so a
|
||||
* new kind is a coordinated change across known packages, not a plugin
|
||||
* extension. Consumers `switch` on `kind` ending in `default: assertNever(...)`
|
||||
* so adding a kind breaks compilation at every consumer until handled. Each arm
|
||||
* stays its own object literal even where fields coincide today, leaving room
|
||||
* for arm-specific fields later (a `pdf` body's `pageCount`).
|
||||
*/
|
||||
type WebFetchBody =
|
||||
| { readonly kind: 'html'; readonly content: string }
|
||||
| { readonly kind: 'text'; readonly content: string }
|
||||
|
||||
@@ -11,11 +11,24 @@ Source: [`packages/workflow/workflow/src/types.ts`](../../packages/workflow/work
|
||||
What a caller asks for when starting a run. The tool layer builds this from the model's `{ script, meta, args }` call plus the calling agent; `meta` and `args` are plain JSON DATA (the engine shape-validates `meta` and rejects loud BEFORE anything runs — no script text is ever evaluated to obtain it). `parent` is REQUIRED — every child the script spawns is attributed to it (cwd, lineage, and depth flow through the [subagent seam](subagent.md)).
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* What a caller asks for when starting a workflow run. `meta` and `args` are
|
||||
* plain JSON DATA by the seam contract (the tool builds both from the model's
|
||||
* schema-validated call; the engine validates `meta`'s shape and rejects loud
|
||||
* before anything runs) — an engine never evaluates script text to obtain
|
||||
* them. `parent` is REQUIRED — every `agent()` the script spawns is
|
||||
* attributed to it (cwd, lineage, depth flow through the subagent seam).
|
||||
*/
|
||||
interface WorkflowStartRequest {
|
||||
/** The plain-JS script body (top-level await allowed; ends with `return <json-value>`). */
|
||||
script: string
|
||||
/** The workflow's identity block, as plain JSON data (shape-validated by the engine). */
|
||||
meta: WorkflowMeta
|
||||
/** Optional input exposed verbatim to the script as the `args` global. */
|
||||
args?: unknown
|
||||
/** The agent on whose behalf the run executes (parent of every child). */
|
||||
parent: Agent
|
||||
/** Cancels the run when aborted (the tool's `exec.signal`). */
|
||||
signal?: AbortSignal
|
||||
}
|
||||
```
|
||||
@@ -25,10 +38,21 @@ interface WorkflowStartRequest {
|
||||
The identity block carried as data on the start request (the tool's `meta` parameter; the field vocabulary matches the Claude Code dynamic-workflows meta block). `phases` is progress vocabulary only: `phase()` calls match titles for observers; no execution structure is implied.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The script's identity block, provided as plain JSON data alongside the
|
||||
* script body (the model-facing tool carries it as its `meta` parameter) and
|
||||
* validated by the engine before the body runs. `name`/`description` are
|
||||
* required; the rest is optional annotation. The field vocabulary matches the
|
||||
* Claude Code dynamic-workflows meta block.
|
||||
*/
|
||||
interface WorkflowMeta {
|
||||
/** Short kebab-case workflow name (display + persistence key). */
|
||||
name: string
|
||||
/** One-line description of what the workflow does. */
|
||||
description: string
|
||||
/** Optional guidance on when this workflow applies (shown in listings). */
|
||||
whenToUse?: string
|
||||
/** Optional phase declarations matched by `phase()` calls. */
|
||||
phases?: WorkflowPhase[]
|
||||
}
|
||||
```
|
||||
@@ -38,10 +62,27 @@ interface WorkflowMeta {
|
||||
The outcome of one run, resolved by `WorkflowRun.result`. `value` is the script's materialized return value — plain host-realm JSON data (`null` when the script returned nothing) — meaningful only for `completed`. `stopReason` is a CLOSED union (engine-owned; consumers may exhaust it): `completed` | `cancelled` | `error`. A non-`completed` reason carries the failure in `error`, and the consumer maps it to an `isError` tool result rather than reporting partial output as success.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* The outcome of one run, resolved by {@link WorkflowRun.result}. `value` is
|
||||
* the script's materialized return value (plain host-realm JSON data; `null`
|
||||
* when the script returned `undefined`) — meaningful only for `completed`.
|
||||
* A non-`completed` reason carries the failure in `error`; the consumer maps
|
||||
* it to an `isError` tool result rather than reporting partial output.
|
||||
*/
|
||||
interface WorkflowResult {
|
||||
/** The script's return value (host JSON data; `null` for no return). */
|
||||
value: unknown
|
||||
/** Why the run settled. */
|
||||
stopReason: WorkflowStopReason
|
||||
/** The failure message (present iff `stopReason` is not `completed`). */
|
||||
error?: string
|
||||
/**
|
||||
* How many `agent()` calls the run accepted over its whole lifetime. On a
|
||||
* graceful settlement this is the script-side count (calls still queued for
|
||||
* a concurrency slot included); on a termination path (grace force-settle,
|
||||
* worker death) it degrades to the host-observed count — calls queued
|
||||
* inside a terminated script are unknowable then.
|
||||
*/
|
||||
agentsStarted: number
|
||||
}
|
||||
```
|
||||
@@ -51,11 +92,20 @@ interface WorkflowResult {
|
||||
The handle the consumer holds while a script executes. The consumer awaits `result`, may `cancel` mid-flight, and MUST `dispose` on every path. `result` does NOT reject — a script failure resolves with `stopReason: 'error'` — and once the run is cancelled it SETTLES within the engine's bounded grace even if the script itself never settles (the engine force-settles `cancelled`; the worker-thread engine then terminates the script's worker), so a consumer awaiting `result` is never wedged past a cancellation. `dispose()` = cancel + that bounded settle + child quiescence; it never hangs on a stuck script.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Holder-owned live workflow. `result` never rejects and settles within the
|
||||
* engine's cancellation grace; failures resolve through `stopReason`. Consumers
|
||||
* may cancel and must call idempotent `dispose()` on every path to await bounded
|
||||
* script settlement and child quiescence.
|
||||
*/
|
||||
interface WorkflowRun {
|
||||
readonly id: WorkflowRunId
|
||||
/** The validated meta block (available before the body runs). */
|
||||
readonly meta: WorkflowMeta
|
||||
readonly result: Promise<WorkflowResult>
|
||||
/** Cancel the run: children abort, pending hooks reject, the script dies at its next await (or is force-settled at the grace). */
|
||||
cancel(reason?: string): void
|
||||
/** Cancel + bounded-grace settle; safe to call on every path (idempotent). */
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
development.md: b3c338f03f548b4de4b676850732731323d611f1
|
||||
development.zh.md: 20f5c585dd378a7b0a3bd6b0af8ebf7dc5e0fd3c
|
||||
development.md: 37811f7215001fc371ac4943fe109dd5512ea8b0
|
||||
development.zh.md: 5036e3e75516fcaf063675fc9ab4e63c1fca851a
|
||||
|
||||
+15
-3
@@ -109,12 +109,24 @@ The echo demo does not need API credentials:
|
||||
pnpm run demo:echo
|
||||
```
|
||||
|
||||
The REPL agent demo uses the real DeepSeek adapter and needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
|
||||
The coding-agent REPL uses the line-oriented readline front door and needs `DEEPSEEK_API_KEY` in the environment or repo-root `.env`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
The full-screen TUI reuses the coding-agent composition through the pi-tui front door and needs the same credentials:
|
||||
|
||||
```sh
|
||||
pnpm run demo:tui
|
||||
```
|
||||
|
||||
The self-referential cordis-agent demo can inspect and modify its live plugin runtime and needs the same credentials:
|
||||
|
||||
```sh
|
||||
pnpm run demo:cordis
|
||||
```
|
||||
|
||||
The ACP server agent demo exposes the agent over JSON-RPC stdio and also needs `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
@@ -133,13 +145,13 @@ Pick the tag that matches the urgency so anyone scanning the code can tell a rel
|
||||
|
||||
## Documenting types verbatim (`ts type-equiv`)
|
||||
|
||||
The [core data structures](core-data-structures/core.md) docs paste real type definitions so a reader sees the exact shape. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors:
|
||||
The [core data structures](core-data-structures/core.md) docs paste real type declarations together with their original JSDoc so a reader sees the exact shape and source contract. To keep a paste from drifting when source changes, fence it as ` ```ts type-equiv ` (instead of ` ```ts `) and register it in `scripts/type-equiv.manifest.json` with the source file and symbol it mirrors:
|
||||
|
||||
```json
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" }
|
||||
```
|
||||
|
||||
`pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration from source via the TypeScript parser and asserts the block matches it (whitespace- and comment-insensitive, so a doc block may show a clean definition and the prose can carry the semantics). It also enforces a 1:1 correspondence: every `ts type-equiv` block has exactly one manifest entry and vice-versa, so a block can't go silently unchecked and a stale entry can't linger. `doc-typecheck` skips `ts type-equiv` blocks (they aren't standalone-compilable) and excludes them from its opt-out ratio. When you change a documented type, the gate fails until you update the paste; when you add or remove a block, update the manifest in the same change.
|
||||
`pnpm run verify-type-equiv` (part of `doc-sync`) then extracts that symbol's declaration and attached JSDoc from source via the TypeScript parser and asserts the block matches both. Comparison ignores whitespace and non-JSDoc comments but requires every original JSDoc comment, including member documentation, so readers see the source contract beside the exact shape. The gate also enforces a 1:1 correspondence: every `ts type-equiv` block has exactly one manifest entry and vice-versa, so a block can't go silently unchecked and a stale entry can't linger. `doc-typecheck` skips `ts type-equiv` blocks (they aren't standalone-compilable) and excludes them from its opt-out ratio. When you change a documented declaration or its JSDoc, the gate fails until you update the paste; when you add or remove a block, update the manifest in the same change.
|
||||
|
||||
## Architecture context
|
||||
|
||||
|
||||
+15
-3
@@ -109,12 +109,24 @@ echo 演示不需要 API 凭证:
|
||||
pnpm run demo:echo
|
||||
```
|
||||
|
||||
REPL agent 演示使用真实的 DeepSeek 适配器,需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
coding-agent REPL 使用面向行的 readline 前端,并需要环境变量或仓库根目录 `.env` 中的 `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
全屏 TUI 通过 pi-tui 前端复用 coding-agent 组装,并需要相同的凭证:
|
||||
|
||||
```sh
|
||||
pnpm run demo:tui
|
||||
```
|
||||
|
||||
自指的 cordis-agent 演示可以检查并修改其实时插件运行时,并需要相同的凭证:
|
||||
|
||||
```sh
|
||||
pnpm run demo:cordis
|
||||
```
|
||||
|
||||
ACP 服务器 agent 演示通过 JSON-RPC stdio 暴露 agent,同样需要 `DEEPSEEK_API_KEY`:
|
||||
|
||||
```sh
|
||||
@@ -133,13 +145,13 @@ pnpm run demo:acp
|
||||
|
||||
## 逐字记录类型(`ts type-equiv`)
|
||||
|
||||
[核心数据结构](core-data-structures/core.md)文档粘贴真实的类型定义,让读者看到确切的形状。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号:
|
||||
[核心数据结构](core-data-structures/core.md)文档会把真实类型声明及其原始 JSDoc 一并粘贴,让读者看到确切形状和源码契约。为防止粘贴内容在源码变化时漂移,请将其围栏为 ` ```ts type-equiv `(而不是 ` ```ts `),并在 `scripts/type-equiv.manifest.json` 中登记它镜像的源文件和符号:
|
||||
|
||||
```json
|
||||
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" }
|
||||
```
|
||||
|
||||
`pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明,并断言文档块与之一致(对空白和注释不敏感,因此文档块可以展示干净的定义,语义由行文承载)。它还强制 1:1 对应:每个 `ts type-equiv` 块恰好有一条 manifest 条目,反之亦然;因此不会有块被静默漏检,也不会有陈旧条目滞留。`doc-typecheck` 跳过 `ts type-equiv` 块(它们不能独立编译),并将其排除在 opt-out 比例之外。当你改动一个被记录的类型时,门禁会失败直到你更新粘贴内容;当你增删一个块时,请在同一个变更里更新 manifest。
|
||||
`pnpm run verify-type-equiv`(`doc-sync` 的一环)随后通过 TypeScript 解析器从源码提取该符号的声明及其附带的 JSDoc,并断言代码块同时匹配两者。比对会忽略空白和非 JSDoc 注释,但要求保留每条原始 JSDoc(包括成员文档),让读者同时看到源码契约和确切形状。该门禁还强制 1:1 对应:每个 `ts type-equiv` 块恰好有一条 manifest 条目,反之亦然;因此不会有块被静默漏检,也不会有陈旧条目滞留。`doc-typecheck` 跳过 `ts type-equiv` 块(它们不能独立编译),并将其排除在 opt-out 比例之外。当你改动一个已记录的类型声明或其 JSDoc 时,门禁会失败直到你更新粘贴内容;当你增删一个块时,请在同一个变更里更新 manifest。
|
||||
|
||||
## 架构上下文
|
||||
|
||||
|
||||
@@ -7,17 +7,17 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
|
||||
| Event | Mode | Declared in | Dispatchers | Listeners |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:363`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:141`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:150`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:285`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:362`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:141`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:150`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:285`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`tui`](../packages/ui/tui) |
|
||||
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:204`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic), [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
|
||||
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:214`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
|
||||
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:169`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:226`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp) |
|
||||
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:241`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:182`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:159`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:159`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:252`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
|
||||
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:262`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
|
||||
| `agent/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:272`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
||||
@@ -26,10 +26,10 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:70`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:53`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:40`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`invariants`](../packages/support/invariants), [`llm-replay`](../packages/support/llm-replay) |
|
||||
| `session/created` | `emit` | [`packages/core/session/src/index.ts:46`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:56`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/event` | `emit` | [`packages/core/session/src/index.ts:68`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence), [`stdio`](../packages/ui/stdio), [`token-meter`](../packages/llm/token-meter), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:78`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/created` | `emit` | [`packages/core/session/src/index.ts:47`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:57`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/event` | `emit` | [`packages/core/session/src/index.ts:69`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`acp`](../packages/ui/acp), [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence), [`stdio`](../packages/ui/stdio), [`token-meter`](../packages/llm/token-meter), [`tui`](../packages/ui/tui), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| `session/flush` | `parallel` | [`packages/core/session/src/index.ts:79`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:112`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`jsonrpc`](../packages/ui/jsonrpc) |
|
||||
| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:86`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:92`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
|
||||
@@ -14,6 +14,7 @@ The process decision behind this index is recorded in [the documentation graph R
|
||||
| [capability seams and core services](capability-seams.md) | `hybrid generated` |
|
||||
| [echo-agent app composition](../examples/echo-agent/composition.md) | `hybrid generated` |
|
||||
| [coding-agent app composition](../examples/coding-agent/composition.md) | `hybrid generated` |
|
||||
| [tui-agent app composition](../examples/tui-agent/composition.md) | `hybrid generated` |
|
||||
| [cordis-agent app composition](../examples/cordis-agent/composition.md) | `hybrid generated` |
|
||||
| [acp-agent app composition](../examples/acp-agent/composition.md) | `hybrid generated` |
|
||||
| [event producer/consumer matrix](event-producer-consumer.md) | `hybrid generated` |
|
||||
|
||||
@@ -123,9 +123,9 @@ Follow the Good versions; these sentence-level examples illustrate error categor
|
||||
- Good: `A green gate does not mean the translation is correct.`
|
||||
|
||||
### Code block comments — never translate
|
||||
- Source code block contains: `# REPL agent demo (needs DEEPSEEK_API_KEY)`
|
||||
- Bad: `# REPL agent 演示(需要 DEEPSEEK_API_KEY)`
|
||||
- Good: `# REPL agent demo (needs DEEPSEEK_API_KEY)` (byte-identical)
|
||||
- Source code block contains: `# readline coding agent (needs DEEPSEEK_API_KEY)`
|
||||
- Bad: `# readline 编码 agent(需要 DEEPSEEK_API_KEY)`
|
||||
- Good: `# readline coding agent (needs DEEPSEEK_API_KEY)` (byte-identical)
|
||||
|
||||
### Language switcher — English to Chinese
|
||||
- Source: `English | [中文](README.zh.md)`
|
||||
|
||||
+11
-8
@@ -101,7 +101,6 @@ flowchart TD
|
||||
pkg_invariants["invariants"]
|
||||
pkg_llm_replay["llm-replay"]
|
||||
pkg_loader_smoke["loader-smoke"]
|
||||
pkg_subagent_mock["subagent-mock"]
|
||||
end
|
||||
subgraph group_ui["packages/ui"]
|
||||
pkg_acp["acp"]
|
||||
@@ -110,6 +109,7 @@ flowchart TD
|
||||
pkg_permission["permission"]
|
||||
pkg_stdio["stdio"]
|
||||
pkg_tool_ask_user["tool-ask-user"]
|
||||
pkg_tui["tui"]
|
||||
pkg_user_approval["user-approval"]
|
||||
pkg_user_interaction["user-interaction"]
|
||||
end
|
||||
@@ -372,10 +372,6 @@ flowchart TD
|
||||
pkg_hooks_claude --> pkg_session_persistence
|
||||
pkg_hooks_claude --> pkg_subagent
|
||||
pkg_hooks_claude --> pkg_tools
|
||||
pkg_subagent_mock --> pkg_agent
|
||||
pkg_subagent_mock --> pkg_llm
|
||||
pkg_subagent_mock --> pkg_session
|
||||
pkg_subagent_mock --> pkg_subagent
|
||||
pkg_jsonrpc --> pkg_agent
|
||||
pkg_jsonrpc --> pkg_llm
|
||||
pkg_jsonrpc --> pkg_llm_deepseek
|
||||
@@ -387,6 +383,12 @@ flowchart TD
|
||||
pkg_stdio --> pkg_llm
|
||||
pkg_stdio --> pkg_session
|
||||
pkg_stdio --> pkg_user_interaction
|
||||
pkg_tui --> pkg_agent
|
||||
pkg_tui --> pkg_agent_loop
|
||||
pkg_tui --> pkg_llm
|
||||
pkg_tui --> pkg_session
|
||||
pkg_tui --> pkg_tools
|
||||
pkg_tui --> pkg_user_interaction
|
||||
pkg_agent_spine_demo --> pkg_agent
|
||||
pkg_agent_spine_demo --> pkg_agent_execution
|
||||
pkg_agent_spine_demo --> pkg_agent_loop
|
||||
@@ -433,6 +435,7 @@ flowchart TD
|
||||
pkg_stdio_demo --> pkg_stdio
|
||||
pkg_stdio_demo --> pkg_tool_ask_user
|
||||
pkg_stdio_demo --> pkg_tools
|
||||
pkg_stdio_demo --> pkg_tui
|
||||
pkg_stdio_demo --> pkg_user_interaction
|
||||
pkg_stdio_demo --> pkg_workspace_context
|
||||
```
|
||||
@@ -519,12 +522,12 @@ flowchart TD
|
||||
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
|
||||
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
|
||||
| [`subagent-mock`](../packages/support/subagent-mock) | `support` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`stdio`](../packages/ui/stdio) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`user-interaction`](../packages/ui/user-interaction) |
|
||||
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-execution`](../packages/core/agent-execution), [`agent-loop`](../packages/core/agent-loop), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| [`tui`](../packages/ui/tui) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
|
||||
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`home`](../packages/util/home), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tool-bash`](../packages/bash/tool-bash), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
||||
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
||||
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
||||
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/ui/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| [`stdio-demo`](../packages/examples/stdio-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`stdio`](../packages/ui/stdio), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| [`stdio-demo`](../packages/examples/stdio-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`stdio`](../packages/ui/stdio), [`tool-ask-user`](../packages/ui/tool-ask-user), [`tools`](../packages/core/tools), [`tui`](../packages/ui/tui), [`user-interaction`](../packages/ui/user-interaction), [`workspace-context`](../packages/context/workspace-context) |
|
||||
|
||||
+4
-2
@@ -22,8 +22,6 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
|
||||
|---|---|
|
||||
| [Prune dead public and result surface](proposed/simplification/2026-07-04-prune-dead-core-spine-surface.md) | 2026-07-04 |
|
||||
| [Make JSON-RPC completion and transport directional](proposed/simplification/2026-07-19-make-jsonrpc-directional.md) | 2026-07-19 |
|
||||
| [Retire the standalone subagent mock package](proposed/simplification/2026-07-19-retire-subagent-mock-package.md) | 2026-07-19 |
|
||||
| [Use one surface manager per session](proposed/simplification/2026-07-19-use-one-session-surface-manager.md) | 2026-07-19 |
|
||||
|
||||
### Architecture
|
||||
|
||||
@@ -89,6 +87,7 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
|
||||
| [Session query relationship tracing](implemented/feature/2026-07-13-session-query-tracing.md) | 2026-07-13 |
|
||||
| [Optional time-context plugin](implemented/feature/2026-07-14-time-context-plugin.md) | 2026-07-14 |
|
||||
| [Durable per-step time context](implemented/feature/2026-07-16-durable-per-step-time-context.md) | 2026-07-16 |
|
||||
| [Dedicated full-screen TUI front door](implemented/feature/2026-07-17-dedicated-full-screen-tui-front-door.md) | 2026-07-17 |
|
||||
|
||||
### Simplification
|
||||
|
||||
@@ -117,6 +116,8 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
|
||||
| [Drop unconsumed skill provider events](implemented/simplification/2026-07-12-drop-unconsumed-skill-provider-events.md) | 2026-07-12 |
|
||||
| [Prune unused web seam fields](implemented/simplification/2026-07-12-prune-unused-web-seam-fields.md) | 2026-07-12 |
|
||||
| [Simplify session-log representation](implemented/simplification/2026-07-12-simplify-session-log-representation.md) | 2026-07-12 |
|
||||
| [Retire the standalone subagent mock package](implemented/simplification/2026-07-19-retire-subagent-mock-package.md) | 2026-07-19 |
|
||||
| [Use one surface manager per session](implemented/simplification/2026-07-19-use-one-session-surface-manager.md) | 2026-07-19 |
|
||||
|
||||
### Architecture
|
||||
|
||||
@@ -213,6 +214,7 @@ Generated by `pnpm run gen-rfc-index` from the RFC tree — never edit by hand;
|
||||
| [Single-source the acp-agent replay config](implemented/testing/2026-07-04-single-source-acp-replay-config.md) | 2026-07-04 |
|
||||
| [Pin request-header content in one snapshot scenario](implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md) | 2026-07-06 |
|
||||
| [Extract the ACP snapshot suite into a support package](implemented/testing/2026-07-08-shared-acp-snapshot-package.md) | 2026-07-08 |
|
||||
| [Snapshot semantic terminal state for the TUI](implemented/testing/2026-07-18-tui-terminal-state-snapshots.md) | 2026-07-18 |
|
||||
|
||||
## Rejected
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export type SurfaceOp =
|
||||
|
||||
### SurfaceManager: delta-based, not full rebuild
|
||||
|
||||
A `SurfaceManager` class (private to `Session`) maintains one ordered `number[]` of event seqs. It tracks `_lastProcessedSeq` and processes only the new events since the last access rather than rescanning the entire log. Because the log is append-only, prior events never change; a seeded log is simply the initial suffix folded on first access. Replace locates its inclusive endpoints by array position and splices the replacement seq into that range; no link objects or seq-to-node map duplicate the order.
|
||||
A `Session` owns one `SurfaceManager` that maintains an ordered `number[]` of event seqs. The manager validates each seed or append candidate without applying it before commit, then processes only committed events since its previous synchronization rather than rescanning the entire log. `Session.surface` exposes the same manager through the readonly `SessionSurface` contract, so acceptance, derived history, compaction, and workspace context share one incremental state. Replace locates its inclusive endpoints by array position and splices the replacement seq into that range; no second manager, link objects, or seq-to-node map duplicates the order.
|
||||
|
||||
Delta processing is O(1) when no new events and O(new events) when new events arrive.
|
||||
|
||||
@@ -60,7 +60,7 @@ Every surface-eligible event must carry `surfaceOp` or it would disappear from d
|
||||
|
||||
## Consequences
|
||||
|
||||
- **`packages/core/session`**: `surface.ts` (`SurfaceManager`) maintains one ordered seq array; `SurfaceOp`/`SurfaceIntent` and the top-level session-event fields record how entries join it. `append()` requires a `SurfaceIntent` for surface events, `deriveMessages()` walks the surface as the sole derivation path, and `repair.ts` emits surface-aware closers. The seed constructor rejects a surface-eligible seed event missing its `surfaceOp` marker (see § Invariants).
|
||||
- **`packages/core/session`**: `surface.ts` (`SurfaceManager`) maintains one ordered seq array for candidate acceptance and live projection; `SessionSurface` is its readonly public view. `SurfaceOp`/`SurfaceIntent` and the top-level session-event fields record how entries join it. `append()` requires a `SurfaceIntent` for surface events, `deriveMessages()` walks the surface as the sole derivation path, and `repair.ts` emits surface-aware closers. The seed constructor rejects a surface-eligible seed event missing its `surfaceOp` marker (see § Invariants).
|
||||
- **`packages/core/agent-loop`**: All surface-capable appends pass surface opts. Chunk seqs are collected for `assistant/message` provenance; `tool/call` seqs are captured for `tool/result` provenance.
|
||||
- **`packages/session-persistence/session-persistence-sqlite`**: Two new nullable TEXT columns (`source_event_seqs`, `surface_op`) on the `events` table; `SCHEMA_VERSION` bumped (bump-and-reject, no migration).
|
||||
- **`packages/support/invariants`**: Surface-related validation rules.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Status: implemented
|
||||
|
||||
> The full seam is shipped: the `dsh-subagent` interface, the `dsh-subagent-mock` test backend, and the `dsh-tool-subagent` consumer; the two in-process backends (`dsh-subagent-spawn`, `dsh-subagent-fork`); the nested-agent snapshot infrastructure ([per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md)); and the out-of-process `dsh-subagent-acp` backend ([its RFC](2026-06-22-acp-subagent-backend.md)).
|
||||
> The full seam is shipped: the `dsh-subagent` interface and `dsh-tool-subagent` consumer; the two in-process backends (`dsh-subagent-spawn`, `dsh-subagent-fork`); the nested-agent snapshot infrastructure ([per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md)); and the out-of-process `dsh-subagent-acp` backend ([its RFC](2026-06-22-acp-subagent-backend.md)).
|
||||
|
||||
## Problem
|
||||
|
||||
@@ -32,7 +32,6 @@ A new package group `packages/subagent/`:
|
||||
| `@deepseek-ai/dsh-subagent-spawn` | implementation: a fresh in-process child via `ctx.agents.create` |
|
||||
| `@deepseek-ai/dsh-subagent-fork` | implementation: an in-process child seeded with a snapshot of the parent's log |
|
||||
| `@deepseek-ai/dsh-subagent-acp` | implementation: an ACP client driving a configured child process |
|
||||
| `@deepseek-ai/dsh-subagent-mock` | support: a scripted provider for testing the seam through the real load path |
|
||||
| `@deepseek-ai/dsh-tool-subagent` | consumer: the model-facing `subagent` tool over `ctx.subagents` |
|
||||
|
||||
### The primitive: async `start → SubagentRun`
|
||||
@@ -62,7 +61,7 @@ Each subagent runs in its **own `Session`** (own id, `parentSession` lineage), p
|
||||
|
||||
## Testing
|
||||
|
||||
The seam is tested through the real Cordis Loader/export path, which catches the export-shape failure described in [postmortem 0001](../../../postmortem/0001-acp-default-export-drops-inject.md). Registry tests cover reload safety, duplicate names, and start-time capability rejection; nested-agent scenarios replay keylessly through [per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md); in-process backends also have real-loop unit tests and a with-key e2e.
|
||||
Registry and tool tests replace only the nondeterministic child boundary with a package-local scripted provider while exercising the real `SubagentService`, lifecycle, task integration, and model-facing tool. Provider and consumer export shapes retain their Loader regression coverage for the failure described in [postmortem 0001](../../../postmortem/0001-acp-default-export-drops-inject.md). Registry tests cover reload safety, duplicate names, and start-time capability rejection; nested-agent scenarios replay keylessly through [per-session snapshot replay](../testing/2026-06-22-subagent-snapshot-replay.md); in-process backends also have real-loop unit tests and a with-key e2e.
|
||||
|
||||
## Consequences
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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
|
||||
2026-07-17-dedicated-full-screen-tui-front-door.md: 5e66b85fa23ef394b88fd16880cf218ac5cc2202
|
||||
2026-07-17-dedicated-full-screen-tui-front-door.zh.md: 4ccfde2d633115a41b24227ee78f7e4010bd70ee
|
||||
@@ -0,0 +1,48 @@
|
||||
# RFC: Dedicated full-screen TUI front door
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-17-dedicated-full-screen-tui-front-door.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The line-oriented `@deepseek-ai/dsh-stdio` front door works in pipes and ordinary terminals, but a full-screen coding interface must own raw input, differential screen drawing, cursor state, overlays, and terminal restoration. Combining those contracts in one UI plugin couples the pipe-safe path to a TTY-only lifecycle and makes it unclear which terminal behavior a composition selects.
|
||||
|
||||
The interactive channel must remain a Cordis plugin over the same agent, session, tool, and user-interaction services as every other front door. It needs to resume durable history, follow compaction replacements, display tool-owned presentation, and restore the terminal on startup failure and disposal. A standalone chat application or a second agent composition would duplicate behavior outside the plugin graph.
|
||||
|
||||
## Decision
|
||||
|
||||
DeepSeek Harness ships [`@deepseek-ai/dsh-tui`](../../../../packages/ui/tui/README.md) as a dedicated Cordis plugin. It owns terminal input and presentation only; agent lifecycle, session persistence, tool execution, and the model-facing question tool remain separate composition entries. The plugin requires both stdin and stdout to be TTYs and fails instead of silently changing to line-oriented behavior.
|
||||
|
||||
The app layer selects a concrete terminal front door before mounting it. `@deepseek-ai/dsh-stdio-demo` can resolve `auto` from the two process streams, while the `coding-agent` and `tui-agent` leaves explicitly select readline and TUI respectively. The TUI leaf reuses the coding agent's backend and tool composition through an asserted include patch, so the three runnable agent leaves remain symmetric without duplicating deployment choices.
|
||||
|
||||
The selected front door receives the exact generated or resumed `SessionId` used by the pre-created agent. It mounts before the agent composition, waits for the matching root agent, and enters full-screen mode only after that agent exists. A matching `agent-loop/config-start-failed` event is therefore reported before screen takeover and exits with status 1.
|
||||
|
||||
### Session projection and interaction
|
||||
|
||||
The TUI rebuilds the transcript from the active `session.surface` and reprojects it whenever an event carries a `surfaceOp`, so resumed and compacted history matches the model-visible conversation. It renders Markdown text and reasoning, token totals, the latest `todo/write` plan, and tool cards produced through each tool definition's `presentCall` and `presentResult` methods. Pending chunks and tool calls update the same components that completed events settle.
|
||||
|
||||
Editor input calls `agent.send()` while idle and `agent.steer()` while a turn is running. Cancellation, reasoning visibility, tool-card expansion, redraw, transcript clearing, and exit are terminal-only controls. The plugin registers the shared `userInteraction` provider and presents questions as queued keyboard overlays; agent behavior and answer logging remain owned by their existing services.
|
||||
|
||||
### Terminal ownership
|
||||
|
||||
Before model output, session data, tool presentation, questions, configuration, or diagnostics reach pi-tui or the terminal title, `displayText()` renders C0 and C1 controls other than line feeds as visible hexadecimal escapes. Only the TUI and pi-tui create ANSI control sequences.
|
||||
|
||||
The built-in palette uses standard 16-color ANSI foregrounds and SGR attributes, keeps body text and backgrounds at terminal defaults, and uses reverse video for selection. Host terminals therefore remap the interface for light and dark themes without a TUI-specific theme setting; `color: false` removes styling.
|
||||
|
||||
## Verification
|
||||
|
||||
The implemented [TUI terminal-state snapshot RFC](../testing/2026-07-18-tui-terminal-state-snapshots.md) owns the four-layer verification contract: direct behavior tests, transient semantic terminal snapshots, recorded JSONL journeys through production tools, and Loader/PTY smoke tests. The package README owns configuration, commands, model-visible effects, and current limitations.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep readline and full-screen modes inside `@deepseek-ai/dsh-stdio`** — rejected because line-oriented output and differential TTY rendering have different dependencies, input rules, logging ownership, and teardown obligations. Separate packages keep the pipe-safe contract small and explicit.
|
||||
- **Let the TUI plugin silently downgrade when either stream is not a TTY** — rejected because a fallback hides deployment mistakes and changes interaction semantics. The app bundle may select a front door with `auto`; an explicitly mounted TUI fails loud.
|
||||
- **Keep TUI wiring and tests under the readline `coding-agent` leaf** — rejected because one leaf would represent two distinct front doors and break symmetry with `acp-agent`. A dedicated `tui-agent` leaf owns TUI overlays and tests while reusing the coding agent's backend composition.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Interactive terminal work gains a stateful Markdown, card, plan, and question interface without changing the line-oriented protocol used by pipes and automation.
|
||||
- The TUI carries a pi-tui dependency and a strict TTY requirement; non-TTY deployments select `@deepseek-ai/dsh-stdio` at composition time.
|
||||
- Session projection makes resume and compaction consistent with the durable conversation, but one configured session owns the transcript and editor.
|
||||
- Tool packages extend terminal cards through their existing presentation methods without adding tool-specific branches to the TUI.
|
||||
@@ -0,0 +1,48 @@
|
||||
# RFC: 独立的全屏 TUI 入口
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-17-dedicated-full-screen-tui-front-door.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
逐行输出的 `@deepseek-ai/dsh-stdio` 入口适用于管道和普通终端,但全屏编码界面必须负责原始输入、差分绘制、光标状态、浮层和终端恢复。把这两类契约合并到一个 UI 插件中,会迫使管道安全路径依赖仅适用于 TTY 的生命周期,也使组合无法明确表达所选终端行为。
|
||||
|
||||
交互通道必须继续作为 Cordis 插件,使用与其他入口相同的 agent(智能体)、会话、工具和用户交互服务。它需要恢复持久历史、跟随压缩替换、显示工具自有的呈现内容,并在启动失败和资源释放时恢复终端。独立聊天应用或第二套 agent 组合会在插件图之外重复实现这些行为。
|
||||
|
||||
## 决策
|
||||
|
||||
DeepSeek Harness 将 [`@deepseek-ai/dsh-tui`](../../../../packages/ui/tui/README.md) 作为独立的 Cordis 插件交付。该插件只负责终端输入与呈现;agent 生命周期、会话持久化、工具执行以及模型可见的提问工具仍由不同组合项负责。插件要求 stdin 和 stdout 均为 TTY;条件不满足时会失败,不会静默切换为逐行输出。
|
||||
|
||||
应用组合层在挂载前选择具体的终端入口。`@deepseek-ai/dsh-stdio-demo` 可以根据两个进程流通过 `auto` 作出选择,`coding-agent` 和 `tui-agent` 叶节点则分别明确选择 readline 与 TUI。TUI 叶节点通过带断言的 include patch 复用 coding agent 的后端和工具组合,使三个可运行的 agent 叶节点保持对称,同时避免重复部署选项。
|
||||
|
||||
所选入口接收预创建 agent 使用的同一个新建或恢复 `SessionId`。入口先于 agent 组合挂载,等待相符的根 agent 出现,然后才进入全屏模式。因此,相符的 `agent-loop/config-start-failed` 事件会在接管屏幕前报告,并以状态码 1 退出。
|
||||
|
||||
### 会话投影与交互
|
||||
|
||||
TUI 从活跃的 `session.surface` 重建 transcript(文本记录),并在事件携带 `surfaceOp` 时重新投影,因此恢复或压缩后的历史与模型可见会话保持一致。TUI 渲染 Markdown 文本与推理、token 用量、最新 `todo/write` 计划,以及各工具定义通过 `presentCall` 和 `presentResult` 方法生成的工具卡片。进行中的分片与工具调用会更新同一组组件,随后由完成事件收束状态。
|
||||
|
||||
agent 空闲时,编辑器输入调用 `agent.send()`;轮次运行中则调用 `agent.steer()`。取消、推理显隐、工具卡片展开、重绘、清空 transcript 和退出都只是终端控制。插件注册共享的 `userInteraction` 提供方,以排队的键盘浮层呈现问题;agent 行为和答案日志仍由既有服务负责。
|
||||
|
||||
### 终端所有权
|
||||
|
||||
在模型输出、会话数据、工具呈现、问题、配置或诊断信息进入 pi-tui 或终端标题前,`displayText()` 会把换行之外的 C0 和 C1 控制字符显示为十六进制转义文本。只有 TUI 和 pi-tui 可以生成 ANSI 控制序列。
|
||||
|
||||
内置配色仅使用标准 16 色 ANSI 前景色和 SGR 属性,正文文字和背景沿用终端默认值,选中项使用反显。因此,宿主终端可以直接按浅色或深色主题重映射界面,无需 TUI 专用主题设置;`color: false` 会移除样式。
|
||||
|
||||
## 验证
|
||||
|
||||
已实现的 [TUI 终端状态快照 RFC](../testing/2026-07-18-tui-terminal-state-snapshots.md) 规定四层验证契约:直接行为测试、瞬态语义终端快照、通过生产工具执行的已录制 JSONL 流程,以及 Loader/PTY 冒烟测试。包(package)README 负责记录配置、命令、模型可见效果和当前限制。
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **把 readline 与全屏模式都保留在 `@deepseek-ai/dsh-stdio` 中**:不予采纳,因为逐行输出和差分 TTY 渲染具有不同的依赖、输入规则、日志所有权和资源清理义务。拆分为独立包可以让管道安全契约保持精简、明确。
|
||||
- **当任一进程流不是 TTY 时,让 TUI 插件静默降级**:不予采纳,因为回退会掩盖部署错误并改变交互语义。应用包可以通过 `auto` 选择入口;明确挂载的 TUI 会快速失败。
|
||||
- **把 TUI 接线与测试保留在 readline `coding-agent` 叶节点下**:不予采纳,因为一个叶节点会代表两个不同入口,也会破坏它与 `acp-agent` 的对称性。独立的 `tui-agent` 叶节点负责 TUI 浮层和测试,同时复用 coding agent 的后端组合。
|
||||
|
||||
## 后果
|
||||
|
||||
- 交互式终端获得带状态的 Markdown、卡片、计划和提问界面,同时不会改变管道与自动化使用的逐行协议。
|
||||
- TUI 会引入 pi-tui 依赖并严格要求 TTY;非 TTY 部署在组合时选择 `@deepseek-ai/dsh-stdio`。
|
||||
- 会话投影使恢复和压缩与持久会话保持一致,但只有一个已配置会话拥有 transcript 和编辑器。
|
||||
- 工具包通过既有呈现方法扩展终端卡片,无需在 TUI 中增加工具专用分支。
|
||||
@@ -10,7 +10,7 @@ So the work had two intertwined questions: **what belongs in such a catalog** (t
|
||||
|
||||
## Decision
|
||||
|
||||
A new `docs/core-data-structures/` folder catalogs the vocabulary, with a new `verify-type-equiv` doc-sync gate that keeps every pasted type definition byte-identical to its source.
|
||||
A new `docs/core-data-structures/` folder catalogs the vocabulary, with a new `verify-type-equiv` doc-sync gate that keeps every pasted type declaration and its JSDoc synchronized with source.
|
||||
|
||||
### What counts as "core" — the spine-vs-seam line
|
||||
|
||||
@@ -27,10 +27,10 @@ The rule that settled the remaining cases: ***the type you write, hold, or recei
|
||||
|
||||
### The `ts type-equiv` mechanism — literal AND drift-proof
|
||||
|
||||
The durability requirement was specific: the doc should show the **literal** current type definition (so a reader sees the real shape, not a paraphrase) **and** be mechanically guaranteed to match source. The repo already compiles fenced ` ```ts ` blocks (`doc-typecheck`), but a real typechecked block needs import noise and proves only *assignability*, not *byte-equality* — a renamed field with the same type would pass. So:
|
||||
The durability requirement was specific: the doc shows the **literal** current type declaration and original JSDoc (so a reader sees the real shape and source contract, not a paraphrase) **and** is mechanically guaranteed to match source. The repo already compiles fenced ` ```ts ` blocks (`doc-typecheck`), but a real typechecked block needs import noise and proves only *assignability* — a renamed field or changed JSDoc can pass. So:
|
||||
|
||||
- Type definitions are pasted verbatim into a dedicated ` ```ts type-equiv ` fence. `doc-typecheck` recognizes the fence and skips it (a bare definition is not standalone-compilable), and **excludes it from the opt-out ratio** — it is a separately-checked category, not an unchecked sketch.
|
||||
- A new `scripts/verify-type-equiv.ts` extracts each block via the TypeScript parser and asserts a **verbatim source match** against the declared symbol — chosen over a compiled `_Check` assertion precisely because byte-equality, not assignability, is the property we want.
|
||||
- Type declarations and their JSDoc are pasted verbatim into a dedicated ` ```ts type-equiv ` fence. `doc-typecheck` recognizes the fence and skips it (a bare definition is not standalone-compilable), and **excludes it from the opt-out ratio** — it is a separately-checked category, not an unchecked sketch.
|
||||
- A new `scripts/verify-type-equiv.ts` extracts each block via the TypeScript parser and asserts that its declaration structure and every JSDoc comment match the declared symbol, ignoring only formatting whitespace and non-JSDoc comments. This is chosen over a compiled `_Check` assertion because source names and documentation identity, not assignability, are the properties the catalog preserves.
|
||||
- Provenance lives in a central `scripts/type-equiv.manifest.json` (`{ doc, symbol, source }` entries), **not** in directive comments in the prose. The script enforces a **1:1 correspondence**: every type-equiv block has exactly one manifest entry and vice versa, so a block can never be silently unchecked and an entry can never rot.
|
||||
- Wired into `doc-sync`, so it runs in the same lefthook pre-push and CI paths as the other doc gates.
|
||||
|
||||
@@ -41,7 +41,7 @@ The durability requirement was specific: the doc should show the **literal** cur
|
||||
## Alternatives considered
|
||||
|
||||
- **A flat dump of all cross-package vocabulary** — the `BashExecRequest` test case killed it: if seam vocabulary is "core", the catalog helps no one; the tiered spine-vs-seam structure won.
|
||||
- **A compiled `_Check` assignability assertion** instead of the verbatim source match — rejected because byte-equality, not assignability, is the property we want: a renamed field with the same type would pass assignability.
|
||||
- **A compiled `_Check` assignability assertion** instead of the source match — rejected because assignability does not preserve names or JSDoc: a renamed field with the same type or a changed contract comment would pass.
|
||||
- **Provenance as directive comments in the prose** — rejected for the central manifest, whose enforced 1:1 correspondence means a block can never be silently unchecked and an entry can never rot.
|
||||
|
||||
## Verification lesson
|
||||
|
||||
+2
-2
@@ -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
|
||||
2026-07-19-retire-subagent-mock-package.md: 2731d35448cbc4dc9db4c92579c35202a53eefdd
|
||||
2026-07-19-retire-subagent-mock-package.zh.md: 919c56502227157465277cc07c23fb7096763684
|
||||
2026-07-19-retire-subagent-mock-package.md: 47174d22eaf27c5a5509280793dd50d70c542d00
|
||||
2026-07-19-retire-subagent-mock-package.zh.md: bfad78e1912f9d5153196df3fea2494c88436a91
|
||||
@@ -0,0 +1,34 @@
|
||||
# RFC: Retire the standalone subagent mock package
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-19-retire-subagent-mock-package.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`@deepseek-ai/dsh-subagent-mock` was a configurable test double packaged as a workspace plugin. Its only external consumers were the `tool-subagent` unit suite and the tool-catalog generator; no runtime package, example, snapshot configuration, or real provider loaded it.
|
||||
|
||||
That narrow fixture carried a manifest, exports, peer and development dependencies, project references, package README obligations, Loader composition tests, module-graph membership, and documentation exceptions. The tool-catalog generator mounted it only to make production consumers register their schemas and never executed a child.
|
||||
|
||||
## Decision
|
||||
|
||||
The standalone package is deleted. Its scripted child behavior now lives in `packages/subagent/tool-subagent/tests/scripted-provider.ts`, where tests mount the real `SubagentService`, provider registry, tool implementation, and task runtime while replacing only the nondeterministic child boundary.
|
||||
|
||||
The local fixture retains deterministic replies, structured results, stop reasons, cancellation before and after publication, conversation-inheritance descriptors, and effect-scoped disposal. Package-specific Schemastery and Loader-export tests disappear because the fixture is no longer a deployable plugin.
|
||||
|
||||
The tool-catalog generator registers a minimal local `SubagentProvider` descriptor before mounting `ToolSubagent` or the workflow engine. The descriptor cannot start a child; it exists only to satisfy production load-time dependencies while harvesting schemas from the real consumers.
|
||||
|
||||
Workspace project references, package dependencies, lockfile entries, graph metadata, support-package prose, config-catalog entries, and README gate exceptions no longer name the retired package.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep a reusable mock package for future tests.** Reuse never materialized outside one test file and one generator. A future second behavioral consumer can extract a shared fixture after its contract is known; pre-packaging it made test infrastructure look like a supported backend.
|
||||
|
||||
**Generate subagent schemas without mounting production consumers.** Hand-constructing or importing schemas would weaken the catalog check that the real registry and tool composition expose the documented shape. A minimal provider descriptor preserves that check without carrying executable fake-backend behavior.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The workspace has one fewer deployable package and no test-only node in the capability or module graphs.
|
||||
- `tool-subagent` tests retain foreground, background-task, lifecycle, cancellation, reply, stop-reason, and structured-result coverage through production services.
|
||||
- Tool-catalog output remains generated from production registrations and is byte-for-byte unchanged.
|
||||
- Runtime and example packages gain no dependency on test fixtures.
|
||||
@@ -0,0 +1,34 @@
|
||||
# RFC: 撤销独立的 subagent mock 包
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-19-retire-subagent-mock-package.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`@deepseek-ai/dsh-subagent-mock` 曾是一个以工作区插件形式发布的可配置测试替身。它仅有两个外部消费方:`tool-subagent` 单元测试和工具目录生成器;运行时包、示例、快照配置和真实提供方都不会加载它。
|
||||
|
||||
这个用途狭窄的 fixture(测试前置数据)需要维护 manifest(元数据清单)、导出、对等依赖(peer dependency)与开发依赖、项目引用、包(package)README 契约、Loader 组合测试、模块图成员关系以及文档例外。工具目录生成器挂载它,只是为了让生产消费方注册 schema,并不会执行子 agent。
|
||||
|
||||
## 决策
|
||||
|
||||
删除独立包。脚本化子 agent 行为现位于 `packages/subagent/tool-subagent/tests/scripted-provider.ts`;测试挂载真实的 `SubagentService`、提供方注册表、工具实现和任务运行时,只替换具有不确定性的子 agent 边界。
|
||||
|
||||
本地 fixture 保留确定性回复、结构化结果、停止原因、发布前后的取消、对话继承描述和作用域化的 dispose(资源释放)覆盖。由于 fixture 不再是可部署插件,删除包专用的 Schemastery 与 Loader 导出测试。
|
||||
|
||||
工具目录生成器在挂载 `ToolSubagent` 或工作流引擎之前,注册一个最小本地 `SubagentProvider` 描述。该描述无法启动子 agent;它只用于满足生产消费方的加载时依赖,同时从真实消费方提取 schema。
|
||||
|
||||
工作区项目引用、包依赖、锁文件条目、图元数据、支持包说明、配置目录条目和 README 门禁例外不再提及已撤销的包。
|
||||
|
||||
## 备选方案
|
||||
|
||||
**为未来测试保留可复用 mock 包。** 除一个测试文件和一个生成器外,复用需求始终没有出现。未来产生第二个行为消费方时,可以在共享契约明确后再提取 fixture;提前将其打包会使测试基础设施看起来像受支持的后端。
|
||||
|
||||
**不挂载生产消费方,直接生成 subagent schema。** 手工构造或直接导入 schema,会削弱目录门禁对真实注册表与工具组合是否公开文档结构的校验。最小提供方描述能保留该校验,而无需携带可执行的虚假后端行为。
|
||||
|
||||
## 影响
|
||||
|
||||
- 工作区减少一个可部署包,能力图与模块图也不再包含测试专用节点。
|
||||
- `tool-subagent` 测试继续通过生产服务覆盖前台、后台任务、生命周期、取消、回复、停止原因和结构化结果。
|
||||
- 工具目录输出仍根据生产注册生成,并保持字节级一致。
|
||||
- 运行时包与示例包都不会依赖测试 fixture。
|
||||
+2
-2
@@ -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
|
||||
2026-07-19-use-one-session-surface-manager.md: 0e4fa14b21f5054e1ace425712506c5c060f2251
|
||||
2026-07-19-use-one-session-surface-manager.zh.md: 26126310eefaf56aa45efc428a54e65f0cf35947
|
||||
2026-07-19-use-one-session-surface-manager.md: 0c0fd70717ff4b49e8817e88592d0781d9ccbd09
|
||||
2026-07-19-use-one-session-surface-manager.zh.md: 6d11c9bb475b0ee5a568f96678d0251f1160dda3
|
||||
@@ -0,0 +1,37 @@
|
||||
# RFC: Use one surface manager per session
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-19-use-one-session-surface-manager.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`Session` maintained two `SurfaceManager` instances over the same append-only event log. One validated seed and append candidates, while a second lazy instance independently folded committed events for `session.surface`, derived messages, compaction, and workspace context. Once the public surface had been read, every later event advanced duplicate node and replacement-generation state without creating a separate authority or failure boundary.
|
||||
|
||||
## Decision
|
||||
|
||||
Each `Session` owns one eagerly constructed `SurfaceManager`. Seed and append acceptance call `validateNext()` on that manager before committing an event, and `session.surface` returns the same object through this readonly contract:
|
||||
|
||||
```ts
|
||||
export interface SessionSurface {
|
||||
readonly nodes: readonly number[]
|
||||
readonly replaceGeneration: number
|
||||
}
|
||||
```
|
||||
|
||||
Candidate validation remains atomic. `validateNext()` may synchronize committed log entries, but it only plans the uncommitted candidate. The candidate enters manager state after `log.push()` and the next delta synchronization, so surface validation failures and pre-commit `internal/dispatch` vetoes leave no phantom node or replacement generation.
|
||||
|
||||
`foldSurface()` remains the detached full-log replay function for offline validation and reconstruction. It uses the same transitions and agrees with the live manager for every committed prefix without sharing mutable state.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep acceptance and projection state separate.** Separate instances appeared to isolate public reads from validation, but callers already receive borrowed surface state and the declared readonly contract prevents ordinary mutation. Duplicating the manager was not a runtime trust boundary.
|
||||
|
||||
**Recompute the public surface from the full log on every access.** This removed duplicate cached state but gave up incremental derivation and made repeated request construction scale with complete session history.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Acceptance, `session.surface`, derived messages, compaction, and workspace context observe one incremental state.
|
||||
- `Session.surface` exposes no validation method, while its object identity and borrowed readonly node array remain stable.
|
||||
- A hostile cast can still corrupt borrowed state; JavaScript callers that deliberately bypass the readonly contract remain outside the supported same-process boundary.
|
||||
- Surface, seed, dispatch-veto, request-reconstruction, compaction, and workspace-context tests exercise the shared manager and detached replay paths.
|
||||
@@ -0,0 +1,37 @@
|
||||
# RFC: 每个会话只使用一个表层管理器
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-19-use-one-session-surface-manager.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`Session` 曾针对同一份仅追加事件日志维护两个 `SurfaceManager` 实例。一个实例负责校验种子事件和追加候选事件,另一个延迟创建的实例则独立折叠已提交事件,供 `session.surface`、派生消息、压缩(compaction)和工作区上下文使用。一旦读取公共表层,之后的每个事件都会推进两份重复的节点状态与替换代数状态,却没有形成独立真源或失败边界。
|
||||
|
||||
## 决策
|
||||
|
||||
每个 `Session` 主动创建并只持有一个 `SurfaceManager`。种子事件与追加事件的接纳流程在提交事件之前调用该管理器的 `validateNext()`,`session.surface` 则通过以下只读契约返回同一个对象:
|
||||
|
||||
```ts
|
||||
export interface SessionSurface {
|
||||
readonly nodes: readonly number[]
|
||||
readonly replaceGeneration: number
|
||||
}
|
||||
```
|
||||
|
||||
候选事件校验仍保持原子性。`validateNext()` 可以同步已提交的日志事件,但对尚未提交的候选事件只制定变更计划。候选事件在 `log.push()` 之后、下一次增量同步时才进入管理器状态,因此表层校验失败或提交前 `internal/dispatch` 否决都不会留下虚假节点或替换代数。
|
||||
|
||||
`foldSurface()` 仍是离线校验与重建使用的分离式完整日志回放函数。它使用相同的状态转换,并且对每个已提交前缀都与活跃管理器一致,但不共享可变状态。
|
||||
|
||||
## 备选方案
|
||||
|
||||
**继续分离接纳状态与投影视图。** 两个独立实例看似能够隔离公共读取和校验,但调用方取得的本来就是借用的表层状态,声明的只读契约会阻止普通修改。复制管理器并不能构成运行时信任边界。
|
||||
|
||||
**每次读取都根据完整日志重新计算公共表层。** 该方案能消除重复缓存状态,但会放弃增量派生,使每次请求构造都随完整会话历史增长。
|
||||
|
||||
## 影响
|
||||
|
||||
- 接纳流程、`session.surface`、派生消息、压缩和工作区上下文观察同一份增量状态。
|
||||
- `Session.surface` 不暴露校验方法,同时保持对象标识和借用的只读节点数组稳定。
|
||||
- 恶意类型断言仍可破坏借用状态;刻意绕过只读契约的 JavaScript 调用方不属于受支持的同进程边界。
|
||||
- 表层、种子、调度否决、请求重建、压缩和工作区上下文测试覆盖共享管理器与分离回放路径。
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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
|
||||
2026-07-18-tui-terminal-state-snapshots.md: 280c2b4faec3bd5e14a24a5df7bc901ad31718cd
|
||||
2026-07-18-tui-terminal-state-snapshots.zh.md: d1c609c6ce511bba1f498b72444af662b4444578
|
||||
@@ -0,0 +1,70 @@
|
||||
# RFC: Snapshot semantic terminal state for the TUI
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-18-tui-terminal-state-snapshots.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The TUI is a stateful renderer. Its user-visible result depends on ANSI parsing, differential frames, wrapping, scrollback, viewport position, terminal width, focus, cursor state, and each tool's presentation intent. Unit tests that collect `Terminal.write()` fragments can prove event handling, but they cannot prove the final screen a terminal displays. The same screen may also be emitted through different write fragments, so pinning those fragments creates false regressions.
|
||||
|
||||
Component-line snapshots stop before ANSI reaches a terminal and miss cursor movement, clearing, styling, overlay composition, and reflow. Raster screenshots include font and platform rendering noise that is unrelated to the TUI contract. A completed flow built by directly appending plausible session events has another blind spot: it proves the renderer accepts those shapes, not that the production agent loop and tool implementations produce them.
|
||||
|
||||
The TUI therefore needs a deterministic, reviewable representation of terminal state, recorded model journeys that execute the real downstream stack, and a smaller test at the real process and PTY boundary.
|
||||
|
||||
## Decision
|
||||
|
||||
TUI coverage has four complementary layers:
|
||||
|
||||
1. `packages/ui/tui/tests/tui.spec.ts` tests event mapping, input routing, disposal, and error behavior directly.
|
||||
2. `packages/ui/tui/tests/tui.snapshot.ts` mounts the production TUI against a headless terminal emulator for transient states that a completed session log cannot retain: in-flight streaming, pending tool calls, overlays, expansion, compaction reflow, errors, and shutdown.
|
||||
3. `examples/tui-agent/tests/tui.snapshot.ts` replays committed JSONL session logs through the production agent loop and real tools, then compares the resulting semantic terminal state.
|
||||
4. `examples/tui-agent/tests/tui-keyless-smoke.e2e.ts` boots the real Loader composition in a PTY, drives a scripted conversation through streaming and `ask_user_question`, and verifies startup, input, exit, failure reporting, and terminal restoration.
|
||||
|
||||
The runnable TUI has its own `examples/tui-agent` leaf beside the readline `coding-agent` and `acp-agent` leaves. It reuses the coding agent's backend and tool composition through an asserted include patch while fixing the shared terminal app to `ui.mode: tui`; TUI snapshots and PTY tests live with that leaf.
|
||||
|
||||
### Recorded-session replay
|
||||
|
||||
Each example-level scenario directory owns `session.jsonl`, optional child logs `session.<n>.jsonl`, and `terminal.golden.txt`. The primary log supplies user-authored `user/message` prompts and the recorded `assistant/chunk` sequence. `dsh-llm-replay` derives one model-call script per session, binds child logs to fresh child sessions, and is the only mocked boundary. The agent loop, bash and filesystem implementations, Code Mode worker, subagent provider, workflow worker, Cordis tools, presenters, and TUI are production implementations.
|
||||
|
||||
The suite rejects a journey when its tool-call sequence differs, an expected event count is missing, a tool result is an error, a turn ends in error, a workflow lifecycle is incomplete, or the live child-session count differs from the fixture set. These assertions prevent an attractive terminal golden from hiding a failed or bypassed production path.
|
||||
|
||||
The live-model fixtures use `DSH_SNAPSHOT=record`; record mode rewrites their primary and child JSONL logs and terminal goldens. The deterministic Cordis toolchain keeps an authored complete JSONL script because reliably coercing a live model through five exact tool boundaries and two children is not a stable recording contract. `DSH_SNAPSHOT=refresh` replays every committed script keylessly and rewrites only derived terminal goldens. Plain replay compares without writing, and unknown mode values fail loud.
|
||||
|
||||
### Semantic terminal projection
|
||||
|
||||
The package-local `HeadlessTerminal` implements the same pi-tui `Terminal` interface as the process terminal and feeds every ANSI write into the pinned `@xterm/headless` parser. Snapshot code waits for synchronized frames to quiesce before reading state, so a checkpoint represents a completed screen rather than a timer-dependent write prefix.
|
||||
|
||||
Each golden projects dimensions, active-buffer and viewport coordinates, lifecycle and cursor state, rows, wrap markers, and non-default style ranges into text. Scroll-heavy cards capture the used buffer; overlays capture the visible viewport. Text and style remain separate so a reviewer can distinguish content changes from presentation changes without decoding ANSI bytes.
|
||||
|
||||
Every checkpoint enforces theme independence across the complete terminal state: no RGB colors, no palette entries beyond ANSI 0–15, and no explicit background colors. Reverse video remains valid for selection because it uses terminal defaults. Both suites own closed inventories that reject missing scenarios, missing checkpoints, and orphaned golden files.
|
||||
|
||||
### Required scenario matrix
|
||||
|
||||
| Layer | Scenario | Contract pinned |
|
||||
|---|---|---|
|
||||
| Recorded journey | Multi-turn conversation | Recorded reasoning/text chunks, two input turns, retained history, token totals, and idle editor state |
|
||||
| Recorded journey | Todo plan | Real `todo_write` execution, result card, and persistent plan rendering |
|
||||
| Recorded journey | Bash terminal card | Real local executor output, description, exit status, and completed terminal card |
|
||||
| Recorded journey | Parallel filesystem reads | Two calls from one assistant message, real file contents, ordering, and separate completed cards |
|
||||
| Recorded journey | Code Mode | Real `run_code` worker execution, two `tool/code-dispatch` events, captured program output, and completed card |
|
||||
| Recorded journey | Dynamic workflow | Real workflow worker, phase lifecycle, replayed child session, structured return value, and completed card |
|
||||
| Recorded journey | Cordis dynamic toolchain | Real mount, Code Mode inspect, direct subagent, workflow child, unmount, and all production presenters |
|
||||
| Transient state | Streaming and pending advanced calls | In-flight reasoning/text plus pending Code Mode, workflow, and Cordis cards that disappear from completed logs |
|
||||
| Transient state | Cards, interaction, layout, failure, and shutdown | Collapsed/expanded card families, question validation, compaction replacement, resize reflow, help/errors, cursor restoration, and terminal stop |
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Snapshot raw terminal writes** — rejected because differential rendering may change write boundaries without changing the screen, while cursor and clear sequences are unreadable in review.
|
||||
- **Snapshot component render lines before terminal output** — rejected because it does not test ANSI parsing, cursor movement, overlays, viewport behavior, or independent components in one frame.
|
||||
- **Build every completed flow by appending session events** — rejected because a hand-authored event sequence can drift from the agent loop, tool execution, child-session binding, or worker behavior while its presentation test stays green. Direct event construction remains limited to transient renderer states.
|
||||
- **Reuse ACP stdout goldens as the TUI oracle** — rejected because a recorded model journey is transport-neutral but its presentation is not. TUI scenarios own terminal goldens while using the same JSONL replay vocabulary.
|
||||
- **Commit raster screenshots** — rejected because fonts, glyph metrics, antialiasing, and host terminal themes make them platform-sensitive and make semantic style changes difficult to review.
|
||||
- **Use only PTY end-to-end tests** — rejected because raw PTY output is a stream of historical drawing operations, not queryable final state. PTY tests retain the real Loader/input/teardown boundary, while the emulator owns broad state coverage.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Completed advanced snapshots now fail when the real Code Mode, workflow, subagent, filesystem, bash, or Cordis path breaks, rather than accepting a fabricated result event.
|
||||
- TUI visual regressions produce readable cell-and-style diffs, while JSONL fixtures retain the exact model chunks that made the production path execute.
|
||||
- The emulator uses xterm's proposed buffer API. An xterm upgrade requires rerunning and reviewing the semantic projection; terminal-specific behavior still needs the PTY smoke.
|
||||
- Goldens deliberately encode wrapping and viewport behavior at fixed sizes. Intentional layout changes use keyless refresh, while model-journey changes use record mode and review both JSONL and terminal diffs.
|
||||
@@ -0,0 +1,70 @@
|
||||
# RFC: TUI 语义终端状态快照
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-18-tui-terminal-state-snapshots.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
TUI 是有状态的渲染器。用户最终看到的结果取决于 ANSI 解析、差分帧、换行、回滚缓冲、视口位置、终端宽度、焦点、光标状态,以及各工具的呈现意图。收集 `Terminal.write()` 片段的单元测试可以验证事件处理,却无法验证终端最终显示的画面。同一画面也可能由不同的写入片段产生,因此固定这些片段会制造误报。
|
||||
|
||||
组件行快照止于 ANSI 进入终端之前,无法覆盖光标移动、清屏、样式、浮层组合和重排。栅格截图会带入与 TUI 契约无关的字体和平台渲染噪声。直接追加看似合理的会话事件来构造完整流程还存在另一处盲区:这种测试只能证明渲染器接受这些数据形态,无法证明生产环境的 agent loop(智能体循环)和工具实现会生成这些事件。
|
||||
|
||||
因此,TUI 既需要确定、便于评审的终端状态表示,也需要通过已录制模型流程执行真实下游组件,并保留一项范围更小、覆盖真实进程与 PTY 边界的测试。
|
||||
|
||||
## 决策
|
||||
|
||||
TUI 覆盖分为四个互补层次:
|
||||
|
||||
1. `packages/ui/tui/tests/tui.spec.ts` 直接测试事件映射、输入路由、资源释放和错误行为。
|
||||
2. `packages/ui/tui/tests/tui.snapshot.ts` 将生产 TUI 挂载到无界面终端模拟器,覆盖完整会话日志无法保留的瞬态:进行中的流式输出、待完成工具调用、浮层、展开状态、压缩重排、错误和关闭过程。
|
||||
3. `examples/tui-agent/tests/tui.snapshot.ts` 通过生产 agent loop 和真实工具回放已提交的 JSONL 会话日志,再比较生成的语义终端状态。
|
||||
4. `examples/tui-agent/tests/tui-keyless-smoke.e2e.ts` 在 PTY 中启动真实 Loader 组合,驱动一段经过流式输出和 `ask_user_question` 的脚本化会话,并验证启动、输入、退出、失败报告和终端恢复。
|
||||
|
||||
可运行 TUI 在 `examples/tui-agent` 中拥有独立叶节点,与 readline `coding-agent` 和 `acp-agent` 叶节点并列。它通过带断言的 include patch 复用 coding agent 的后端与工具组合,只把共享终端应用固定为 `ui.mode: tui`;TUI 快照和 PTY 测试也归属这个叶节点。
|
||||
|
||||
### 已录制会话回放
|
||||
|
||||
每个示例级场景目录都包含 `session.jsonl`、可选的子会话日志 `session.<n>.jsonl`,以及 `terminal.golden.txt`。主日志提供用户来源的 `user/message` 提示词和已录制的 `assistant/chunk` 序列。`dsh-llm-replay` 为每个会话派生一份模型调用脚本,并将子日志绑定到新建的子会话;这是测试中唯一的 mock 边界。agent loop、bash 与文件系统实现、Code Mode worker、subagent 提供方、工作流 worker、Cordis 工具、呈现器和 TUI 都使用生产实现。
|
||||
|
||||
如果工具调用顺序不符、预期事件数量不足、工具结果报错、轮次以错误结束、工作流生命周期不完整,或者实时子会话数量与 fixture(测试前置数据)集合不一致,测试都会失败。即使终端金标表面正确,这些断言也能阻止失败或被绕过的生产路径混入结果。
|
||||
|
||||
真实模型 fixture 通过 `DSH_SNAPSHOT=record` 更新;录制模式会重写其主会话与子会话 JSONL 日志以及终端金标。确定性的 Cordis 工具链保留一份人工编写的完整 JSONL 脚本,因为要求真实模型稳定经过五个指定工具边界和两个子会话并不是可靠的录制契约。`DSH_SNAPSHOT=refresh` 会无密钥回放所有已提交脚本,并且只重写派生的终端金标。普通回放只比较而不写入,未知模式值会快速失败。
|
||||
|
||||
### 语义终端投影
|
||||
|
||||
包内的 `HeadlessTerminal` 实现与进程终端相同的 pi-tui `Terminal` 接口,并把每次 ANSI 写入交给固定版本的 `@xterm/headless` 解析器。读取状态前,快照代码会等待同步帧稳定,因此每个检查点表示已经完成的画面,而不是依赖计时的写入前缀。
|
||||
|
||||
每份金标把终端尺寸、活动缓冲区和视口坐标、生命周期与光标状态、各行、换行标记以及非默认样式区间投影为文本。滚动内容较多的卡片捕获已使用缓冲区;浮层捕获可见视口。文本和样式相互分离,评审人无需解码 ANSI 字节即可区分内容变化与呈现变化。
|
||||
|
||||
每个检查点还会对完整终端状态强制执行主题无关性:禁止 RGB 颜色、禁止 ANSI 0–15 以外的调色板项,也禁止显式背景色。选择行使用终端默认色进行反显,因此仍然有效。两套测试都拥有封闭清单,会拒绝缺失的场景、缺失的检查点和遗留金标文件。
|
||||
|
||||
### 必需场景矩阵
|
||||
|
||||
| 层次 | 场景 | 固定的契约 |
|
||||
|---|---|---|
|
||||
| 已录制流程 | 多轮会话 | 已录制的推理与文本分片、两轮输入、保留历史、token 总量和空闲编辑器状态 |
|
||||
| 已录制流程 | Todo 计划 | 真实 `todo_write` 执行、结果卡片和持久计划渲染 |
|
||||
| 已录制流程 | Bash 终端卡片 | 真实本地执行器输出、说明、退出状态和已完成终端卡片 |
|
||||
| 已录制流程 | 并行文件读取 | 同一条 assistant 消息中的两次调用、真实文件内容、顺序和两个独立完成卡片 |
|
||||
| 已录制流程 | Code Mode | 真实 `run_code` worker 执行、两条 `tool/code-dispatch` 事件、捕获的程序输出和已完成卡片 |
|
||||
| 已录制流程 | 动态工作流 | 真实工作流 worker、阶段生命周期、回放的子会话、结构化返回值和已完成卡片 |
|
||||
| 已录制流程 | Cordis 动态工具链 | 真实挂载、Code Mode 检查、直接 subagent、工作流子会话、卸载和全部生产呈现器 |
|
||||
| 瞬态 | 流式输出与待完成高级调用 | 进行中的推理和文本,以及完整日志中不会保留的待完成 Code Mode、工作流和 Cordis 卡片 |
|
||||
| 瞬态 | 卡片、交互、布局、失败和关闭 | 折叠与展开的卡片族、问题校验、压缩替换、尺寸重排、帮助与错误、光标恢复和终端停止 |
|
||||
|
||||
## 曾考虑的替代方案
|
||||
|
||||
- **快照原始终端写入**:不予采纳,因为差分渲染可能在画面不变时改变写入边界,而且光标与清屏序列难以评审。
|
||||
- **快照进入终端输出之前的组件渲染行**:不予采纳,因为它无法测试 ANSI 解析、光标移动、浮层、视口行为,也无法测试独立组件在同一帧中的相互作用。
|
||||
- **通过追加会话事件构造所有完整流程**:不予采纳,因为人工编写的事件序列可能与 agent loop、工具执行、子会话绑定或 worker 行为发生偏差,但呈现测试仍然保持绿色。直接构造事件只用于渲染器瞬态。
|
||||
- **复用 ACP stdout 金标作为 TUI 判定依据**:不予采纳,因为已录制模型流程与传输方式无关,其呈现方式却并非如此。TUI 场景使用同一套 JSONL 回放词汇,但拥有独立的终端金标。
|
||||
- **提交栅格截图**:不予采纳,因为字体、字形度量、抗锯齿和宿主终端主题会使结果依赖平台,也会增加语义样式变更的评审难度。
|
||||
- **只使用 PTY 端到端测试**:不予采纳,因为原始 PTY 输出是一系列历史绘制操作,而不是可查询的最终状态。PTY 测试保留真实 Loader、输入与清理边界,模拟器负责广泛的状态覆盖。
|
||||
|
||||
## 后果
|
||||
|
||||
- 当真实 Code Mode、工作流、subagent、文件系统、bash 或 Cordis 路径损坏时,已完成高级快照会失败,不会继续接受伪造的结果事件。
|
||||
- TUI 视觉回归会产生便于阅读的单元格和样式 diff,而 JSONL fixture 会保留触发生产路径的确切模型分片。
|
||||
- 模拟器使用 xterm 的拟议缓冲区 API。升级 xterm 时必须重新运行并评审语义投影;终端特有行为仍需由 PTY 冒烟测试覆盖。
|
||||
- 金标有意固定指定尺寸下的换行与视口行为。预期布局变更使用无密钥刷新;模型流程变更使用录制模式,并同时评审 JSONL 与终端 diff。
|
||||
@@ -1,35 +0,0 @@
|
||||
# RFC: Retire the standalone subagent mock package
|
||||
|
||||
Status: proposed
|
||||
|
||||
English | [中文](2026-07-19-retire-subagent-mock-package.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`@deepseek-ai/dsh-subagent-mock` is a configurable test double packaged as a workspace plugin. Its only external consumers are the `tool-subagent` unit suite and the tool-catalog generator. No runtime package, example, snapshot configuration, or real provider loads it.
|
||||
|
||||
That narrow fixture carries a manifest, exports, peer and development dependencies, project references, package README obligations, Loader composition tests, module-graph membership, and documentation-gate exceptions. The tool-catalog generator mounts it only to make the real subagent tool register its schema; it never executes a child.
|
||||
|
||||
## Proposal
|
||||
|
||||
Delete `packages/support/subagent-mock`. Move the scripted provider behavior actually used by `tool-subagent` into a package-local test fixture while continuing to exercise the real `SubagentService`, provider registry, and tool implementation.
|
||||
|
||||
Have the tool-catalog generator register the minimal provider descriptor required before mounting `ToolSubagent`. Remove the package references, manifest dependency, graph node, README allowlists, and mock-specific Loader tests.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep a reusable mock package for future tests.** Reuse has not materialized outside one test file and one generator. A future second consumer can extract a fixture once its shared contract is known; packaging all configurable reply, cancellation, result, and Loader behavior today makes test infrastructure look like a supported backend.
|
||||
|
||||
**Generate the subagent schema without mounting the real tool.** Hand-constructing or importing the schema would weaken the catalog's check that the production registry and tool composition expose the documented shape. The generator should keep mounting the real service and tool with only the child boundary replaced.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- `packages/support/subagent-mock` and every workspace, graph, dependency, and documentation entry for it are removed.
|
||||
- `tool-subagent` tests retain every scripted reply, structured-result, cancellation, foreground/background, and task-integration case they currently exercise through the real service and tool.
|
||||
- Tool-catalog generation mounts the production subagent registry and tool with a minimal local provider and produces a byte-identical catalog.
|
||||
- No runtime or example package gains a dependency on test-only fixtures.
|
||||
- Focused subagent tests, catalog and graph generation, module-graph verification, build, hygiene, and the full pre-push suite pass.
|
||||
|
||||
## Risks
|
||||
|
||||
Relocating the fixture could accidentally replace too much production composition with a stub. The local fixture must implement only the nondeterministic child boundary; capability checks, lifecycle, task handling, and tool output remain under production code. Mock Loader and HMR coverage can disappear because no deployed composition consumes the package afterward.
|
||||
@@ -1,35 +0,0 @@
|
||||
# RFC: 撤销独立的 subagent mock 包
|
||||
|
||||
Status: proposed
|
||||
|
||||
[English](2026-07-19-retire-subagent-mock-package.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`@deepseek-ai/dsh-subagent-mock` 是一个以工作区插件形式发布的可配置测试替身。它仅有两个外部消费方:`tool-subagent` 单元测试和工具目录生成器。运行时包、示例、快照配置和真实提供方都不会加载它。
|
||||
|
||||
这个用途狭窄的 fixture(测试前置数据)需要维护 manifest(元数据清单)、导出、对等依赖(peer dependency)与开发依赖、项目引用、包(package)README 契约、Loader 组合测试、模块图成员关系以及文档门禁例外。工具目录生成器挂载它,只是为了让真实 subagent 工具注册 schema;生成器从不执行子 agent。
|
||||
|
||||
## 提案
|
||||
|
||||
删除 `packages/support/subagent-mock`。把 `tool-subagent` 实际使用的脚本化提供方行为移入该包的本地测试 fixture,同时继续测试真实的 `SubagentService`、提供方注册表和工具实现。
|
||||
|
||||
工具目录生成器在挂载 `ToolSubagent` 前,只注册所需的最小提供方描述。删除该包的项目引用、manifest 依赖、图节点、README 允许列表和 mock 专用 Loader 测试。
|
||||
|
||||
## 备选方案
|
||||
|
||||
**为未来测试保留可复用 mock 包。** 除一个测试文件和一个生成器外,复用需求并未出现。未来产生第二个消费方时,可以在共享契约明确后再提取 fixture;当前把所有可配置回复、取消、结果与 Loader 行为打包,会使测试基础设施看起来像受支持的后端。
|
||||
|
||||
**不挂载真实工具,直接生成 subagent schema。** 手工构造或直接导入 schema,会削弱目录生成器对生产注册表与工具组合是否公开文档结构的校验。生成器应继续挂载真实服务与工具,只替换不确定的子 agent 边界。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 删除 `packages/support/subagent-mock`,并移除其全部工作区、图、依赖和文档条目。
|
||||
- `tool-subagent` 测试保留当前通过真实服务与工具覆盖的全部脚本化回复、结构化结果、取消、前台与后台运行以及任务集成用例。
|
||||
- 工具目录生成器使用最小本地提供方挂载生产 subagent 注册表与工具,并生成字节级一致的目录。
|
||||
- 运行时包与示例包都不会新增对测试专用 fixture 的依赖。
|
||||
- 聚焦 subagent 测试、目录与图生成、模块图校验、构建、hygiene 和完整 pre-push 门禁全部通过。
|
||||
|
||||
## 风险
|
||||
|
||||
迁移 fixture 时,可能会误将过多生产组合替换成测试替身。本地 fixture 只能实现不确定的 subagent 边界;能力检查、生命周期、任务处理与工具输出仍由生产代码负责。由于之后不再有部署组合消费该包,可以删除 mock 的 Loader 与 HMR 覆盖。
|
||||
@@ -1,43 +0,0 @@
|
||||
# RFC: Use one surface manager per session
|
||||
|
||||
Status: proposed
|
||||
|
||||
English | [中文](2026-07-19-use-one-session-surface-manager.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`Session` maintains two `SurfaceManager` instances over the same append-only event log. `surfaceValidator` eagerly validates seed and append candidates, while the lazy `_surface` independently folds committed events for `session.surface`, derived messages, compaction, and workspace context. Once the public surface is read, every later event advances duplicate node and replacement-generation state.
|
||||
|
||||
The [session surface decision](../../implemented/architecture/2026-06-18-session-surface.md) calls for one ordered surface and one representation to validate. The second manager does not create an independent authority or protect a different failure boundary; it repeats the canonical fold and gives the two views a state-drift opportunity.
|
||||
|
||||
## Proposal
|
||||
|
||||
Keep one `SurfaceManager` per `Session`. Seed and append acceptance continue to call `validateNext()` before committing an event, and the public surface view reads `nodes` and `replaceGeneration` from that same manager.
|
||||
|
||||
Expose only the readonly surface contract from `Session.surface`; candidate validation remains owned by `Session`. Retain `foldSurface()` as the detached full-log replay function used by offline validation and reconstruction.
|
||||
|
||||
## Implementation plan
|
||||
|
||||
1. In `packages/core/session/src/surface.ts`, export a structural `SessionSurface` contract containing only readonly `nodes` and `replaceGeneration`, and make `SurfaceManager` implement it. Re-export that type from `packages/core/session/src/index.ts` so `Session.surface` no longer exposes `validateNext()` through its declaration.
|
||||
2. In `Session`, replace `surfaceValidator` and lazy `_surface` with one eagerly constructed `surfaceManager`. Route seed and append validation through that manager and return it from `get surface(): SessionSurface`; `deriveMessages()` will read the same nodes and generation. `validateNext()` may synchronize already committed log entries, but it must only plan the uncommitted candidate. The candidate reaches manager state after `log.push()` and the next delta synchronization, so rejection by surface validation or pre-commit `internal/dispatch` cannot leave phantom state.
|
||||
3. Keep `foldSurface()` and the transition functions in `surface.ts` unchanged. Compile and exercise the direct consumers in `packages/compact/compact/src/tool-pairing.ts`, `packages/compact/compact-basic/src/region.ts`, and `packages/context/workspace-context/src/state.ts`; they continue to consume only nodes and replacement generation.
|
||||
4. Extend `packages/core/session/tests/surface.spec.ts` to read the public view before an invalid candidate, prove that nodes and generation remain at the accepted prefix after rejection, append a later valid event, and compare every resulting prefix with `foldSurface()`. Add an `internal/dispatch` veto case and a type-level `SessionSurface` assertion in `session.spec.ts`, while retaining the seeded replay, delta-growth, replacement, generation, and derived-cache cases.
|
||||
5. Run the request-reconstruction, compaction tool-pairing, compaction range, and workspace-context regression suites that consume the surface. In the implementation PR, update `packages/core/session/README.md`, `docs/core-data-structures/session.md`, the implemented session-surface RFC and its Chinese counterpart, the translation record, `scripts/type-equiv.manifest.json`, and the generated RFC index before moving this RFC pair to `implemented/`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep acceptance and projection state separate.** Separate instances appear to isolate public reads from validation, but ordinary callers already receive borrowed surface state and cannot mutate it through the declared readonly contract. A cast that mutates the returned node array already corrupts derived history; duplicating the manager is not a sound runtime trust boundary.
|
||||
|
||||
**Recompute the public surface from the full log on every access.** This removes cached duplicate state but gives up incremental derivation and makes repeated request construction scale with complete session history.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- A live `Session` owns exactly one incremental `SurfaceManager`.
|
||||
- Seed and append candidates are validated before publication with no partial surface mutation on rejection.
|
||||
- `session.surface`, derived messages, compaction, and workspace context observe the same nodes and replacement generation as the acceptance path.
|
||||
- `foldSurface()` remains available for detached replay and agrees with the live manager for every accepted prefix.
|
||||
- Session surface, seed, request reconstruction, compaction tool-pairing, and workspace-context tests pass.
|
||||
|
||||
## Risks
|
||||
|
||||
Sharing one manager makes the readonly borrowed-state contract more important because a hostile cast could corrupt both validation and projection state. The implementation should return a narrowed view and keep mutation methods inaccessible through `Session.surface`; JavaScript callers that deliberately bypass the type contract remain outside the supported same-process boundary.
|
||||
@@ -1,43 +0,0 @@
|
||||
# RFC: 每个会话只使用一个表层管理器
|
||||
|
||||
Status: proposed
|
||||
|
||||
[English](2026-07-19-use-one-session-surface-manager.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`Session` 针对同一份仅追加事件日志维护两个 `SurfaceManager` 实例。`surfaceValidator` 主动校验种子事件与追加候选事件,延迟创建的 `_surface` 则独立折叠已提交事件,供 `session.surface`、派生消息、压缩(compaction)和工作区上下文使用。一旦读取公共表层,之后的每个事件都会推进两份重复的节点状态与替换代数状态。
|
||||
|
||||
[会话表层决策](../../implemented/architecture/2026-06-18-session-surface.md)要求系统只保留一个有序表层,并使用一种表示完成校验。第二个管理器既不形成独立真源,也不保护不同的失败边界;它只会重复规范折叠,并使两个视图可能出现状态偏差。
|
||||
|
||||
## 提案
|
||||
|
||||
每个 `Session` 只保留一个 `SurfaceManager`。种子事件与追加事件的接纳流程仍在提交事件之前调用 `validateNext()`,公共表层视图则从同一个管理器读取 `nodes` 与 `replaceGeneration`。
|
||||
|
||||
`Session.surface` 只公开只读表层契约,候选事件校验仍由 `Session` 负责。保留 `foldSurface()`,用于离线校验与重建时执行分离的完整日志回放。
|
||||
|
||||
## 实施计划
|
||||
|
||||
1. 在 `packages/core/session/src/surface.ts` 中,导出结构化的 `SessionSurface` 契约,只包含只读的 `nodes` 与 `replaceGeneration`,并让 `SurfaceManager` 实现该契约。从 `packages/core/session/src/index.ts` 重新导出这个类型,使 `Session.surface` 的声明不再暴露 `validateNext()`。
|
||||
2. 在 `Session` 中,用一个主动创建的 `surfaceManager` 替换 `surfaceValidator` 与延迟创建的 `_surface`。种子事件与追加事件都通过该管理器校验,`get surface(): SessionSurface` 返回同一个对象,`deriveMessages()` 也读取同一份节点与代数。`validateNext()` 可以同步已提交的日志事件,但对尚未提交的候选事件只能制定变更计划。候选事件在 `log.push()` 之后、下一次增量同步时才进入管理器状态,因此表层校验拒绝或提交前 `internal/dispatch` 否决都不会留下虚假状态。
|
||||
3. 保持 `foldSurface()` 与 `surface.ts` 中的状态转换函数不变。编译并验证 `packages/compact/compact/src/tool-pairing.ts`、`packages/compact/compact-basic/src/region.ts` 和 `packages/context/workspace-context/src/state.ts` 中的直接消费方;它们仍然只读取节点与替换代数。
|
||||
4. 扩展 `packages/core/session/tests/surface.spec.ts`:先读取公共视图,再提交无效候选事件,证明拒绝后节点与代数仍停留在已接纳前缀;随后追加有效事件,并把每个结果前缀与 `foldSurface()` 比较。在 `session.spec.ts` 中新增 `internal/dispatch` 否决用例与类型层面的 `SessionSurface` 断言,同时保留种子回放、增量增长、替换、代数和派生缓存用例。
|
||||
5. 运行消费表层的请求重建、压缩工具配对、压缩范围与工作区上下文回归套件。在实现 PR 中,先更新 `packages/core/session/README.md`、`docs/core-data-structures/session.md`、已实现会话表层 RFC 及其中文对应文件、翻译记录、`scripts/type-equiv.manifest.json` 和生成的 RFC 索引,再把本 RFC 双语文件移入 `implemented/`。
|
||||
|
||||
## 备选方案
|
||||
|
||||
**继续分离接纳状态与投影视图。** 两个独立实例看似能够隔离公共读取和校验,但普通调用方目前取得的就是借用的表层状态,无法通过声明的只读契约修改它。通过类型断言修改返回的节点数组,本就会破坏派生历史;复制管理器并不能构成可靠的运行时信任边界。
|
||||
|
||||
**每次读取都根据完整日志重新计算公共表层。** 该方案不再缓存重复状态,但会放弃增量派生,使每次请求构造都随完整会话历史增长。
|
||||
|
||||
## 验收标准
|
||||
|
||||
- 每个活跃 `Session` 只拥有一个增量 `SurfaceManager`。
|
||||
- 种子事件与追加候选事件都在发布前完成校验,拒绝事件时不会留下只修改一半的表层状态。
|
||||
- `session.surface`、派生消息、压缩和工作区上下文观察到的节点与替换代数,和接纳路径使用的状态完全一致。
|
||||
- `foldSurface()` 仍可用于分离回放,并且对任意已接纳前缀都与活跃管理器一致。
|
||||
- 会话表层、种子、请求重建、压缩工具配对和工作区上下文测试全部通过。
|
||||
|
||||
## 风险
|
||||
|
||||
共享一个管理器会提高只读借用状态契约的重要性,因为恶意类型断言可能同时破坏校验状态和投影视图。实现应返回收窄后的视图,避免通过 `Session.surface` 暴露修改方法;刻意绕过类型契约的 JavaScript 调用方不属于受支持的同进程边界。
|
||||
+3
-3
@@ -1,19 +1,19 @@
|
||||
# RFC: Prune the unimplemented subagent seam vocabulary
|
||||
|
||||
Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below stands as the record of what is currently unimplemented.
|
||||
Status: rejected — the deferred capability vocabulary (`outputSchema`/`structured`, `toolFilter`, `sendMessage`/`resume`) is intentionally reserved surface: the seam advertises the full intended contract ahead of its implementations by design, so providers and consumers grow into a stable shape rather than re-negotiating it per capability. The consumer-evidence analysis below records the decision-time state.
|
||||
|
||||
## Problem
|
||||
|
||||
The [subagent seam](../../implemented/feature/2026-06-21-subagent-capability-seam.md) shipped a two-tier capability design: start-time capability flags checked by the service, and optional runtime methods on `SubagentRun`. Three start-time features and both optional runtime methods have zero implementations and zero callers:
|
||||
|
||||
- **`outputSchema`/`structured` and `toolFilter`** (`SubagentCapabilities`, `SubagentStartRequest`, `SubagentResult` in `packages/subagent/subagent/src/types.ts`): every real provider declares `outputSchema: false, toolFilter: false` (`packages/subagent/subagent-spawn/src/index.ts`, `packages/subagent/subagent-fork/src/index.ts`, `packages/subagent/subagent-acp/src/index.ts`); the sole production `ctx.subagents.start` caller (`packages/subagent/tool-subagent/src/index.ts`) builds `{ prompt, parent, signal?, agentOptions? }` and structurally cannot set either; `structured` is produced only by the test mock (`packages/support/subagent-mock`) for its own spec. The service's capability check carries two assert rows whose only exercisers are the rejection tests.
|
||||
- **`outputSchema`/`structured` and `toolFilter`** (`SubagentCapabilities`, `SubagentStartRequest`, `SubagentResult` in `packages/subagent/subagent/src/types.ts`): at the decision point, every real provider declared `outputSchema: false, toolFilter: false` (`packages/subagent/subagent-spawn/src/index.ts`, `packages/subagent/subagent-fork/src/index.ts`, `packages/subagent/subagent-acp/src/index.ts`); the sole production `ctx.subagents.start` caller (`packages/subagent/tool-subagent/src/index.ts`) built `{ prompt, parent, signal?, agentOptions? }` and structurally could not set either; `structured` appeared only in the scripted test fixture. The service's capability check carried two assert rows whose only exercisers were the rejection tests.
|
||||
- **`SubagentRun.sendMessage` / `SubagentRun.resume`** (same file): implemented by NO provider — not even the mock; the spawn spec asserts their *absence*.
|
||||
|
||||
The only reason `dsh-subagent` depends on `dsh-tools` at all is `outputSchema`'s `SchemaSpec` type. Three subsequent subagent workstreams (per-session snapshot replay, the fork seed boundary, the ACP backend) landed around this surface without growing a single consumer.
|
||||
|
||||
## Proposal
|
||||
|
||||
Remove `outputSchema`/`structured`, `toolFilter`, `sendMessage`, and `resume` from the seam; shrink `SubagentCapabilities` to `{ depthLimit }`; drop the two capability-assert rows, the all-false flags on the three providers, the mock's structured branch and its `capabilities`/`structured` config knobs, and the tests that exist to pin the removed surface (the two rejection rows, the spawn absence test, the mock structured specs). Drop the `dsh-tools` peer/dev dependency from `packages/subagent/subagent/package.json`. Update the [subagent.md](../../../core-data-structures/subagent.md) pastes and the type-equiv manifest, and the README rows in `packages/subagent/subagent`, `packages/subagent/subagent-spawn`, `packages/subagent/subagent-fork`, and `packages/support/subagent-mock`. The implementing PR amends the seam RFC's capability catalog per [implemented/AGENTS.md](../../implemented/AGENTS.md).
|
||||
Remove `outputSchema`/`structured`, `toolFilter`, `sendMessage`, and `resume` from the seam; shrink `SubagentCapabilities` to `{ depthLimit }`; drop the two capability-assert rows, the all-false flags on the three providers, the scripted fixture's structured branch and capability knobs, and the tests that exist to pin the removed surface. Drop the `dsh-tools` peer/dev dependency from `packages/subagent/subagent/package.json`. Update the [subagent.md](../../../core-data-structures/subagent.md) pastes and the type-equiv manifest, plus the affected provider READMEs. The implementing PR amends the seam RFC's capability catalog per [implemented/AGENTS.md](../../implemented/AGENTS.md).
|
||||
|
||||
**Keep** `depthLimit`/`maxDepth` and capability checks. The in-process backend enforces the limit, although the shipping tool does not yet set it. Recursion is a known seam risk, so the appropriate follow-up is to supply a tool default rather than delete working enforcement.
|
||||
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ How this repo tests, tier by tier, and the rules that keep a green suite meaning
|
||||
- **Unit** (`pnpm run test`): vitest over `packages|examples/*/tests/**/*.spec.ts`, colocated with what they test. Every registry gets an HMR-safety test (dispose the contributing fiber, assert cleanup). Prefer edge cases, error paths, event ordering, concurrency races, and permanent contract regressions (see `packages/core/agent-loop/tests/contract-regressions.spec.ts`).
|
||||
- **Coverage gate** (`pnpm run test:coverage`): the gating run, per-file 100% on `packages/*/*/src`. An uncovered line is often dead code the gate is correctly flagging for deletion, not a missing test to bolt on. Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped.
|
||||
- **Real-API e2e** (`pnpm run test:e2e`): with-key tests against live provider APIs — the DeepSeek model plus provider-specific smokes that gate on their own keys (`EXA_API_KEY`, `PERPLEXITY_API_KEY`, …); each suite self-skips without its key so keyless CI stays green ([real-API e2e RFC](rfc/implemented/testing/2026-06-19-real-api-e2e-ci.md)).
|
||||
- **Snapshot** (`pnpm run test:snapshot`): boots the real example subprocess, replays a recorded session keyless, diffs normalized stdout + the re-persisted log against committed goldens ([snapshot RFC](rfc/implemented/testing/2026-06-19-acp-snapshot-tests.md)). Use `pnpm run test:snapshot:record` when the model transcript should change; use `pnpm run test:snapshot:refresh` when the committed transcript is still the right mock LLM input and replay goldens need keyless rewrite. Review the golden diff. System-prompt/tool-schema content is pinned by ONE scenario (`text-turn`) and tokenized in every other fixture, so a prompt or schema edit churns one committed line ([pinned-header RFC](rfc/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
|
||||
- **Snapshot** (`pnpm run test:snapshot`): transport-specific keyless goldens cover external presentation. ACP suites boot the real example subprocess, replay a recorded session, and diff normalized stdout plus the re-persisted log ([ACP snapshot RFC](rfc/implemented/testing/2026-06-19-acp-snapshot-tests.md)). TUI completed journeys replay recorded primary/child JSONL through the real agent loop and tools before projecting ANSI into semantic terminal-state goldens; package-local snapshots retain transient renderer states, and a real PTY conversation covers the process boundary ([TUI snapshot RFC](rfc/implemented/testing/2026-07-18-tui-terminal-state-snapshots.md)). Use `pnpm run test:snapshot:record` when a model transcript must change and `pnpm run test:snapshot:refresh` when committed replay input remains correct; review every JSONL and golden diff. System-prompt/tool-schema content is pinned by ONE ACP scenario (`text-turn`) and tokenized in every other fixture, so a prompt or schema edit churns one committed line ([pinned-header RFC](rfc/implemented/testing/2026-07-06-pin-request-header-content-in-one-scenario.md)).
|
||||
|
||||
## The with-key policy: inference is cheap here
|
||||
|
||||
@@ -35,4 +35,4 @@ An e2e assertion re-runs the command or re-reads the file externally; a keyword
|
||||
|
||||
## When a snapshot test is required
|
||||
|
||||
Any change affecting the editor-facing transcript or end-to-end agent UX — the ACP bridge, the loop's observable output, tool presentation — adds or updates a scenario in the owning example's snapshot suite (`examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory; `examples/acp-agent` is the primary suite), or states in the PR why none applies. New capability seams, lifecycle shapes, or transcript surfaces name their coverage at every tier at plan time and verify the harness can express it — a harness gap is scheduled work, not a mid-build surprise.
|
||||
Any change affecting an editor-facing transcript or end-to-end agent UX adds or updates a scenario in the owning snapshot suite, or states in the PR why none applies. ACP surfaces use `examples/<name>/tests/snapshots/`, a scenario table over the [`dsh-acp-snapshot`](../packages/support/acp-snapshot/README.md) suite factory (`examples/acp-agent` is primary). Completed interactive-terminal journeys use JSONL-driven scenarios under `examples/tui-agent/tests/snapshots/`; transient presentation uses the package-local semantic matrix, with a PTY case when input, Loader selection, or terminal teardown changes. New capability seams, lifecycle shapes, or transcript surfaces name their coverage at every tier at plan time and verify the harness can express it — a harness gap is scheduled work, not a mid-build surprise.
|
||||
|
||||
+7
-1
@@ -15,12 +15,18 @@ Run with: `pnpm run demo:echo`. When prompted, type "echo <something>" to trigge
|
||||
|
||||
## coding-agent
|
||||
|
||||
A REPL agent demo: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the same `@deepseek-ai/dsh-stdio-demo` app. The UI is a terminal readline REPL.
|
||||
A coding-agent REPL: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite, `subagent` delegation, and the `todo_write` task tracker on the `@deepseek-ai/dsh-stdio-demo` app's readline front door.
|
||||
|
||||
Run with: `pnpm run demo:repl` (needs `DEEPSEEK_API_KEY` in the environment or a gitignored repo-root `.env`). See [coding-agent/README.md](coding-agent/README.md) for details.
|
||||
|
||||
Run the Code Mode overlay with `pnpm run demo:code-mode`, or pass `acp` for the ACP example. See the [Code Mode example](coding-agent/README.md#code-mode) for its composition and a sample task.
|
||||
|
||||
## tui-agent
|
||||
|
||||
The full-screen terminal sibling of `coding-agent`: it reuses the same coding backends and tools while forcing the shared terminal app to `dsh-tui`. It is the home of TUI PTY and snapshot scenarios.
|
||||
|
||||
Run with: `pnpm run demo:tui` (needs `DEEPSEEK_API_KEY`). See [tui-agent/README.md](tui-agent/README.md) for controls and composition.
|
||||
|
||||
## cordis-agent
|
||||
|
||||
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 live cordis runtime it runs inside, mount model-written plugins into it (an event listener, a brand-new tool for itself, or a service another mount injects), and dispose them again — all dynamic mounts grouped under one `cordis-dynamic` fiber subtree. The `ctx.fs`/`ctx.web` services ride along provider-only, as the capabilities those plugins build on.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# coding-agent
|
||||
|
||||
The REPL agent demo wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + `todo_write` + stdio chat + JSONL persistence, loaded from `cordis.yml`. The UI is a terminal readline REPL.
|
||||
The coding-agent REPL wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem tools + the bash tool suite + subagent delegation + `todo_write` + readline chat + JSONL persistence, loaded from `cordis.yml`. The sibling [`tui-agent`](../tui-agent/README.md) fixes the same agent composition to the full-screen terminal front door.
|
||||
|
||||
## Run it
|
||||
|
||||
@@ -11,15 +11,9 @@ The REPL agent demo wiring: DeepSeek V4 + the `read`/`write`/`edit` filesystem t
|
||||
pnpm run demo:repl
|
||||
```
|
||||
|
||||
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` and track multi-step work with `todo_write` (a whole-list task tracker rendered as a checklist). Reasoning streams dimmed; tool calls/results render inline.
|
||||
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` and track multi-step work with `todo_write`.
|
||||
|
||||
```
|
||||
> fix the failing test in /path/to/project
|
||||
[main turn 1] (reasoning…)
|
||||
[tool call] bash({"command": "node --test", "workdir": "/path/to/project"})
|
||||
[tool result] … [exit code: 1]
|
||||
…
|
||||
```
|
||||
The REPL renders reasoning, tool calls/results, and the latest todo list as line-oriented output suitable for terminals and pipes. Use `pnpm run demo:tui` for the interactive Markdown/card interface.
|
||||
|
||||
### Resuming a prior session
|
||||
|
||||
@@ -29,7 +23,7 @@ Each run starts a fresh session by default (its event log lands under `./.sessio
|
||||
RESUME_SESSION_ID=<prior-session-id> pnpm run demo:repl
|
||||
```
|
||||
|
||||
The id is wired through `cordis.yml` (`resumeSessionId: !!js process.env.RESUME_SESSION_ID`); unset, the agent starts a new session. A missing/unreadable id is non-fatal — it logs a warning and starts no `main` agent.
|
||||
The id is wired through `cordis.yml` (`resumeSessionId: !!js process.env.RESUME_SESSION_ID`); unset, 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, while readline reports any dropped queued input and allows piped EOF to finish. Unset it or choose an existing session id.
|
||||
|
||||
## Code Mode
|
||||
|
||||
@@ -48,17 +42,17 @@ and watch the transcript: one `run_code` call, a program looping over tools, and
|
||||
|
||||
## 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 (console logger, JSONL persistence, readline UI, the pre-created `main` agent) live inside the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-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:
|
||||
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 selected terminal channel, the pre-created `main` agent) live inside the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-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 is Loader-only and needs `node --expose-internals`, which `demo:repl` passes |
|
||||
| `llm-deepseek` | real `LlmAdapter` via config (`!!js process.env.…` secrets); swap one line to `@deepseek-ai/dsh-llm-pi-ai` for the library-backed twin |
|
||||
| `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 |
|
||||
| `stdio-agent` (`@deepseek-ai/dsh-stdio-demo`) | the app bundle: the agent-spine demo + console logger + JSONL persistence + readline UI + a pre-created `main` agent. Its config carries the model, system prompt, `persistenceRoot` (`./.sessions`), and `resumeSessionId` — so persistence and the agent are configured here, not wired as separate leaf plugins |
|
||||
| `stdio-agent` (`@deepseek-ai/dsh-stdio-demo`) | the app bundle: the agent-spine demo + JSONL persistence + the configured terminal channel + a pre-created `main` agent. This leaf fixes `ui.mode` to `readline`; `tui-agent` owns the corresponding TUI leaf |
|
||||
| `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`) |
|
||||
| `tool-todo` | the model-facing `todo_write` tool; writes the whole task list to the session log and renders as a checklist in stdio |
|
||||
| `tool-todo` | the model-facing `todo_write` tool; writes the whole task list to the session log and renders as a persistent TUI plan or readline checklist |
|
||||
| `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`, key-gated)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
tools:
|
||||
mode: code
|
||||
welcome: 'code-mode agent ready. Give it a multi-tool task.'
|
||||
ui:
|
||||
mode: readline
|
||||
persona: |
|
||||
You are coding-agent, a coding assistant powered by the {{model}} model.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# Coding Agent App Composition
|
||||
|
||||
The coding REPL demo adds the real DeepSeek adapter, filesystem tools, todo_write, compaction, and both subagent transports on top of the stdio app package.
|
||||
The coding-agent demo adds the real DeepSeek adapter, filesystem tools, todo_write, compaction, and both subagent transports on top of the stdio app package.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -18,7 +18,7 @@ flowchart LR
|
||||
cfg --> plugin_coding_stdio_agent
|
||||
plugin_coding_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
|
||||
plugin_coding_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
plugin_coding_stdio_agent --> frontdoor_stdio["readline UI<br/>console logger<br/>pre-created main agent"]
|
||||
plugin_coding_stdio_agent --> frontdoor_stdio["@deepseek-ai/dsh-stdio<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"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# REPL agent with swappable DeepSeek and local-bash backends. `dsh-stdio-demo`
|
||||
# supplies the agent spine, workspace instructions, generic task controls,
|
||||
# logging, JSONL persistence, readline UI, and `main` agent.
|
||||
# Readline coding REPL with swappable DeepSeek and local-bash backends.
|
||||
# `dsh-stdio-demo` supplies the agent spine, workspace instructions, generic
|
||||
# task controls, JSONL persistence, the line-oriented front door, and `main`.
|
||||
# HMR remains a leaf because it requires Loader internals; `demo:repl` passes
|
||||
# `--expose-internals`. The app bin loads the gitignored root `.env`; this file
|
||||
# reads `DEEPSEEK_API_KEY` and optional `DEEPSEEK_BASE_URL` through `!!js`.
|
||||
@@ -37,6 +37,8 @@
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
welcome: 'agent REPL ready. Give it a coding task.'
|
||||
ui:
|
||||
mode: readline
|
||||
# Keep the persona to identity and behavior; tool plugins own tool guidance.
|
||||
# The loop resolves {{model}} from this agent's configuration.
|
||||
persona: |
|
||||
|
||||
@@ -24,7 +24,7 @@ flowchart LR
|
||||
cfg --> plugin_cordis_stdio_agent
|
||||
plugin_cordis_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
|
||||
plugin_cordis_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
plugin_cordis_stdio_agent --> frontdoor_stdio["readline UI<br/>console logger<br/>pre-created main agent"]
|
||||
plugin_cordis_stdio_agent --> frontdoor_stdio["dsh-tui (TTY) / dsh-stdio (pipes)<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"]
|
||||
|
||||
@@ -4,7 +4,7 @@ Runnable demo: stdin chat with a scripted mock model and an echo tool. The all-m
|
||||
|
||||
## What it shows
|
||||
|
||||
This example is just a leaf `cordis.yml`: it loads the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo) app (which bundles the whole [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) spine, the console logger, JSONL persistence, the readline UI, and a pre-created `main` agent), and swaps in two example-local backends plus `hmr`:
|
||||
This example is just a leaf `cordis.yml`: it loads the [`@deepseek-ai/dsh-stdio-demo`](../../packages/examples/stdio-demo) app (which bundles the whole [`@deepseek-ai/dsh-agent-spine-demo`](../../packages/examples/agent-spine-demo) spine, JSONL persistence, the TTY-selected `dsh-tui`/`dsh-stdio` front doors, and a pre-created `main` agent), and swaps in two example-local backends plus `hmr`:
|
||||
|
||||
- `mock-llm.ts` — a mock `LlmAdapter` that streams scripted responses and calls the `echo` tool when the user types "echo <something>". Registered with `ctx.llm.registerAdapter(['mock-echo'], …)`.
|
||||
- `echo-tool.ts` — a tool registered via `ctx.tools.register(defineTool(…))` with typed `execute` args; echoes text back uppercased.
|
||||
|
||||
@@ -22,7 +22,7 @@ flowchart LR
|
||||
cfg --> plugin_echo_stdio_agent
|
||||
plugin_echo_stdio_agent --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
|
||||
plugin_echo_stdio_agent --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
plugin_echo_stdio_agent --> frontdoor_stdio["readline UI<br/>console logger<br/>pre-created main agent"]
|
||||
plugin_echo_stdio_agent --> frontdoor_stdio["dsh-tui (TTY) / dsh-stdio (pipes)<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"]
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
|
||||
# The app pre-creates `main` on the mock model and supplies logging, persistence, and readline UI.
|
||||
# The app pre-creates `main` on the mock model and supplies persistence plus
|
||||
# TTY-selected `dsh-tui`/`dsh-stdio` front doors; readline mode also owns logging.
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-demo'
|
||||
config:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# tui-agent
|
||||
|
||||
The full-screen terminal counterpart to the [`coding-agent`](../coding-agent/README.md) readline REPL and [`acp-agent`](../acp-agent/README.md) server. It reuses the coding agent's backends and tool composition, then fixes the shared terminal app to the `dsh-tui` front door.
|
||||
|
||||
## Run it
|
||||
|
||||
```sh
|
||||
pnpm run demo:tui
|
||||
```
|
||||
|
||||
The command needs `DEEPSEEK_API_KEY` in the environment or the gitignored repository-root `.env`. Set `RESUME_SESSION_ID` to reopen a persisted conversation under `./.sessions`.
|
||||
|
||||
The TUI renders Markdown history, reasoning, tool-owned terminal/diff/generic cards, token totals, and the latest todo list. Enter submits or steers while the agent is running; Ctrl+O expands cards, Ctrl+R toggles reasoning, Escape cancels, and `/help` lists commands. `ask_user_question` opens a keyboard-driven overlay.
|
||||
|
||||
Run `pnpm run demo:code-mode tui` for the sibling Code Mode overlay.
|
||||
|
||||
## Composition
|
||||
|
||||
[`cordis.yml`](cordis.yml) includes the readline coding-agent leaf so the LLM, bash, filesystem, compaction, subagent, workflow, todo, timeout, and spill choices have one owner. Its asserted patch replaces only the terminal app config and forces `ui.mode: tui`; [`code-mode.cordis.yml`](code-mode.cordis.yml) applies the same front-door patch to the coding agent's Code Mode overlay.
|
||||
|
||||
## 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 terminal cell/style goldens. 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 RFC](../../docs/rfc/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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Code Mode keeps the TUI front door while reusing the coding-agent overlay's
|
||||
# worker runtime and one-tool registry composition.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ../coding-agent/code-mode.cordis.yml
|
||||
patches:
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-demo'
|
||||
config:
|
||||
provider: deepseek
|
||||
model: deepseek-v4-flash
|
||||
resumeSessionId: !!js process.env.RESUME_SESSION_ID
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
tools:
|
||||
mode: code
|
||||
welcome: 'TUI Code Mode ready. Give it a multi-tool task.'
|
||||
ui:
|
||||
mode: tui
|
||||
tui:
|
||||
showReasoning: true
|
||||
maxToolOutputLines: 12
|
||||
persona: |
|
||||
You are coding-agent, a coding assistant 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.
|
||||
@@ -0,0 +1,28 @@
|
||||
<!-- 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 reuses the coding-agent backend and tool composition while fixing the shared terminal app to the full-screen dsh-tui front door.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
cfg["examples/tui-agent<br/>cordis.yml"]
|
||||
plugin_tui_base["base<br/>@deepseek-ai/dsh-stdio-demo"]
|
||||
cfg --> plugin_tui_base
|
||||
plugin_tui_base --> bundle_agent_core["@deepseek-ai/dsh-agent-spine-demo"]
|
||||
plugin_tui_base --> bundle_jsonl["@deepseek-ai/dsh-session-persistence-jsonl"]
|
||||
plugin_tui_base --> frontdoor_stdio["@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 id | Package / module |
|
||||
| --- | --- |
|
||||
| `base` | `@deepseek-ai/dsh-stdio-demo` |
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Full-screen TUI front door over the same coding-agent composition used by the
|
||||
# readline REPL. The include keeps backends and optional tools aligned; the
|
||||
# patch owns only the terminal-specific app config.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ../coding-agent/cordis.yml
|
||||
patches:
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-demo'
|
||||
config:
|
||||
provider: deepseek
|
||||
model: deepseek-v4-flash
|
||||
resumeSessionId: !!js process.env.RESUME_SESSION_ID
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
welcome: 'TUI agent ready. Give it a coding task.'
|
||||
ui:
|
||||
mode: tui
|
||||
tui:
|
||||
showReasoning: true
|
||||
maxToolOutputLines: 12
|
||||
persona: |
|
||||
You are coding-agent, a coding assistant powered by the {{model}} model.
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and
|
||||
factual.
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "tui-agent-example",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"description": "Runnable demo: the coding agent through the full-screen terminal UI"
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { Context } from 'cordis'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } 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.'
|
||||
|
||||
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 two-step adapter for the real-PTY TUI conversation test. */
|
||||
class ScriptedTuiAdapter extends LlmAdapter {
|
||||
async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
const hasToolResult = options.messages.at(-1)?.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())
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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'
|
||||
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-local'
|
||||
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-demo'
|
||||
config:
|
||||
provider: tui-scripted
|
||||
model: tui-scripted-model
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
welcome: 'scripted TUI ready.'
|
||||
ui:
|
||||
mode: tui
|
||||
tui:
|
||||
showReasoning: true
|
||||
@@ -0,0 +1,98 @@
|
||||
{"type":"session","version":0,"id":"e128dda9-ed11-4868-8266-0ef90d03c3d6","createdAt":1783352050748,"cwd":"/tmp/acp-snap-cwd-mrFUuk"}
|
||||
{"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":"assistant/chunk","seq":5,"time":1783352051422,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783352051590,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783352051618,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783352051618,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783352051619,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783352051619,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" run"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783352051619,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783352051645,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" simple"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783352051675,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783352051675,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783352051675,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783352051676,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783352051676,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783352051703,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783352051704,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783352051704,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783352051704,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783352051704,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783352051790,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783352051791,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783352051820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783352051820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783352051820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"command"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783352051820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1783352051820,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783352051848,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1783352051848,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1783352051848,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" TER"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1783352051848,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"MIN"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1783352051877,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"AL"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1783352051877,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1783352051877,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783352051905,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783352051906,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783352051906,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783352051935,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783352051935,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783352051935,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783352051935,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"E"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"cho"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" TER"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"MIN"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"AL"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783352051967,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783352052041,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" verify"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783352052041,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" terminal"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783352052041,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":" access"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783352052054,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783352052054,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fkbBRJsUrGKd1pWVc4Gn8233","name":"bash","argumentsDelta":"}"}}}
|
||||
{"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":"assistant/chunk","seq":65,"time":1783352052702,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1783352052780,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" command"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1783352052809,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" ran"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1783352052838,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1783352052838,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1783352052838,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" output"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"TER"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"MIN"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"AL"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1783352052867,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1783352052895,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1783352052896,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" should"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1783352052924,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" now"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1783352052925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1783352052925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1783352052925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" just"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1783352052957,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,73 @@
|
||||
terminal 100x36 buffer=normal length=36 base=0 viewport=0
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=27 bufferRow=27
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: bash-terminal-card │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-36 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Use the bash tool to run exactly: echo TERMINAL_OK. Then reply with the single word DONE and stop."
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
10| <blank>
|
||||
11| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
12| " The user wants me to run a simple bash command and then reply with \"DONE\". "
|
||||
style 1-74 fg=bright-black italic
|
||||
13| <blank>
|
||||
14| "▌ "
|
||||
style 0-0 fg=green
|
||||
15| "▌ ✓ echo TERMINAL_OK "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-19 bold
|
||||
16| "▌ Echo TERMINAL_OK to verify terminal access "
|
||||
style 0-0 fg=green
|
||||
style 2-43 fg=bright-black
|
||||
17| "▌ TERMINAL_OK "
|
||||
style 0-0 fg=green
|
||||
18| "▌ [exit 0] "
|
||||
style 0-0 fg=green
|
||||
style 2-9 dim
|
||||
19| "▌ "
|
||||
style 0-0 fg=green
|
||||
20| <blank>
|
||||
21| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
22| " The command ran successfully and output \"TERMINAL_OK\". I should now reply with just \"DONE\". "
|
||||
style 1-91 fg=bright-black italic
|
||||
23| <blank>
|
||||
24| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
25| " DONE "
|
||||
26| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
27| " "
|
||||
style 1-1 inverse
|
||||
28| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
29| "/workspace/project ↑3.0k ↓115 idle reasoning:on tools:compact"
|
||||
style 0-58 dim
|
||||
style 67-99 dim
|
||||
30-35| <blank>
|
||||
@@ -0,0 +1,150 @@
|
||||
{"type":"session","version":0,"id":"94cd1ae4-e1d1-4ec8-9d27-50a1f849b6b3","createdAt":1783611771392,"cwd":"/var/folders/2c/psb0_fmx7hbgz558xjt_f0l00000gn/T/acp-snap-cwd-BteTVR"}
|
||||
{"type":"turn/start","seq":0,"time":1783611771394,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1783611771394,"data":{"content":[{"type":"text","text":"Using ONE run_code program: call the bash tool twice — exactly `echo CODE_ONE` then exactly `echo CODE_TWO` — and return the two outputs joined with a plus sign. Then reply with that joined string only and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":2,"time":1783611771396,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":3,"time":1783611771396,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1783611771869,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1783611771869,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783611771978,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783611772007,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783611772008,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783611772036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783611772036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" run"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783611772036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_code"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783611772036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" program"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783611772036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" that"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783611772089,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" calls"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783611772089,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" bash"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783611772089,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" twice"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783611772096,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783611772124,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783611772153,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" returns"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783611772183,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783611772183,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" two"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783611772211,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" outputs"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783611772212,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" joined"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783611772212,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783611772212,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783611772212,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" plus"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783611772212,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" sign"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783611772241,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1783611772241,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783611772241,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1783611772241,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" write"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1783611772270,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" this"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1783611772270,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1783611772361,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1783611772361,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1783611772361,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783611772362,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783611772390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"code"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783611772390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783611772390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783611772390,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783611772420,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"const"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783611772420,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" out"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783611772420,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"1"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783611772420,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" ="}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783611772421,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" await"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783611772421,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783611772449,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":".b"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783611772449,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"ash"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783611772449,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"({"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783611772449,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" command"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783611772478,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":":"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783611772479,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" \\\""}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783611772479,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1783611772479,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" CODE"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1783611772479,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"_"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1783611772508,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1783611772510,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\\\","}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1783611772510,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" description"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783611772510,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":":"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1783611772510,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" \\\""}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1783611772510,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"First"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1783611772538,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" echo"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1783611772538,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\\\""}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1783611772566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" });\\n"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1783611772567,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"const"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1783611772567,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" out"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1783611772567,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"2"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1783611772567,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" ="}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1783611772567,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" await"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1783611772597,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1783611772597,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":".b"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1783611772597,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"ash"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1783611772598,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"({"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1783611772598,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" command"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1783611772598,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":":"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1783611772625,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" \\\""}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1783611772625,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"echo"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1783611772625,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" CODE"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1783611772626,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"_T"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1783611772626,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1783611772626,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\\\","}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1783611772654,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" description"}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1783611772655,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":":"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1783611772655,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" \\\""}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1783611772655,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"Second"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1783611772655,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" echo"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1783611772655,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\\\""}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1783611772684,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" });\\n"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1783611772685,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"return"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":1783611772685,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" out"}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":1783611772713,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"1"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":1783611772714,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":".trim"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":1783611772714,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"()"}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1783611772714,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" +"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1783611772714,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" \\\"+"}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1783611772744,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\\\""}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1783611772744,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" +"}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1783611772745,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":" out"}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1783611772745,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"2"}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1783611772745,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":".trim"}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":1783611772745,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"();"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1783611772772,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1783611772773,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1783611772836,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants a single run_code program that calls bash twice, then returns the two outputs joined with a plus sign. Let me write this."}}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1783611772836,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","arguments":"{\"code\": \"const out1 = await tools.bash({ command: \\\"echo CODE_ONE\\\", description: \\\"First echo\\\" });\\nconst out2 = await tools.bash({ command: \\\"echo CODE_TWO\\\", description: \\\"Second echo\\\" });\\nreturn out1.trim() + \\\"+\\\" + out2.trim();\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1783611772836,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3009,"outputTokens":133,"cacheReadTokens":0,"reasoningTokens":29}}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1783611772836,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":109,"time":1783611772840,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants a single run_code program that calls bash twice, then returns the two outputs joined with a plus sign. Let me write this."},{"type":"tool-call","id":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","arguments":"{\"code\": \"const out1 = await tools.bash({ command: \\\"echo CODE_ONE\\\", description: \\\"First echo\\\" });\\nconst out2 = await tools.bash({ command: \\\"echo CODE_TWO\\\", description: \\\"Second echo\\\" });\\nreturn out1.trim() + \\\"+\\\" + out2.trim();\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3009,"outputTokens":133,"cacheReadTokens":0,"reasoningTokens":29}},"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],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":110,"time":1783611772840,"data":{"turn":1,"step":1,"callId":"call_00_DRxnM6R1TThfDwcudW0f2050","name":"run_code","arguments":"{\"code\": \"const out1 = await tools.bash({ command: \\\"echo CODE_ONE\\\", description: \\\"First echo\\\" });\\nconst out2 = await tools.bash({ command: \\\"echo CODE_TWO\\\", description: \\\"Second echo\\\" });\\nreturn out1.trim() + \\\"+\\\" + out2.trim();\"}"}}
|
||||
{"type":"tool/code-dispatch","seq":111,"time":1783611772933,"data":{"parentCallId":"call_00_DRxnM6R1TThfDwcudW0f2050","subCallId":"call_00_DRxnM6R1TThfDwcudW0f2050:code:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"First echo"},"isError":false,"resultSummary":"CODE_ONE\n"}}
|
||||
{"type":"tool/code-dispatch","seq":112,"time":1783611772936,"data":{"parentCallId":"call_00_DRxnM6R1TThfDwcudW0f2050","subCallId":"call_00_DRxnM6R1TThfDwcudW0f2050:code:2","name":"bash","arguments":{"command":"echo CODE_TWO","description":"Second echo"},"isError":false,"resultSummary":"CODE_TWO\n"}}
|
||||
{"type":"tool/result","seq":113,"time":1783611772937,"data":{"turn":1,"step":1,"callId":"call_00_DRxnM6R1TThfDwcudW0f2050","content":[{"type":"text","text":"CODE_ONE+CODE_TWO"}],"isError":false,"meta":{"logs":[]}},"sourceEventSeqs":[110],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":114,"time":1783611772938,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":115,"time":1783611772938,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1783611773376,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1783611773376,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":118,"time":1783611773480,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" output"}}}
|
||||
{"type":"assistant/chunk","seq":119,"time":1783611773511,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1783611773512,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1783611773540,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" what"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1783611773541,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":123,"time":1783611773541,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":124,"time":1783611773569,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" asked"}}}
|
||||
{"type":"assistant/chunk","seq":125,"time":1783611773570,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":126,"time":1783611773570,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":":"}}}
|
||||
{"type":"assistant/chunk","seq":127,"time":1783611773597,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" CODE"}}}
|
||||
{"type":"assistant/chunk","seq":128,"time":1783611773597,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_"}}}
|
||||
{"type":"assistant/chunk","seq":129,"time":1783611773597,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":130,"time":1783611773626,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"+"}}}
|
||||
{"type":"assistant/chunk","seq":131,"time":1783611773627,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"CODE"}}}
|
||||
{"type":"assistant/chunk","seq":132,"time":1783611773627,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_T"}}}
|
||||
{"type":"assistant/chunk","seq":133,"time":1783611773627,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":134,"time":1783611773654,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":135,"time":1783611773655,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"CODE"}}}
|
||||
{"type":"assistant/chunk","seq":136,"time":1783611773655,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"_"}}}
|
||||
{"type":"assistant/chunk","seq":137,"time":1783611773656,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":138,"time":1783611773656,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"+"}}}
|
||||
{"type":"assistant/chunk","seq":139,"time":1783611773656,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"CODE"}}}
|
||||
{"type":"assistant/chunk","seq":140,"time":1783611773657,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"_T"}}}
|
||||
{"type":"assistant/chunk","seq":141,"time":1783611773685,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":142,"time":1783611773686,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The output is exactly what the user asked for: CODE_ONE+CODE_TWO"}}}}
|
||||
{"type":"assistant/chunk","seq":143,"time":1783611773686,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}}}
|
||||
{"type":"assistant/chunk","seq":144,"time":1783611773686,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":89,"outputTokens":25,"cacheReadTokens":3072,"reasoningTokens":17}}}}
|
||||
{"type":"assistant/chunk","seq":145,"time":1783611773686,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":146,"time":1783611773687,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The output is exactly what the user asked for: CODE_ONE+CODE_TWO"},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":89,"outputTokens":25,"cacheReadTokens":3072,"reasoningTokens":17}},"sourceEventSeqs":[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],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":147,"time":1783611773687,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":148,"time":1783611773687,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,79 @@
|
||||
terminal 100x36 buffer=normal length=36 base=0 viewport=0
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=29 bufferRow=29
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: code-mode │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-27 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Using ONE run_code program: call the bash tool twice — exactly echo CODE_ONE then exactly echo "
|
||||
style 0-0 fg=bright-blue
|
||||
style 65-77 fg=cyan
|
||||
style 92-99 fg=cyan
|
||||
9| "▌ CODE_TWO — and return the two outputs joined with a plus sign. Then reply with that joined string "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=cyan
|
||||
10| "▌ only and stop. "
|
||||
style 0-0 fg=bright-blue
|
||||
11| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
12| <blank>
|
||||
13| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
14| " The user wants a single run_code program that calls bash twice, then returns the two outputs "
|
||||
style 1-99 fg=bright-black italic
|
||||
15| " joined with a plus sign. Let me write this. "
|
||||
style 1-43 fg=bright-black italic
|
||||
16| <blank>
|
||||
17| "▌ "
|
||||
style 0-0 fg=green
|
||||
18| "▌ ✓ const out1 = await tools.bash({ command: \"echo CODE_ONE\", description: \"First echo\" }); "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-99 bold
|
||||
19| "▌ const o "
|
||||
style 0-0 fg=green
|
||||
style 2-8 bold
|
||||
20| "▌ CODE_ONE+CODE_TWO "
|
||||
style 0-0 fg=green
|
||||
21| "▌ "
|
||||
style 0-0 fg=green
|
||||
22| <blank>
|
||||
23| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
24| " The output is exactly what the user asked for: CODE_ONE+CODE_TWO "
|
||||
style 1-64 fg=bright-black italic
|
||||
25| <blank>
|
||||
26| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
27| " CODE_ONE+CODE_TWO "
|
||||
28| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
29| " "
|
||||
style 1-1 inverse
|
||||
30| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
31| "/workspace/project ↑3.1k ↓158 idle reasoning:on tools:compact"
|
||||
style 0-49 dim
|
||||
style 67-99 dim
|
||||
32-35| <blank>
|
||||
@@ -0,0 +1,13 @@
|
||||
{"type":"session","version":0,"id":"22222222-2222-4222-8222-222222222222","createdAt":1783950001000,"cwd":"/tmp/advanced-acp","parentSession":"11111111-1111-4111-8111-111111111111"}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,13 @@
|
||||
{"type":"session","version":0,"id":"33333333-3333-4333-8333-333333333333","createdAt":1783950002000,"cwd":"/tmp/advanced-acp","parentSession":"11111111-1111-4111-8111-111111111111"}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,64 @@
|
||||
{"type":"session","version":0,"id":"11111111-1111-4111-8111-111111111111","createdAt":1783950000000,"cwd":"/tmp/advanced-acp"}
|
||||
{"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: mount a no-op Cordis plugin named snapshot-marker; use run_code to inspect the live dynamic mounts through tools.cordis_inspect; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; unmount 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":"mounted dyn-1 (plugin \"snapshot-marker\", state: active)"}],"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: 'dynamic' })\"}"}}}
|
||||
{"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: 'dynamic' })\"}"}}}}
|
||||
{"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: 'dynamic' })\"}"}],"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: 'dynamic' })\"}"}}
|
||||
{"type":"tool/code-dispatch","seq":21,"time":1783957884560,"data":{"parentCallId":"advanced-code","subCallId":"advanced-code:code:1","name":"cordis_inspect","arguments":{"what":"dynamic"},"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":"unmounted dyn-1 (plugin \"snapshot-marker\")"}],"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"}}}
|
||||
@@ -0,0 +1,116 @@
|
||||
terminal 100x36 buffer=normal length=50 base=14 viewport=14
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=33 bufferRow=47
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: cordis-dynamic-toolchain │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-42 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Run this advanced flow exactly once: mount a no-op Cordis plugin named snapshot-marker; use "
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ run_code to inspect the live dynamic mounts through tools.cordis_inspect; delegate once to a "
|
||||
style 0-0 fg=bright-blue
|
||||
10| "▌ direct spawn child; run one workflow that delegates to another spawn child; unmount dyn-1; then "
|
||||
style 0-0 fg=bright-blue
|
||||
11| "▌ reply with exactly ADVANCED_ACP_OK. "
|
||||
style 0-0 fg=bright-blue
|
||||
12| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
13| <blank>
|
||||
14| "▌ "
|
||||
style 0-0 fg=green
|
||||
15| "▌ ✓ Mount plugin into live cordis runtime "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-40 bold
|
||||
16| "▌ mounted dyn-1 (plugin \"snapshot-marker\", state: active) "
|
||||
style 0-0 fg=green
|
||||
17| "▌ "
|
||||
style 0-0 fg=green
|
||||
18| <blank>
|
||||
19| "▌ "
|
||||
style 0-0 fg=green
|
||||
20| "▌ ✓ return await tools.cordis_inspect({ what: 'dynamic' }) "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-57 bold
|
||||
21| "▌ ## dynamic "
|
||||
style 0-0 fg=green
|
||||
22| "▌ - dyn-1: snapshot-marker [active] "
|
||||
style 0-0 fg=green
|
||||
23| "▌ "
|
||||
style 0-0 fg=green
|
||||
24| <blank>
|
||||
25| "▌ "
|
||||
style 0-0 fg=green
|
||||
26| "▌ ✓ subagent "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-11 bold
|
||||
27| "▌ DIRECT_CHILD_OK "
|
||||
style 0-0 fg=green
|
||||
28| "▌ "
|
||||
style 0-0 fg=green
|
||||
29| <blank>
|
||||
30| "▌ "
|
||||
style 0-0 fg=green
|
||||
31| "▌ ✓ workflow: advanced-acp-snapshot "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-34 bold
|
||||
32| "▌ workflow \"advanced-acp-snapshot\" completed (1 agent). "
|
||||
style 0-0 fg=green
|
||||
33| "▌ Return value: "
|
||||
style 0-0 fg=green
|
||||
34| "▌ { "
|
||||
style 0-0 fg=green
|
||||
35| "▌ \"reply\": \"WORKFLOW_CHILD_OK\" "
|
||||
style 0-0 fg=green
|
||||
36| "▌ } "
|
||||
style 0-0 fg=green
|
||||
37| "▌ "
|
||||
style 0-0 fg=green
|
||||
38| <blank>
|
||||
39| "▌ "
|
||||
style 0-0 fg=green
|
||||
40| "▌ ✓ Unmount dyn-1 "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-16 bold
|
||||
41| "▌ unmounted dyn-1 (plugin \"snapshot-marker\") "
|
||||
style 0-0 fg=green
|
||||
42| "▌ "
|
||||
style 0-0 fg=green
|
||||
43| <blank>
|
||||
44| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
45| " ADVANCED_ACP_OK "
|
||||
46| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
47| " "
|
||||
style 1-1 inverse
|
||||
48| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
49| "/workspace/project ↑18 ↓18 idle reasoning:on tools:compact"
|
||||
style 0-61 dim
|
||||
style 67-99 dim
|
||||
@@ -0,0 +1,36 @@
|
||||
{"type":"session","version":0,"id":"583a4db2-3350-436c-b4a5-5615fd159052","createdAt":1783600636316,"cwd":"/var/folders/bn/vj1dvck95yd5jh3x4wskflxm0000gn/T/acp-snap-cwd-vdJYjz","parentSession":"3fd7d599-56b1-493a-930d-f1fc5e1556e8"}
|
||||
{"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":"assistant/chunk","seq":5,"time":1783600638073,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783600638173,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783600638189,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783600638213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783600638213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783600638213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WF"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783600638213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_CH"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783600638213,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ILD"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" nothing"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" else"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783600638242,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"WF"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"_CH"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ILD"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783600638276,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"_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"}}}
|
||||
@@ -0,0 +1,209 @@
|
||||
{"type":"session","version":0,"id":"3fd7d599-56b1-493a-930d-f1fc5e1556e8","createdAt":1783600631835,"cwd":"/var/folders/bn/vj1dvck95yd5jh3x4wskflxm0000gn/T/acp-snap-cwd-vdJYjz"}
|
||||
{"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":"assistant/chunk","seq":5,"time":1783600634643,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" use"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" workflow"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tool"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783600635634,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" once"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783600635635,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783600635635,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" specific"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783600635635,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" parameters"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783600635635,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" carefully"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" follow"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" instructions"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783600635743,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":":\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"1"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" args"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" omitted"}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" ("}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"so"}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" don"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"'t"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" include"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" it"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":")\n"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"2"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" meta"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" ="}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" {"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"name"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783600635744,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"sn"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"apshot"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"-flow"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\","}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"description"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"one"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" child"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" for"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" snapshot"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" }\n"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"3"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" script"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" ="}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" as"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" given"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" verb"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"atim"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1783600635745,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\n"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1783600635746,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"4"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1783600635746,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1783600635746,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" After"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1783600635746,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" it"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" returns"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WORK"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1783600635754,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FL"}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"OW"}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_D"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"Let"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" do"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" that"}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"meta"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1783600635756,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"{\""}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"name"}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"sn"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"apshot"}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"-flow"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\","}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"description"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"one"}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" child"}}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" for"}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" the"}}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" snapshot"}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":118,"time":1783600635757,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":119,"time":1783600635759,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1783600635759,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1783600635759,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"script"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1783600635759,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":123,"time":1783600635759,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":124,"time":1783600635764,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":125,"time":1783600635764,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\\n"}}}
|
||||
{"type":"assistant/chunk","seq":126,"time":1783600636039,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"phase"}}}
|
||||
{"type":"assistant/chunk","seq":127,"time":1783600636039,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"('"}}}
|
||||
{"type":"assistant/chunk","seq":128,"time":1783600636039,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"Run"}}}
|
||||
{"type":"assistant/chunk","seq":129,"time":1783600636039,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"')\\n"}}}
|
||||
{"type":"assistant/chunk","seq":130,"time":1783600636039,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"const"}}}
|
||||
{"type":"assistant/chunk","seq":131,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":132,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" ="}}}
|
||||
{"type":"assistant/chunk","seq":133,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" await"}}}
|
||||
{"type":"assistant/chunk","seq":134,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" agent"}}}
|
||||
{"type":"assistant/chunk","seq":135,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"('"}}}
|
||||
{"type":"assistant/chunk","seq":136,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"Reply"}}}
|
||||
{"type":"assistant/chunk","seq":137,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" with"}}}
|
||||
{"type":"assistant/chunk","seq":138,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":139,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" the"}}}
|
||||
{"type":"assistant/chunk","seq":140,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" word"}}}
|
||||
{"type":"assistant/chunk","seq":141,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" WF"}}}
|
||||
{"type":"assistant/chunk","seq":142,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"_CH"}}}
|
||||
{"type":"assistant/chunk","seq":143,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"ILD"}}}
|
||||
{"type":"assistant/chunk","seq":144,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":145,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" and"}}}
|
||||
{"type":"assistant/chunk","seq":146,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" nothing"}}}
|
||||
{"type":"assistant/chunk","seq":147,"time":1783600636245,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" else"}}}
|
||||
{"type":"assistant/chunk","seq":148,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":".')\\n"}}}
|
||||
{"type":"assistant/chunk","seq":149,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"return"}}}
|
||||
{"type":"assistant/chunk","seq":150,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" {"}}}
|
||||
{"type":"assistant/chunk","seq":151,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":152,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":" }\\n"}}}
|
||||
{"type":"assistant/chunk","seq":153,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":154,"time":1783600636246,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_dD2BLuNeJCTh2iiYC1QR3449","name":"workflow","argumentsDelta":"}"}}}
|
||||
{"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":"assistant/chunk","seq":165,"time":1783600640028,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":166,"time":1783600640134,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" workflow"}}}
|
||||
{"type":"assistant/chunk","seq":167,"time":1783600640162,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" returned"}}}
|
||||
{"type":"assistant/chunk","seq":168,"time":1783600640195,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":169,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":170,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":171,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":172,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":173,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"WF"}}}
|
||||
{"type":"assistant/chunk","seq":174,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_CH"}}}
|
||||
{"type":"assistant/chunk","seq":175,"time":1783600640862,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ILD"}}}
|
||||
{"type":"assistant/chunk","seq":176,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_OK"}}}
|
||||
{"type":"assistant/chunk","seq":177,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":178,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":179,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":180,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
|
||||
{"type":"assistant/chunk","seq":181,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":182,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":183,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":184,"time":1783600640864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":185,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":186,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"WORK"}}}
|
||||
{"type":"assistant/chunk","seq":187,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"FL"}}}
|
||||
{"type":"assistant/chunk","seq":188,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OW"}}}
|
||||
{"type":"assistant/chunk","seq":189,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"_D"}}}
|
||||
{"type":"assistant/chunk","seq":190,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":191,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":192,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":193,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":194,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":195,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":196,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"WORK"}}}
|
||||
{"type":"assistant/chunk","seq":197,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"FL"}}}
|
||||
{"type":"assistant/chunk","seq":198,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"OW"}}}
|
||||
{"type":"assistant/chunk","seq":199,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"_D"}}}
|
||||
{"type":"assistant/chunk","seq":200,"time":1783600640865,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"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"}}}
|
||||
@@ -0,0 +1,106 @@
|
||||
terminal 100x36 buffer=normal length=47 base=11 viewport=11
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=33 bufferRow=44
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: dynamic-workflow │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-34 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Use the workflow tool exactly once, with args omitted, meta set to { \"name\": \"snapshot-flow\", "
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ \"description\": \"one child for the snapshot\" }, and this EXACT script body (copy it verbatim): "
|
||||
style 0-0 fg=bright-blue
|
||||
10| "▌ phase('Run') "
|
||||
style 0-0 fg=bright-blue
|
||||
11| "▌ const reply = await agent('Reply with exactly the word WF_CHILD_OK and nothing else.') "
|
||||
style 0-0 fg=bright-blue
|
||||
12| "▌ return { reply } "
|
||||
style 0-0 fg=bright-blue
|
||||
13| "▌ After the workflow returns, reply with the single word WORKFLOW_DONE and stop. Do not use any "
|
||||
style 0-0 fg=bright-blue
|
||||
14| "▌ other tool. "
|
||||
style 0-0 fg=bright-blue
|
||||
15| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
16| <blank>
|
||||
17| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
18| " The user wants me to use the workflow tool exactly once with specific parameters. Let me carefully "
|
||||
style 1-99 fg=bright-black italic
|
||||
19| " follow the instructions: "
|
||||
style 1-24 fg=bright-black italic
|
||||
20| " "
|
||||
21| " 1. args omitted (so I don't include it) "
|
||||
style 1-3 fg=bright-blue
|
||||
style 4-39 fg=bright-black italic
|
||||
22| " 2. meta = { \"name\": \"snapshot-flow\", \"description\": \"one child for the snapshot\" } "
|
||||
style 1-3 fg=bright-blue
|
||||
style 4-82 fg=bright-black italic
|
||||
23| " 3. script = as given verbatim "
|
||||
style 1-3 fg=bright-blue
|
||||
style 4-29 fg=bright-black italic
|
||||
24| " 4. After it returns, reply with \"WORKFLOW_DONE\" "
|
||||
style 1-3 fg=bright-blue
|
||||
style 4-47 fg=bright-black italic
|
||||
25| " "
|
||||
26| " Let me do exactly that. "
|
||||
style 1-23 fg=bright-black italic
|
||||
27| <blank>
|
||||
28| "▌ "
|
||||
style 0-0 fg=green
|
||||
29| "▌ ✓ workflow: snapshot-flow "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-26 bold
|
||||
30| "▌ workflow \"snapshot-flow\" completed (1 agent). "
|
||||
style 0-0 fg=green
|
||||
31| "▌ Return value: "
|
||||
style 0-0 fg=green
|
||||
32| "▌ { "
|
||||
style 0-0 fg=green
|
||||
33| "▌ \"reply\": \"WF_CHILD_OK\" "
|
||||
style 0-0 fg=green
|
||||
34| "▌ } "
|
||||
style 0-0 fg=green
|
||||
35| "▌ "
|
||||
style 0-0 fg=green
|
||||
36| <blank>
|
||||
37| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
38| " The workflow returned successfully with the reply \"WF_CHILD_OK\". Now I need to reply with exactly "
|
||||
style 1-99 fg=bright-black italic
|
||||
39| " \"WORKFLOW_DONE\" and stop. "
|
||||
style 1-25 fg=bright-black italic
|
||||
40| <blank>
|
||||
41| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
42| " WORKFLOW_DONE "
|
||||
43| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
44| " "
|
||||
style 1-1 inverse
|
||||
45| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
46| "/workspace/project ↑3.5k ↓227 idle reasoning:on tools:compact"
|
||||
style 0-56 dim
|
||||
style 67-99 dim
|
||||
@@ -0,0 +1,65 @@
|
||||
{"type":"session","version":0,"id":"228b7b82-84ed-49b7-a567-981c03b28c77","createdAt":1783352113760,"cwd":"/tmp/acp-snap-cwd-aN2GRR"}
|
||||
{"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":"assistant/chunk","seq":5,"time":1783352114428,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783352114542,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783352114570,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783352114571,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783352114571,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783352114571,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783352114572,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783352114600,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783352114601,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783352114602,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783352114602,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783352114602,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783352114603,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783352114627,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783352114628,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" use"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783352114657,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783352114658,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783352114658,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"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":"assistant/chunk","seq":36,"time":1783352115341,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783352115465,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783352115492,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783352115493,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783352115493,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783352115493,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783352115521,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783352115521,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783352115521,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783352115552,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783352115552,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783352115552,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"T"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783352115552,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"WO"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783352115552,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783352115580,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783352115580,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" no"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783352115580,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tools"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783352115580,"data":{"turn":2,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,70 @@
|
||||
terminal 100x36 buffer=normal length=36 base=0 viewport=0
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=28 bufferRow=28
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: multi-turn-conversation │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-41 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Reply with exactly the word: ONE. No tools. "
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
10| <blank>
|
||||
11| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
12| " The user wants me to reply with exactly the word \"ONE\" and use no tools. "
|
||||
style 1-72 fg=bright-black italic
|
||||
13| <blank>
|
||||
14| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
15| " ONE "
|
||||
16| <blank>
|
||||
17| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
18| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
19| "▌ Reply with exactly the word: TWO. No tools. "
|
||||
style 0-0 fg=bright-blue
|
||||
20| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
21| <blank>
|
||||
22| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
23| " The user wants me to reply with exactly the word \"TWO\" and no tools. "
|
||||
style 1-68 fg=bright-black italic
|
||||
24| <blank>
|
||||
25| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
26| " TWO "
|
||||
27| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
28| " "
|
||||
style 1-1 inverse
|
||||
29| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
30| "/workspace/project ↑2.9k ↓41 idle reasoning:on tools:compact"
|
||||
style 0-62 dim
|
||||
style 67-99 dim
|
||||
31-35| <blank>
|
||||
@@ -0,0 +1,28 @@
|
||||
{"type":"session","version":0,"id":"{{sessionId}}","createdAt":0,"cwd":"{{cwd}}"}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,91 @@
|
||||
terminal 100x36 buffer=normal length=39 base=3 viewport=3
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=33 bufferRow=36
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: parallel-file-reads │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-37 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Use the read tool twice in the same assistant message: read a.txt and b.txt. Then reply DONE. "
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
10| <blank>
|
||||
11| "▌ "
|
||||
style 0-0 fg=green
|
||||
12| "▌ ✓ Read a.txt "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-13 bold
|
||||
13| "▌ <path>/workspace/project/a.txt</path> "
|
||||
style 0-0 fg=green
|
||||
14| "▌ <type>file</type> "
|
||||
style 0-0 fg=green
|
||||
15| "▌ <content> "
|
||||
style 0-0 fg=green
|
||||
16| "▌ 1: alpha "
|
||||
style 0-0 fg=green
|
||||
17| "▌ "
|
||||
style 0-0 fg=green
|
||||
18| "▌ (End of file - total 1 lines) "
|
||||
style 0-0 fg=green
|
||||
19| "▌ </content> "
|
||||
style 0-0 fg=green
|
||||
20| "▌ "
|
||||
style 0-0 fg=green
|
||||
21| <blank>
|
||||
22| "▌ "
|
||||
style 0-0 fg=green
|
||||
23| "▌ ✓ Read b.txt "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-13 bold
|
||||
24| "▌ <path>/workspace/project/b.txt</path> "
|
||||
style 0-0 fg=green
|
||||
25| "▌ <type>file</type> "
|
||||
style 0-0 fg=green
|
||||
26| "▌ <content> "
|
||||
style 0-0 fg=green
|
||||
27| "▌ 1: beta "
|
||||
style 0-0 fg=green
|
||||
28| "▌ "
|
||||
style 0-0 fg=green
|
||||
29| "▌ (End of file - total 1 lines) "
|
||||
style 0-0 fg=green
|
||||
30| "▌ </content> "
|
||||
style 0-0 fg=green
|
||||
31| "▌ "
|
||||
style 0-0 fg=green
|
||||
32| <blank>
|
||||
33| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
34| " DONE "
|
||||
35| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
36| " "
|
||||
style 1-1 inverse
|
||||
37| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
38| "/workspace/project ↑20 ↓6 idle reasoning:on tools:compact"
|
||||
style 0-55 dim
|
||||
style 67-99 dim
|
||||
@@ -0,0 +1 @@
|
||||
alpha
|
||||
@@ -0,0 +1 @@
|
||||
beta
|
||||
@@ -0,0 +1,134 @@
|
||||
{"type":"session","version":0,"id":"b0f1f758-dcf0-474e-851d-e62c11ec0a09","createdAt":1783352057652,"cwd":"/tmp/acp-snap-cwd-AYilT7"}
|
||||
{"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":"assistant/chunk","seq":5,"time":1783352058320,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783352058426,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783352058466,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783352058467,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783352058467,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783352058467,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" use"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783352058467,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783352058484,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" todo"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783352058484,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_write"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783352058484,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tool"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783352058484,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783352058485,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" record"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783352058511,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783352058512,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" plan"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783352058513,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783352058513,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783352058513,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" three"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783352058514,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" todos"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783352058540,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" in"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783352058540,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783352058571,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" specified"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783352058572,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" status"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783352058597,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"es"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783352058597,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1783352058597,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783352058597,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1783352058626,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1783352058626,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1783352058626,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1783352058626,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1783352058626,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1783352058717,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783352058717,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783352058746,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783352058747,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783352058747,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"t"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783352058747,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"odos"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783352058775,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783352058775,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783352058776,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"["}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783352058805,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"{\""}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783352058806,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"content"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783352058806,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783352058806,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783352058806,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"read"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783352058806,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" the"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" code"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\","}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"status"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1783352058832,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1783352058862,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"in"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1783352058863,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"_pro"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1783352058863,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"gress"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783352058863,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\"},"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1783352058863,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" {\""}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1783352058863,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"content"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1783352058889,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1783352058890,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1783352058890,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"write"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1783352058890,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" the"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1783352058890,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" fix"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1783352058890,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\","}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"status"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"pending"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1783352058918,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\"},"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1783352058947,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" {\""}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1783352058947,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"content"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1783352058947,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1783352058947,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1783352058947,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"run"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1783352058948,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" the"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1783352058976,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" tests"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1783352058976,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\","}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1783352058976,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1783352058976,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"status"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1783352058977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\":"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1783352058977,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":" \""}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1783352059004,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"pending"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1783352059005,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1783352059005,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"}]"}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1783352059033,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_fjAnBThbDjxepBtp3hDt3264","name":"todo_write","argumentsDelta":"}"}}}
|
||||
{"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":"assistant/chunk","seq":102,"time":1783352059733,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1783352059835,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" todos"}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1783352059863,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" have"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1783352059863,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" been"}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1783352059864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" written"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1783352059864,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1783352059892,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":1783352059892,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Now"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":1783352059893,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":1783352059893,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" just"}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1783352059920,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" need"}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1783352059920,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1783352059921,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1783352059921,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1783352059921,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1783352059950,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":118,"time":1783352059950,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":119,"time":1783352059950,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1783352059950,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1783352059951,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1783352059951,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"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"}}}
|
||||
@@ -0,0 +1,81 @@
|
||||
terminal 100x36 buffer=normal length=36 base=0 viewport=0
|
||||
lifecycle started=1 stopped=0 progress=inactive
|
||||
title "DSH TUI snapshot"
|
||||
cursor hidden column=1 viewportRow=33 bufferRow=33
|
||||
buffer
|
||||
0| "╭──────────────────────────────────────────────────────────────────────────────────────────────────╮"
|
||||
style 0-99 fg=bright-blue
|
||||
1| "│ DEEPSEEK HARNESS │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-9 fg=bright-blue bold
|
||||
style 11-17 bold
|
||||
style 99-99 fg=bright-blue
|
||||
2| "│ Recorded replay: todo-plan │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-27 fg=bright-black
|
||||
style 99-99 fg=bright-blue
|
||||
3| "│ deepseek-v4-flash • main-session │"
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-35 dim
|
||||
style 99-99 fg=bright-blue
|
||||
4| "╰──────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
||||
style 0-99 fg=bright-blue
|
||||
5| <blank>
|
||||
6| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
7| "▌ You "
|
||||
style 0-0 fg=bright-blue
|
||||
style 2-4 fg=bright-blue bold
|
||||
8| "▌ Use the todo_write tool to record a plan with exactly three todos: \"read the code\" (in_progress), "
|
||||
style 0-0 fg=bright-blue
|
||||
9| "▌ \"write the fix\" (pending), \"run the tests\" (pending). Send all three in one todo_write call. Then "
|
||||
style 0-0 fg=bright-blue
|
||||
10| "▌ reply with the single word DONE and stop. "
|
||||
style 0-0 fg=bright-blue
|
||||
11| "▌ "
|
||||
style 0-0 fg=bright-blue
|
||||
12| <blank>
|
||||
13| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
14| " The user wants me to use the todo_write tool to record a plan with exactly three todos in the "
|
||||
style 1-99 fg=bright-black italic
|
||||
15| " specified statuses, then reply with \"DONE\". "
|
||||
style 1-43 fg=bright-black italic
|
||||
16| <blank>
|
||||
17| "▌ "
|
||||
style 0-0 fg=green
|
||||
18| "▌ ✓ Update todo list "
|
||||
style 0-0 fg=green
|
||||
style 2-2 fg=green bold
|
||||
style 3-19 bold
|
||||
19| "▌ Updated todo list: 2 pending, 1 in progress, 0 completed. "
|
||||
style 0-0 fg=green
|
||||
20| "▌ "
|
||||
style 0-0 fg=green
|
||||
21| <blank>
|
||||
22| " Reasoning "
|
||||
style 1-9 fg=bright-black italic
|
||||
23| " The todos have been written successfully. Now I just need to reply with the single word \"DONE\". "
|
||||
style 1-95 fg=bright-black italic
|
||||
24| <blank>
|
||||
25| " Assistant "
|
||||
style 1-9 fg=bright-magenta bold
|
||||
26| " DONE "
|
||||
27| <blank>
|
||||
28| "Plan"
|
||||
style 0-3 fg=bright-blue bold
|
||||
29| " ● read the code"
|
||||
style 2-2 fg=yellow
|
||||
30| " ○ write the fix"
|
||||
style 2-2 dim
|
||||
31| " ○ run the tests"
|
||||
style 2-2 dim
|
||||
32| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
33| " "
|
||||
style 1-1 inverse
|
||||
34| "────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-99 dim
|
||||
35| "/workspace/project ↑3.1k ↓145 idle reasoning:on tools:compact"
|
||||
style 0-49 dim
|
||||
style 67-99 dim
|
||||
@@ -0,0 +1,172 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
import { mkdtemp, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { LOADER_SMOKE_TEST_TIMEOUT_MS, resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
|
||||
|
||||
const binScript = fileURLToPath(new URL('../../../packages/examples/stdio-demo/src/bin.ts', import.meta.url))
|
||||
const configPath = fileURLToPath(new URL('../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))
|
||||
|
||||
const PTY_DRIVER = String.raw`
|
||||
import errno, json, os, pty, select, signal, sys, time
|
||||
node, launch_args_json, launch_env_json, cwd, resume_session_id, scenario = sys.argv[1:]
|
||||
env = os.environ.copy()
|
||||
env.update(json.loads(launch_env_json))
|
||||
env.update({
|
||||
"COLUMNS": "100",
|
||||
"LINES": "30",
|
||||
})
|
||||
if resume_session_id:
|
||||
env["RESUME_SESSION_ID"] = resume_session_id
|
||||
pid, fd = pty.fork()
|
||||
if pid == 0:
|
||||
os.chdir(cwd)
|
||||
os.execvpe(node, [node, *json.loads(launch_args_json)], env)
|
||||
|
||||
output = bytearray()
|
||||
answered_question = False
|
||||
sent_prompt = False
|
||||
sent_exit = False
|
||||
deadline = time.monotonic() + 25
|
||||
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)
|
||||
if scenario == "conversation" and not sent_prompt and b"scripted TUI ready." in output:
|
||||
os.write(fd, b"exercise the TUI\r")
|
||||
sent_prompt = True
|
||||
if scenario == "conversation" and sent_prompt and not answered_question and b"How should the scripted run proceed?" in output:
|
||||
os.write(fd, b"\r")
|
||||
answered_question = True
|
||||
if scenario == "conversation" and answered_question and not sent_exit and b"Decision received. Scripted TUI run complete." in output:
|
||||
os.write(fd, b"/exit\r")
|
||||
sent_exit = True
|
||||
if scenario == "boot" and not sent_exit and b"TUI agent ready." in output:
|
||||
os.write(fd, b"/exit\r")
|
||||
sent_exit = True
|
||||
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 scenario == "resume-failure":
|
||||
if b'ui-tui: session "missing-session" failed to start:' not in output:
|
||||
sys.stderr.write("TUI did not render the startup failure before timeout\n")
|
||||
sys.exit(126)
|
||||
if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 1:
|
||||
sys.stderr.write("TUI startup failure did not exit with status 1\n")
|
||||
sys.exit(127)
|
||||
elif scenario == "conversation":
|
||||
if not sent_prompt:
|
||||
sys.stderr.write("TUI did not render the scripted welcome marker before timeout\n")
|
||||
sys.exit(128)
|
||||
if not answered_question:
|
||||
sys.stderr.write("TUI did not render the user-question dialog before timeout\n")
|
||||
sys.exit(129)
|
||||
if not sent_exit:
|
||||
sys.stderr.write("TUI did not finish the scripted tool round-trip before timeout\n")
|
||||
sys.exit(130)
|
||||
if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
|
||||
sys.stderr.write("TUI scripted conversation did not exit cleanly\n")
|
||||
sys.exit(131)
|
||||
else:
|
||||
if not sent_exit:
|
||||
sys.stderr.write("TUI did not render its welcome marker before timeout\n")
|
||||
sys.exit(124)
|
||||
if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0:
|
||||
sys.stderr.write("TUI child did not exit cleanly\n")
|
||||
sys.exit(125)
|
||||
`
|
||||
|
||||
interface TuiLoaderSmokeOptions {
|
||||
config?: string
|
||||
resumeSessionId?: string
|
||||
scenario?: 'boot' | 'conversation' | 'resume-failure'
|
||||
}
|
||||
|
||||
async function runTuiLoaderSmoke(options: TuiLoaderSmokeOptions = {}): Promise<string> {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'tui-agent-smoke-'))
|
||||
try {
|
||||
const launch = resolveExampleLaunch({
|
||||
srcBin: binScript,
|
||||
configArgs: [options.config ?? configPath],
|
||||
tsconfigPath,
|
||||
exposeInternals: true,
|
||||
env: {
|
||||
DEEPSEEK_API_KEY: 'keyless-tui-no-call',
|
||||
DSH_HOME: join(cwd, '.dsh'),
|
||||
DSH_AGENTS_HOME: join(cwd, '.agents'),
|
||||
},
|
||||
})
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn('python3', [
|
||||
'-c',
|
||||
PTY_DRIVER,
|
||||
launch.command,
|
||||
JSON.stringify(launch.args),
|
||||
JSON.stringify(launch.env),
|
||||
cwd,
|
||||
options.resumeSessionId ?? '',
|
||||
options.scenario ?? 'boot',
|
||||
], { stdio: ['ignore', 'pipe', 'pipe'] })
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
child.stdout.setEncoding('utf8')
|
||||
child.stdout.on('data', (chunk: string) => { stdout += chunk })
|
||||
child.stderr.setEncoding('utf8')
|
||||
child.stderr.on('data', (chunk: string) => { stderr += chunk })
|
||||
child.once('error', reject)
|
||||
child.once('exit', (code) => {
|
||||
if (code === 0) resolve(stdout)
|
||||
else reject(new Error(`TUI PTY smoke exited ${String(code)}. stdout:\n${stdout}\nstderr:\n${stderr}`))
|
||||
})
|
||||
})
|
||||
} finally {
|
||||
await rm(cwd, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
describe('tui-agent keyless smoke (real Loader tree in a PTY)', () => {
|
||||
it('boots pi-tui, renders the configured banner, accepts /exit, and restores the terminal', async () => {
|
||||
const output = await runTuiLoaderSmoke()
|
||||
expect(output).toContain('DEEPSEEK')
|
||||
expect(output).toContain('TUI agent ready.')
|
||||
expect(output).toContain('\u001B[?2004l')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('streams a response, answers a user-question dialog, completes the tool round-trip, and exits cleanly', async () => {
|
||||
const output = await runTuiLoaderSmoke({ config: scriptedConfigPath, scenario: 'conversation' })
|
||||
expect(output).toContain('I need one decision before I continue.')
|
||||
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('How should the scripted run proceed?')
|
||||
expect(output).toContain('Safe')
|
||||
expect(output).toContain('Decision received. Scripted TUI run complete.')
|
||||
expect(output).toContain('\u001B[?2004l')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
|
||||
it('prints a config-resume failure and exits instead of leaving a blank terminal', async () => {
|
||||
const output = await runTuiLoaderSmoke({ resumeSessionId: 'missing-session', scenario: 'resume-failure' })
|
||||
expect(output).toContain('ui-tui: session "missing-session" failed to start:')
|
||||
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user