Merge remote-tracking branch 'origin/master' into worktree/preset-plane-fallout-p1

The pre-commit staged-pairing hook was bypassed for this merge: master's
`docs(notes): archive superseded dsh run decision` (7ee9e16001) arrives as a
rename into `.agents/notes/archived/`, and the hook hands that path to
`verify-translation-pairing`, which correctly refuses an archived note as
out-of-corpus. The full-corpus gate passes (851 pairs).
This commit is contained in:
Yichen Jiang
2026-08-11 00:32:25 +08:00
195 changed files with 4373 additions and 975 deletions
@@ -0,0 +1,38 @@
# Keyless twin of product-subagent-both.cordis.yml: preserve both product
# tools while replacing only the external model adapter.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
config:
path: ./cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- insert:
- id: llm-replay
name: '@deepseek-ai/dsh-llm-replay'
config:
providers:
- id: deepseek-official
name: DeepSeek
models:
- id: deepseek-v4-flash
- id: deepseek-v4-pro
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: subagent-claude-code
name: '@deepseek-ai/dsh-subagent-claude-code'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
maxDepth: provider-managed
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
maxDepth: provider-managed
@@ -0,0 +1,27 @@
# Add both native product providers and the same independent foreground tool
# rows an Agent Preset may contribute. Loading the composition starts neither
# product; the scenario pins both model-visible schemas.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
config:
path: ./cordis.yml
patches:
- insert:
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: subagent-claude-code
name: '@deepseek-ai/dsh-subagent-claude-code'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
maxDepth: provider-managed
- id: tool-subagent-claude-code
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
maxDepth: provider-managed
@@ -0,0 +1,29 @@
# Keyless twin of product-subagent-codex.cordis.yml: keep the same product
# provider/tool composition and replace only the external model adapter.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
config:
path: ./cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- insert:
- id: llm-replay
name: '@deepseek-ai/dsh-llm-replay'
config:
providers:
- id: deepseek-official
name: DeepSeek
models:
- id: deepseek-v4-flash
- id: deepseek-v4-pro
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
maxDepth: provider-managed
@@ -0,0 +1,18 @@
# Add the native Codex product provider and its preset-shaped foreground tool to
# the real ACP composition. The model is told not to call it; the scenario pins
# the assembled request schema without starting Codex.
- id: base
name: '@deepseek-ai/cordis-plugin-include'
config:
path: ./cordis.yml
patches:
- insert:
- id: subagent-codex
name: '@deepseek-ai/dsh-subagent-codex'
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
maxDepth: provider-managed
+23
View File
@@ -55,6 +55,8 @@ const WEB_CONFIG = fileURLToPath(new URL('../web.cordis.yml', import.meta.url))
const FS_SEARCH_CONFIG = fileURLToPath(new URL('./fs-search.cordis.yml', import.meta.url))
const PARTIAL_LANDLOCK_CONFIG = fileURLToPath(new URL('../partial-landlock.cordis.yml', import.meta.url))
const PWSH_CONFIG = fileURLToPath(new URL('./pwsh.cordis.yml', import.meta.url))
const PRODUCT_SUBAGENT_CODEX_CONFIG = fileURLToPath(new URL('../product-subagent-codex.cordis.yml', import.meta.url))
const PRODUCT_SUBAGENT_BOTH_CONFIG = fileURLToPath(new URL('../product-subagent-both.cordis.yml', import.meta.url))
const FS_DIFF_BOUND_CONFIG = fileURLToPath(new URL('./fs-diff-bound.cordis.yml', import.meta.url))
const SNAPSHOTS_DIR = join(dirname(fileURLToPath(import.meta.url)), 'snapshots')
const PACKED_CHUNKS_SOURCE = 'hook-cc-pretool-deny'
@@ -126,6 +128,27 @@ const SCENARIOS: Scenario[] = [
// text-turn is the default header pin and owns the prompt and tool-schema
// sidecars reused by alternate classes with identical component sequences.
{ name: 'text-turn', hasModelTurn: true, recorded: true, pinsHeader: true },
// Product-subagent scenarios are authored schema-isolation fixtures: they
// reuse the stable text-turn transcript so only Loader-composed headers and
// tool sidecars vary. Model output and usage are not evidence here, so record
// mode must not replace them with live-API output.
{
name: 'product-subagent-codex',
hasModelTurn: true,
recorded: false,
pinsHeader: true,
headerClass: 'product-subagent-codex',
configPath: PRODUCT_SUBAGENT_CODEX_CONFIG,
},
{
name: 'product-subagent-both',
hasModelTurn: true,
recorded: false,
pinsHeader: true,
headerClass: 'product-subagent-both',
systemPromptSource: 'product-subagent-codex',
configPath: PRODUCT_SUBAGENT_BOTH_CONFIG,
},
{
name: 'session-title-after-turn',
hasModelTurn: true,
@@ -0,0 +1,7 @@
{
"steps": [
{ "op": "initialize" },
{ "op": "newSession" },
{ "op": "prompt", "text": "Reply with exactly the word: PONG. Do not use any tools." }
]
}
@@ -0,0 +1,22 @@
{"type":"session","version":0,"id":"539aa64c-7f37-40ff-abd8-ed45b717be1b","createdAt":1783600629539,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"agent/inbox/spliced","seq":0,"time":1785498761270,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly the word: PONG. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"3e25dc34-48e0-4738-8401-1a8d181d37e5"}]}}
{"type":"turn/start","seq":1,"time":1785821359466,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":2,"time":1785821359466,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":3,"time":1783600629542,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":4,"time":1785498761313,"data":{"content":[{"type":"text","text":"Reply with exactly the word: PONG. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"3e25dc34-48e0-4738-8401-1a8d181d37e5"},"surfaceOp":"append"}
{"type":"user/message","seq":5,"time":1785730415287,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"4b8d9730-0b7b-4e14-8a30-3d852f808f0e"},"surfaceOp":"append"}
{"type":"session/title","seq":6,"time":1785730415287,"data":{"title":"Reply with exactly the word:","messageSeqs":[4],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":7,"time":1785498761318,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":8,"time":1785730415288,"data":{"provider":"deepseek-official","model":"deepseek-v4-pro"}}
{"type":"assistant/chunk","seq":9,"time":1783600630822,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":10,"time0":1783600630852,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,33,1,40,0,0,0,0,0,18,0,36,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","P","ONG","\""," and"," not"," use"," any"," tools","."]}}
{"type":"assistant/chunk","seq":30,"time":1783600631006,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":31,"time":1783600631008,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"P"}}}
{"type":"assistant/chunk","seq":32,"time":1783600631009,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONG"}}}
{"type":"assistant/chunk","seq":33,"time":1783600631009,"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 \"PONG\" and not use any tools."}}}}
{"type":"assistant/chunk","seq":34,"time":1783600631009,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"PONG"}}}}
{"type":"assistant/chunk","seq":35,"time":1785498761338,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3091,"outputTokens":23,"cacheReadTokens":0,"reasoningTokens":20}}}}
{"type":"assistant/chunk","seq":36,"time":1785730415297,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":37,"time":1785730415298,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"PONG\" and not use any tools."},{"type":"text","text":"PONG"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"f1418376-f303-4017-acd7-92899c841c8a"},"usage":{"inputTokens":3091,"outputTokens":23,"cacheReadTokens":0,"reasoningTokens":20}},"sourceEventSeqs":[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],"surfaceOp":"append"}
{"type":"step/end","seq":38,"time":1785730415298,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":39,"time":1785730415298,"data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,4 @@
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"PONG"}}}}
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}
@@ -0,0 +1,569 @@
{
"initial": [
{
"name": "bash",
"description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. Attempting a command the sandbox may deny is safe and expected: run it and read the marker rather than assuming the denial. When a command is denied and a wider mode would let it succeed, escalate immediately in the same turn — the one sanctioned exception to a denial: retry the exact same command once with `sandbox_permissions` (the narrowest wider mode that suffices) plus a one-sentence `justification`. Do not detour through chat to ask permission first — the approval prompt raised by that retry is how the user consents. If the session states approval prompts are disabled, there is no exception: a denial is final — do not set `sandbox_permissions`. Never escalate speculatively: ground the request in a real denial — normally the one this command just hit; escalating up front is fine only when this session already denied the same access. A rejected escalation is final for that command — stop and explain, never work around it — but it does not forbid attempting or escalating other commands later.",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The bash command to execute."
},
"description": {
"type": "string",
"description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."
},
"timeoutMs": {
"type": "number",
"description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
},
"workdir": {
"type": "string",
"description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
},
"run_in_background": {
"type": "boolean",
"description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this command needs. Only valid as a one-shot retry of a command the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access."
}
},
"required": [
"command",
"description"
]
}
},
{
"name": "create_goal",
"description": "Create one persisted same-session completion goal when the current direct human request is a long-running objective that should continue across autonomous goal rounds. You may infer that intent without requiring the user to say \"create a goal\". Do not use this for trivial single-turn work. Execution rejects non-human and subagent authority.",
"parameters": {
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The concrete completion objective inferred from the direct human request."
},
"max_goal_rounds": {
"type": "number",
"description": "Optional positive safe-integer limit on automatic continuation rounds."
}
},
"required": [
"objective"
]
}
},
{
"name": "edit",
"description": "Edit an existing UTF-8 text file by replacing literal text.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to edit, resolved by the filesystem backend."
},
"old_string": {
"type": "string",
"description": "Literal text to replace. Must match exactly."
},
"new_string": {
"type": "string",
"description": "Literal replacement text. Use an empty string to delete the match."
},
"replace_all": {
"type": "boolean",
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
}
},
"required": [
"file_path",
"old_string",
"new_string"
]
}
},
{
"name": "get_goal",
"description": "Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal.",
"parameters": {
"type": "object",
"properties": {}
}
},
{
"name": "interrupt_agent",
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
"parameters": {
"type": "object",
"properties": {
"agent_id": {
"type": "string",
"description": "The agent id of the running agent to interrupt."
}
},
"required": [
"agent_id"
]
}
},
{
"name": "list_agents",
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
"parameters": {
"type": "object",
"properties": {
"scope": {
"type": "string",
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
"enum": [
"children",
"descendants"
]
}
}
}
},
{
"name": "ralph",
"description": "Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.",
"parameters": {
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The immutable completion objective for every fresh Ralph round."
},
"maxRounds": {
"type": "number",
"description": "Optional positive safe-integer round cap, bounded by the deployment ceiling."
}
},
"required": [
"objective"
]
}
},
{
"name": "read",
"description": "Read a UTF-8 text file and return line-numbered content.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to read, resolved by the filesystem backend."
},
"offset": {
"type": "number",
"description": "1-based first line to return. Defaults to 1."
},
"limit": {
"type": "number",
"description": "Maximum number of lines to return. Defaults to 2000."
}
},
"required": [
"file_path"
]
}
},
{
"name": "send_message",
"description": "Send a message to a background subagent by its subagent id, continuing the same conversation. It becomes the subagent's next turn: if it is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. This call returns no answer from the subagent — only confirmation that the message was delivered — so use it to give it more work. A failure means the message was NOT delivered.",
"parameters": {
"type": "object",
"properties": {
"subagent_id": {
"type": "string",
"description": "The subagent id returned when the background subagent was started."
},
"message": {
"type": "string",
"description": "The message to deliver to the subagent."
}
},
"required": [
"subagent_id",
"message"
]
}
},
{
"name": "skill",
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The exact skill name from the available skills list."
}
},
"required": [
"name"
]
}
},
{
"name": "subagent",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"run_in_background": {
"type": "boolean",
"description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "subagent_claude_code",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "subagent_codex",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "subagent_fork",
"description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn), returning only its final result. Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive only its final answer, not its intermediate steps. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new."
},
"run_in_background": {
"type": "boolean",
"description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "task_kill",
"description": "Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops.",
"parameters": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "Task id returned by the tool that started the background work."
},
"reason": {
"type": "string",
"description": "Optional short reason, recorded in the log and forwarded to the task."
}
},
"required": [
"task_id"
]
}
},
{
"name": "task_list",
"description": "List your background tasks (running and finished) with their ids, kinds, and statuses.",
"parameters": {
"type": "object",
"properties": {}
}
},
{
"name": "task_output",
"description": "Read a background task. Stream tasks return only output since the previous read; final-output tasks return their result after settlement. Every response ends with `[status: ...]`. Reads are non-blocking unless `wait: true`, which waits up to the configured cap.",
"parameters": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "Task id returned by the tool that started the background work."
},
"wait": {
"type": "boolean",
"description": "Block until the task reaches a terminal status or the timeout expires. A timed-out wait returns [status: running] and leaves the task alive."
},
"timeout_ms": {
"type": "number",
"description": "Max wait in milliseconds (only meaningful with wait: true). Defaults to the configured wait timeout; capped by the configured maximum."
}
},
"required": [
"task_id"
]
}
},
{
"name": "todo_write",
"description": "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (there are no partial updates, no per-item edits). Use it to plan multi-step work and show progress: add one todo per concrete step before you start. Mark every todo being actively worked on `in_progress` — several at once when work genuinely runs in parallel (e.g. concurrent subagents or background commands), one for sequential work; while work remains, at least one task should be `in_progress`. Mark a todo `completed` the moment it is done (do not batch completions), and allow no `in_progress` item only once all work is complete. Skip the list for trivial single-step tasks. Statuses: `pending` (not started), `in_progress` (being worked on now), `completed` (finished).",
"parameters": {
"type": "object",
"properties": {
"todos": {
"type": "array",
"description": "The COMPLETE task list, replacing any previous list.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"content": {
"type": "string",
"description": "What the task is — a short imperative line."
},
"status": {
"type": "string",
"description": "pending (not started) | in_progress (now) | completed (done).",
"enum": [
"pending",
"in_progress",
"completed"
]
}
},
"required": [
"content",
"status"
]
}
}
},
"required": [
"todos"
]
}
},
{
"name": "update_goal",
"description": "Update the exact current goal revision. edit, pause, and resume require a direct top-level human request. During an automatic continuation of the current goal, complete and blocked are also allowed. blocked is rejected before the configured minimum round count; the model remains responsible for judging that the same condition persisted across those rounds and must explain it in blocked_reason.",
"parameters": {
"type": "object",
"properties": {
"goal_id": {
"type": "string",
"description": "Exact id returned by get_goal."
},
"revision": {
"type": "number",
"description": "Exact positive revision returned by get_goal."
},
"action": {
"type": "string",
"description": "edit | pause | resume | complete | blocked",
"enum": [
"edit",
"pause",
"resume",
"complete",
"blocked"
]
},
"objective": {
"type": "string",
"description": "Replacement objective; valid only with action edit."
},
"max_goal_rounds": {
"type": "number",
"description": "Replacement cap; valid only with action edit."
},
"blocked_reason": {
"type": "string",
"description": "Concrete blocking condition; required only with action blocked."
}
},
"required": [
"goal_id",
"revision",
"action"
]
}
},
{
"name": "workflow",
"description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nThe workflow's identity rides the `meta` parameter as JSON: required `name` (short kebab-case) and `description` strings, optional `whenToUse` string and `phases` array (`{title, detail?, provider?, model?}`). The `script` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO `export const meta` statement — meta is a parameter, not code), running with top-level await; end with `return <value>` — the value must be JSON-serializable and is this tool's result.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise<any>` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides (either may be provided alone). Anything else (`effort`/`isolation`/`agentType`) is rejected loudly.\n- `pipeline(items, ...stages): Promise<any[]>` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. An ordinary stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise<any[]>` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item `null`.\n\nConstraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.",
"parameters": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
},
"meta": {
"type": "object",
"description": "The workflow identity block (plain JSON — never code).",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "Short kebab-case workflow name."
},
"description": {
"type": "string",
"description": "One-line description of what the workflow does."
},
"whenToUse": {
"type": "string",
"description": "Optional guidance on when this workflow applies."
},
"phases": {
"type": "array",
"description": "Optional phase declarations matched by phase() calls.",
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"title": {
"type": "string",
"description": "The phase title phase() calls match by exact string."
},
"detail": {
"type": "string",
"description": "Optional one-line description of the phase."
},
"provider": {
"type": "string",
"description": "Optional provider override this phase is expected to use."
},
"model": {
"type": "string",
"description": "Optional model override this phase is expected to use."
}
},
"required": [
"title"
]
}
}
},
"required": [
"name",
"description"
]
},
"args": {
"type": "object",
"description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]}).",
"additionalProperties": true
}
},
"required": [
"script",
"meta"
]
}
},
{
"name": "write",
"description": "Create or fully replace a UTF-8 text file.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to write, resolved by the filesystem backend."
},
"content": {
"type": "string",
"description": "Full UTF-8 text content to write."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
}
},
"required": [
"file_path",
"content"
]
}
}
],
"changes": []
}
@@ -0,0 +1,7 @@
{
"steps": [
{ "op": "initialize" },
{ "op": "newSession" },
{ "op": "prompt", "text": "Reply with exactly the word: PONG. Do not use any tools." }
]
}
@@ -0,0 +1,22 @@
{"type":"session","version":0,"id":"539aa64c-7f37-40ff-abd8-ed45b717be1b","createdAt":1783600629539,"cwd":"{{cwd}}","delegationDepth":0}
{"type":"agent/inbox/spliced","seq":0,"time":1785498761270,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly the word: PONG. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"3e25dc34-48e0-4738-8401-1a8d181d37e5"}]}}
{"type":"turn/start","seq":1,"time":1785821359466,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":2,"time":1785821359466,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":3,"time":1783600629542,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":4,"time":1785498761313,"data":{"content":[{"type":"text","text":"Reply with exactly the word: PONG. Do not use any tools."}],"source":{"kind":"user"},"role":"user","id":"3e25dc34-48e0-4738-8401-1a8d181d37e5"},"surfaceOp":"append"}
{"type":"user/message","seq":5,"time":1785730415287,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"4b8d9730-0b7b-4e14-8a30-3d852f808f0e"},"surfaceOp":"append"}
{"type":"session/title","seq":6,"time":1785730415287,"data":{"title":"Reply with exactly the word:","messageSeqs":[4],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":7,"time":1785498761318,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":8,"time":1785730415288,"data":{"provider":"deepseek-official","model":"deepseek-v4-pro"}}
{"type":"assistant/chunk","seq":9,"time":1783600630822,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
{"type":"reasoning-chunks","seq0":10,"time0":1783600630852,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,33,1,40,0,0,0,0,0,18,0,36,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," reply"," with"," exactly"," the"," word"," \"","P","ONG","\""," and"," not"," use"," any"," tools","."]}}
{"type":"assistant/chunk","seq":30,"time":1783600631006,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
{"type":"assistant/chunk","seq":31,"time":1783600631008,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"P"}}}
{"type":"assistant/chunk","seq":32,"time":1783600631009,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ONG"}}}
{"type":"assistant/chunk","seq":33,"time":1783600631009,"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 \"PONG\" and not use any tools."}}}}
{"type":"assistant/chunk","seq":34,"time":1783600631009,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"PONG"}}}}
{"type":"assistant/chunk","seq":35,"time":1785498761338,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3091,"outputTokens":23,"cacheReadTokens":0,"reasoningTokens":20}}}}
{"type":"assistant/chunk","seq":36,"time":1785730415297,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
{"type":"assistant/message","seq":37,"time":1785730415298,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to reply with exactly the word \"PONG\" and not use any tools."},{"type":"text","text":"PONG"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"c883cf16-01fe-4afc-b37c-d255bb450d21"},"usage":{"inputTokens":3091,"outputTokens":23,"cacheReadTokens":0,"reasoningTokens":20}},"sourceEventSeqs":[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],"surfaceOp":"append"}
{"type":"step/end","seq":38,"time":1785730415298,"data":{"turn":1,"step":1}}
{"type":"turn/end","seq":39,"time":1785730415298,"data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,4 @@
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"PONG"}}}}
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}
@@ -0,0 +1,22 @@
You are an AI agent powered by the DeepSeek Harness SDK.
You are a coding assistant powered by the deepseek-v4-pro model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug.
Verify your work by running the code or tests. Keep answers brief and factual.
Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.
Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.
Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.
Check the [exit code: N] marker on every bash result; investigate failures before moving on.
Track every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.
Use goal tools for one long-running completion objective in the current session. create_goal may infer goal intent from a direct human request in any language; do not create a goal for routine single-turn work. Call get_goal before update_goal and copy its exact goal_id and revision. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least 3 consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.
Use the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.
@@ -0,0 +1,548 @@
{
"initial": [
{
"name": "bash",
"description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. Attempting a command the sandbox may deny is safe and expected: run it and read the marker rather than assuming the denial. When a command is denied and a wider mode would let it succeed, escalate immediately in the same turn — the one sanctioned exception to a denial: retry the exact same command once with `sandbox_permissions` (the narrowest wider mode that suffices) plus a one-sentence `justification`. Do not detour through chat to ask permission first — the approval prompt raised by that retry is how the user consents. If the session states approval prompts are disabled, there is no exception: a denial is final — do not set `sandbox_permissions`. Never escalate speculatively: ground the request in a real denial — normally the one this command just hit; escalating up front is fine only when this session already denied the same access. A rejected escalation is final for that command — stop and explain, never work around it — but it does not forbid attempting or escalating other commands later.",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The bash command to execute."
},
"description": {
"type": "string",
"description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."
},
"timeoutMs": {
"type": "number",
"description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
},
"workdir": {
"type": "string",
"description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
},
"run_in_background": {
"type": "boolean",
"description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this command needs. Only valid as a one-shot retry of a command the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access."
}
},
"required": [
"command",
"description"
]
}
},
{
"name": "create_goal",
"description": "Create one persisted same-session completion goal when the current direct human request is a long-running objective that should continue across autonomous goal rounds. You may infer that intent without requiring the user to say \"create a goal\". Do not use this for trivial single-turn work. Execution rejects non-human and subagent authority.",
"parameters": {
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The concrete completion objective inferred from the direct human request."
},
"max_goal_rounds": {
"type": "number",
"description": "Optional positive safe-integer limit on automatic continuation rounds."
}
},
"required": [
"objective"
]
}
},
{
"name": "edit",
"description": "Edit an existing UTF-8 text file by replacing literal text.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to edit, resolved by the filesystem backend."
},
"old_string": {
"type": "string",
"description": "Literal text to replace. Must match exactly."
},
"new_string": {
"type": "string",
"description": "Literal replacement text. Use an empty string to delete the match."
},
"replace_all": {
"type": "boolean",
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
}
},
"required": [
"file_path",
"old_string",
"new_string"
]
}
},
{
"name": "get_goal",
"description": "Read the current same-session goal, including its exact id/revision, objective, phase, completed continuation rounds, round limit, blocker reason when present, and whether another continuation is armed. Call this before updating a goal.",
"parameters": {
"type": "object",
"properties": {}
}
},
{
"name": "interrupt_agent",
"description": "Request cancellation of a background agent's current turn by its agent id. The target may be your direct child or a deeper agent created under you. Only the current turn stops: messages already queued for the agent stay parked until a later send_message, agents it started keep running, and the agent itself stays available for follow-ups. This call returns as soon as the stop request is accepted, so the target may keep running briefly; interrupting an agent that already finished is an accepted no-op.",
"parameters": {
"type": "object",
"properties": {
"agent_id": {
"type": "string",
"description": "The agent id of the running agent to interrupt."
}
},
"required": [
"agent_id"
]
}
},
{
"name": "list_agents",
"description": "List your continuable background subagents by durable id and label. Status comes from the live registry: running means the agent is working right now, idle means it is loaded but between turns (it may be waiting on agents it started), and complete means it exists only in storage — a direct child remains a `send_message` candidate in every status. The snapshot is not a delivery promise — `send_message` performs the authoritative check and may still fail. Children that could not be read are reported as diagnostics instead of being silently dropped. Scope `descendants` walks the whole tree below you in stable pre-order, annotating each entry with its durable direct-parent session id and depth. You may use `send_message` only for depth-1 entries; deeper entries are candidates for `interrupt_agent` only.",
"parameters": {
"type": "object",
"properties": {
"scope": {
"type": "string",
"description": "children (default) lists direct children only; descendants walks the complete tree below you.",
"enum": [
"children",
"descendants"
]
}
}
}
},
{
"name": "ralph",
"description": "Run a foreground fresh-agent Ralph loop toward one immutable objective. Use only when the direct human explicitly asks for Ralph or fresh-agent iteration. Each round opens a new child with no parent conversation or prior child session; the shared workspace is long-term memory, and only a bounded structured report crosses rounds. The call returns when a worker reports completion or a concrete blocker, or at the round limit. Ordinary long-running same-session work belongs to goal tools.",
"parameters": {
"type": "object",
"properties": {
"objective": {
"type": "string",
"description": "The immutable completion objective for every fresh Ralph round."
},
"maxRounds": {
"type": "number",
"description": "Optional positive safe-integer round cap, bounded by the deployment ceiling."
}
},
"required": [
"objective"
]
}
},
{
"name": "read",
"description": "Read a UTF-8 text file and return line-numbered content.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to read, resolved by the filesystem backend."
},
"offset": {
"type": "number",
"description": "1-based first line to return. Defaults to 1."
},
"limit": {
"type": "number",
"description": "Maximum number of lines to return. Defaults to 2000."
}
},
"required": [
"file_path"
]
}
},
{
"name": "send_message",
"description": "Send a message to a background subagent by its subagent id, continuing the same conversation. It becomes the subagent's next turn: if it is still working, the message waits until its current turn finishes, so it cannot redirect work already underway. This call returns no answer from the subagent — only confirmation that the message was delivered — so use it to give it more work. A failure means the message was NOT delivered.",
"parameters": {
"type": "object",
"properties": {
"subagent_id": {
"type": "string",
"description": "The subagent id returned when the background subagent was started."
},
"message": {
"type": "string",
"description": "The message to deliver to the subagent."
}
},
"required": [
"subagent_id",
"message"
]
}
},
{
"name": "skill",
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The exact skill name from the available skills list."
}
},
"required": [
"name"
]
}
},
{
"name": "subagent",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
},
"run_in_background": {
"type": "boolean",
"description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "subagent_codex",
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "subagent_fork",
"description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn), returning only its final result. Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive only its final answer, not its intermediate steps. Set `run_in_background: true` to start a background subagent that keeps its conversation: you receive only its subagent id, never its result, and it works on its own. Use this for work whose result you do not need returned by this call; `send_message` sends it more work.",
"parameters": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "A short (3-5 word) description of the delegated task, for display."
},
"prompt": {
"type": "string",
"description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new."
},
"run_in_background": {
"type": "boolean",
"description": "Run as a background subagent that keeps its conversation and return only its subagent id. This call never returns its result; send it more work with send_message."
}
},
"required": [
"description",
"prompt"
]
}
},
{
"name": "task_kill",
"description": "Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops.",
"parameters": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "Task id returned by the tool that started the background work."
},
"reason": {
"type": "string",
"description": "Optional short reason, recorded in the log and forwarded to the task."
}
},
"required": [
"task_id"
]
}
},
{
"name": "task_list",
"description": "List your background tasks (running and finished) with their ids, kinds, and statuses.",
"parameters": {
"type": "object",
"properties": {}
}
},
{
"name": "task_output",
"description": "Read a background task. Stream tasks return only output since the previous read; final-output tasks return their result after settlement. Every response ends with `[status: ...]`. Reads are non-blocking unless `wait: true`, which waits up to the configured cap.",
"parameters": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "Task id returned by the tool that started the background work."
},
"wait": {
"type": "boolean",
"description": "Block until the task reaches a terminal status or the timeout expires. A timed-out wait returns [status: running] and leaves the task alive."
},
"timeout_ms": {
"type": "number",
"description": "Max wait in milliseconds (only meaningful with wait: true). Defaults to the configured wait timeout; capped by the configured maximum."
}
},
"required": [
"task_id"
]
}
},
{
"name": "todo_write",
"description": "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (there are no partial updates, no per-item edits). Use it to plan multi-step work and show progress: add one todo per concrete step before you start. Mark every todo being actively worked on `in_progress` — several at once when work genuinely runs in parallel (e.g. concurrent subagents or background commands), one for sequential work; while work remains, at least one task should be `in_progress`. Mark a todo `completed` the moment it is done (do not batch completions), and allow no `in_progress` item only once all work is complete. Skip the list for trivial single-step tasks. Statuses: `pending` (not started), `in_progress` (being worked on now), `completed` (finished).",
"parameters": {
"type": "object",
"properties": {
"todos": {
"type": "array",
"description": "The COMPLETE task list, replacing any previous list.",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"content": {
"type": "string",
"description": "What the task is — a short imperative line."
},
"status": {
"type": "string",
"description": "pending (not started) | in_progress (now) | completed (done).",
"enum": [
"pending",
"in_progress",
"completed"
]
}
},
"required": [
"content",
"status"
]
}
}
},
"required": [
"todos"
]
}
},
{
"name": "update_goal",
"description": "Update the exact current goal revision. edit, pause, and resume require a direct top-level human request. During an automatic continuation of the current goal, complete and blocked are also allowed. blocked is rejected before the configured minimum round count; the model remains responsible for judging that the same condition persisted across those rounds and must explain it in blocked_reason.",
"parameters": {
"type": "object",
"properties": {
"goal_id": {
"type": "string",
"description": "Exact id returned by get_goal."
},
"revision": {
"type": "number",
"description": "Exact positive revision returned by get_goal."
},
"action": {
"type": "string",
"description": "edit | pause | resume | complete | blocked",
"enum": [
"edit",
"pause",
"resume",
"complete",
"blocked"
]
},
"objective": {
"type": "string",
"description": "Replacement objective; valid only with action edit."
},
"max_goal_rounds": {
"type": "number",
"description": "Replacement cap; valid only with action edit."
},
"blocked_reason": {
"type": "string",
"description": "Concrete blocking condition; required only with action blocked."
}
},
"required": [
"goal_id",
"revision",
"action"
]
}
},
{
"name": "workflow",
"description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nThe workflow's identity rides the `meta` parameter as JSON: required `name` (short kebab-case) and `description` strings, optional `whenToUse` string and `phases` array (`{title, detail?, provider?, model?}`). The `script` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO `export const meta` statement — meta is a parameter, not code), running with top-level await; end with `return <value>` — the value must be JSON-serializable and is this tool's result.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise<any>` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf — no pattern/format/numeric bounds) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides (either may be provided alone). Anything else (`effort`/`isolation`/`agentType`) is rejected loudly.\n- `pipeline(items, ...stages): Promise<any[]>` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. An ordinary stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise<any[]>` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item `null`.\n\nConstraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.",
"parameters": {
"type": "object",
"properties": {
"script": {
"type": "string",
"description": "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
},
"meta": {
"type": "object",
"description": "The workflow identity block (plain JSON — never code).",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "Short kebab-case workflow name."
},
"description": {
"type": "string",
"description": "One-line description of what the workflow does."
},
"whenToUse": {
"type": "string",
"description": "Optional guidance on when this workflow applies."
},
"phases": {
"type": "array",
"description": "Optional phase declarations matched by phase() calls.",
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"title": {
"type": "string",
"description": "The phase title phase() calls match by exact string."
},
"detail": {
"type": "string",
"description": "Optional one-line description of the phase."
},
"provider": {
"type": "string",
"description": "Optional provider override this phase is expected to use."
},
"model": {
"type": "string",
"description": "Optional model override this phase is expected to use."
}
},
"required": [
"title"
]
}
}
},
"required": [
"name",
"description"
]
},
"args": {
"type": "object",
"description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]}).",
"additionalProperties": true
}
},
"required": [
"script",
"meta"
]
}
},
{
"name": "write",
"description": "Create or fully replace a UTF-8 text file.",
"parameters": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to write, resolved by the filesystem backend."
},
"content": {
"type": "string",
"description": "Full UTF-8 text content to write."
},
"sandbox_permissions": {
"type": "string",
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
"enum": [
"workspace-write",
"danger-full-access"
]
},
"justification": {
"type": "string",
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
}
},
"required": [
"file_path",
"content"
]
}
}
],
"changes": []
}
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write examples/headless-agent/README.md
README.md: f12a56920c79f3a7e257c4e56163f323e4312d11
README.zh.md: 9e409735f03afc62cd788fa2a5d1afdef0fa6c2a
README.md: 08b36e9c8e558db3b30175b20bd6e0af160ccef5
README.zh.md: 40d11ebeeb1631af1421b3c013a5e548fd3fd488
+2 -2
View File
@@ -10,10 +10,10 @@ This directory owns the replay and real-model test composition for a headless co
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run dsh run "fix the failing test in this workspace"
pnpm run dsh --profile headless "fix the failing test in this workspace"
```
The product command is [`dsh run`](../../apps/cli/README.md): it accepts one nonblank task, creates and persists a fresh session, prints the final assistant text, and exits. The root `demo:headless` script is only an alias of that command.
The product command is [`dsh --profile headless`](../../apps/cli/README.md): it accepts one nonblank task, creates and persists a fresh session, prints the final assistant text, and exits. The root `demo:headless` script is only an alias of that command.
Snapshot suites run this directory's configuration through [`tests/fixtures/headless-driver.ts`](tests/fixtures/headless-driver.ts), an unexported test-only process that emits canonical session events as JSONL before its result record. That stream is test infrastructure, not a supported CLI output format. Child sessions surface only through parent tool events and results.
+2 -2
View File
@@ -10,10 +10,10 @@
# repo root .env (gitignored) or exported env:
# DEEPSEEK_API_KEY=sk-…
# DEEPSEEK_BASE_URL=https://… # optional; defaults to the public API
pnpm run dsh run "fix the failing test in this workspace"
pnpm run dsh --profile headless "fix the failing test in this workspace"
```
产品命令是 [`dsh run`](../../apps/cli/README.md):它接受一项非空任务,创建并持久化新会话,打印最终 assistant 文本,然后退出。根目录的 `demo:headless` 脚本只是该命令的别名。
产品命令是 [`dsh --profile headless`](../../apps/cli/README.md):它接受一项非空任务,创建并持久化新会话,打印最终 assistant 文本,然后退出。根目录的 `demo:headless` 脚本只是该命令的别名。
快照套件通过 [`tests/fixtures/headless-driver.ts`](tests/fixtures/headless-driver.ts) 运行本目录的配置。这个未导出且仅供测试使用的进程会在结果记录之前,以 JSONL 发出规范会话事件。该事件流属于测试基础设施,不是受支持的 CLI(命令行界面)输出格式。子会话只通过父会话的工具事件和结果对外显示。
@@ -52,9 +52,9 @@ const dshBinScript = fileURLToPath(new URL('../../../apps/cli/src/bin.ts', impor
const tsconfigPath = fileURLToPath(new URL('../../../tsconfig.json', import.meta.url))
const reasoningConfigPath = fileURLToPath(new URL('./fixtures/cli.cordis.yml', import.meta.url))
const deepseekDefaultsConfigPath = fileURLToPath(new URL('./fixtures/deepseek-defaults.cordis.yml', import.meta.url))
const dshRunOverlayPath = fileURLToPath(new URL('./fixtures/dsh-run.cordis.yml', import.meta.url))
const dshRunSessionExpected = join(snapshotsDir, 'dsh-run', 'session.expected.jsonl')
const dshRunFailureExpected = join(snapshotsDir, 'dsh-run', 'stderr.expected.txt')
const headlessOverlayPath = fileURLToPath(new URL('./fixtures/headless-profile.cordis.yml', import.meta.url))
const headlessSessionExpected = join(snapshotsDir, 'headless-profile', 'session.expected.jsonl')
const headlessFailureExpected = join(snapshotsDir, 'headless-profile', 'stderr.expected.txt')
const cliMockLlmPluginPath = fileURLToPath(new URL('./fixtures/cli-mock-llm.ts', import.meta.url))
const refreshing = process.env.DSH_SNAPSHOT === 'refresh'
@@ -217,14 +217,14 @@ async function prepareCliMockFixture(cwd: string): Promise<void> {
}
describe('headless stream-json snapshots', () => {
it('runs one task through the product dsh run command', async () => {
const task = 'Prove the product dsh run path with one real tool round trip.'
it('runs one task through the product headless profile command', async () => {
const task = 'Prove the product headless profile path with one real tool round trip.'
const result = await runLoaderSmoke({
label: 'product dsh run snapshot',
tempDirPrefix: 'headless-snapshot-dsh-run-',
label: 'product headless profile snapshot',
tempDirPrefix: 'headless-snapshot-profile-',
binScript: dshBinScript,
configPath: dshRunOverlayPath,
binArgs: ['run', '--patch', dshRunOverlayPath, task],
configPath: headlessOverlayPath,
binArgs: ['--profile', 'headless', '--patch', headlessOverlayPath, task],
tsconfigPath,
env: {
DSH_PERMISSION_MODE: 'danger-full-access',
@@ -236,11 +236,11 @@ describe('headless stream-json snapshots', () => {
const logs = await persistedLogs(cwd, join(cwd, '.dsh', 'sessions'))
expect(logs).toHaveLength(1)
const actual = logs[0]
if (actual === undefined) throw new Error('dsh run did not persist its session')
if (actual === undefined) throw new Error('the headless profile did not persist its session')
const context = contextFromLogs([actual.content])
const session = scrubRequestHeaders(normalizeSessionLog(actual.content, context))
if (refreshing) await writeFile(dshRunSessionExpected, session)
expect(session).toBe(await readFile(dshRunSessionExpected, 'utf8'))
if (refreshing) await writeFile(headlessSessionExpected, session)
expect(session).toBe(await readFile(headlessSessionExpected, 'utf8'))
expect(session).toContain(task)
expect(session).toContain('CLI tool round trip complete: CLI_TOOL_ROUND_TRIP')
},
@@ -250,13 +250,13 @@ describe('headless stream-json snapshots', () => {
expect(result.stderr).toBe('')
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('prints a terminal model failure through the product dsh run command', async () => {
it('prints a terminal model failure through the product headless profile command', async () => {
const result = await runLoaderSmoke({
label: 'product dsh run model failure snapshot',
tempDirPrefix: 'headless-snapshot-dsh-run-failure-',
label: 'product headless profile model failure snapshot',
tempDirPrefix: 'headless-snapshot-profile-failure-',
binScript: dshBinScript,
configPath: dshRunOverlayPath,
binArgs: ['run', '--patch', dshRunOverlayPath, 'Trigger the keyless model failure.'],
configPath: headlessOverlayPath,
binArgs: ['--profile', 'headless', '--patch', headlessOverlayPath, 'Trigger the keyless model failure.'],
tsconfigPath,
expectedExitCode: 1,
env: {
@@ -268,7 +268,7 @@ describe('headless stream-json snapshots', () => {
})
expect(result.stdout).toBe('\n')
await expect(result.stderr).toMatchFileSnapshot(dshRunFailureExpected)
await expect(result.stderr).toMatchFileSnapshot(headlessFailureExpected)
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('prints the original Loader activation error through the assembled one-shot app', async () => {
@@ -2,16 +2,16 @@
{"type":"permission/preset","seq":0,"time":0,"data":{"preset":"danger-full-access"}}
{"type":"sandbox/mode","seq":1,"time":0,"data":{"mode":"danger-full-access"}}
{"type":"approval/policy","seq":2,"time":0,"data":{"policy":"never"}}
{"type":"agent/inbox/spliced","seq":3,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Prove the product dsh run path with one real tool round trip."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}
{"type":"agent/inbox/spliced","seq":3,"time":0,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Prove the product headless profile path with one real tool round trip."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"}]}}
{"type":"turn/start","seq":4,"time":0,"data":{"turn":1}}
{"type":"agent/inbox/spliced","seq":5,"time":0,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","seq":6,"time":0,"data":{"turn":1,"step":1}}
{"type":"user/message","seq":7,"time":0,"data":{"content":[{"type":"text","text":"Prove the product dsh run path with one real tool round trip."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":7,"time":0,"data":{"content":[{"type":"text","text":"Prove the product headless profile path with one real tool round trip."}],"source":{"kind":"user"},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"user/message","seq":8,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"{{sessionId}}"},"surfaceOp":"append"}
{"type":"session/title","seq":9,"time":0,"data":{"title":"Prove the product dsh run","messageSeqs":[7],"source":{"kind":"fallback"}}}
{"type":"session/title","seq":9,"time":0,"data":{"title":"Prove the product headless profile","messageSeqs":[7],"source":{"kind":"fallback"}}}
{"type":"request/header","seq":10,"time":0,"data":{"header":{"config":{"provider":"cli-mock","model":"cli-mock","reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","seq":11,"time":0,"data":{"provider":"cli-mock","model":"cli-mock"}}
{"type":"session/title-llm-request","seq":12,"time":0,"data":{"titleProvider":"session-title-first-message-llm","messageSeqs":[7],"route":{"provider":"cli-mock","model":"cli-mock"},"system":"Create a concise title for an AI coding-assistant session from the supplied human messages.\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\nUse the language of the messages.\nAim for about 5 words in non-CJK languages or 10 CJK characters.","messages":[{"content":[{"type":"text","text":"Generate the session title from this JSON array of human messages:\n[{\"seq\":7,\"text\":\"Prove the product dsh run path with one real tool round trip.\"}]"}],"source":{"kind":"plugin","plugin":"dsh-session-title-llm"},"role":"user","id":"{{sessionId}}"}],"maxTokens":64}}
{"type":"session/title-llm-request","seq":12,"time":0,"data":{"titleProvider":"session-title-first-message-llm","messageSeqs":[7],"route":{"provider":"cli-mock","model":"cli-mock"},"system":"Create a concise title for an AI coding-assistant session from the supplied human messages.\nReturn only the title on one line, **in plain text of natural language**, with no quotes, prefix, explanation, Markdown, XML, or terminal control codes. No code is allowed.\nUse the language of the messages.\nAim for about 5 words in non-CJK languages or 10 CJK characters.","messages":[{"content":[{"type":"text","text":"Generate the session title from this JSON array of human messages:\n[{\"seq\":7,\"text\":\"Prove the product headless profile path with one real tool round trip.\"}]"}],"source":{"kind":"plugin","plugin":"dsh-session-title-llm"},"role":"user","id":"{{sessionId}}"}],"maxTokens":64}}
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"cli-smoke-call","name":"bash","argumentsDelta":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}
{"type":"assistant/chunk","seq":15,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"cli-smoke-call","name":"bash","arguments":"{\"command\":\"printf CLI_TOOL_ROUND_TRIP\",\"description\":\"Prove the CLI tool round trip.\"}"}}}}