41 KiB
Tool Schema Catalog
Every model-facing tool a shipped plugin contributes to ctx.tools: the name, description, and JSON-Schema parameters the model receives via the system-prompt assembly. It complements the cordis events & services catalogs (the wiring a plugin listens to and calls) and core-data-structures/ (the types those signatures move) — this page is the tools the agent is offered.
This file is GENERATED and verified fresh by pnpm run verify-tool-catalog (part of doc-sync) — do not edit it by hand. Unlike the cordis catalog (a pure source-AST pass), this generator BOOTS each tool plugin on a real context and reads ctx.tools.schemas(), because a tool schema is not statically knowable (runtime-spread enums, concatenated descriptions, config-driven names, raw-JSON-Schema MCP tools). A completeness guard globs packages/*/tool-* and fails if any package is missing from the generator's boot manifest, so a new tool cannot be silently undocumented. See the tool-schema-catalog Agent Note.
Scope: shipped product tools under packages/*/tool-*, each booted with its DEFAULT config. The registered tool NAME can be a load-time config (e.g. tool-subagent's toolName), so a deployment may surface a package under a different or additional name — a per-package note records those shipped aliases where they exist. The examples/ demo tools (e.g. echo) are excluded, matching the cordis catalog's packages-only scope.
Tool Package Map
This table connects model-visible tool names to the plugin package and service seams behind them. Exact JSON Schemas follow in the package sections below.
| Tool package | Model-visible names | Requires | Writes / affects | Shipped aliases | Deployment note |
|---|---|---|---|---|---|
@deepseek-ai/dsh-tool-ask-user |
ask_user_question |
ctx.tools, ctx.userInteraction |
tool/call, tool/result after a UI/provider answers the question |
- | ask_user_question pauses the tool call until the active UI provider returns a human answer. |
@deepseek-ai/dsh-tools |
run_code |
ctx.tools, ctx.codeRuntime (execution time), ctx.systemPrompt |
tool/call, one tool/code-dispatch per bridged sub-call, tool/result |
- | Owned by the tool registry as a reserved transport outside filterable capability layers under mode: code / mode: both (see the Code Mode Agent Note). Under code it is the registry's only wire contribution; the other visible capabilities are declared in a generated TypeScript SDK section, and a program calls them through serialized bindings that re-enter the complete guarded tool pipeline and link each nested execution to this outer result. |
@deepseek-ai/dsh-tool-bash |
bash |
ctx.tools, ctx.bash, ctx.tasks at call time for run_in_background |
tool/call, tool/result |
- | The bash tool is the model-facing consumer of the bash executor seam. A run_in_background run registers with the generic ctx.tasks runtime and is collected/stopped through the task_* tools from @deepseek-ai/dsh-tool-tasks; the enableRunInBackground config (default true) removes the parameter entirely when disabled. |
@deepseek-ai/dsh-tool-cordis |
cordis_inspect, cordis_mount, cordis_unmount |
ctx.tools |
tool/call, tool/result, live plugin-tree mutations (mount/unmount) |
- | Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; a full changed request header logs those tool-set changes. |
@deepseek-ai/dsh-tool-fs |
edit, read, write |
ctx.tools, ctx.fs, ctx.systemPrompt |
tool/call, fs/write-intent or fs/edit-intent for mutations, fs/observed after successful file operations, tool/result |
- | The read-before-write/edit policy is added by @deepseek-ai/dsh-fs-policy (an fs/* event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin. |
@deepseek-ai/dsh-tool-fs-search |
glob, grep |
ctx.tools, ctx.bash, ctx.systemPrompt |
tool/call, tool/result |
- | glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find rg, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments. |
@deepseek-ai/dsh-tool-goal |
create_goal, get_goal, update_goal |
ctx.tools, ctx.agents, ctx.goals, ctx.systemPrompt, a calling Agent in an authorized open turn |
tool/call, context/message goal snapshot for mutations, tool/result |
- | create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds. |
@deepseek-ai/dsh-tool-lsp |
lsp |
ctx.tools, ctx.lsp, ctx.systemPrompt |
tool/call, tool/result |
- | The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. @deepseek-ai/dsh-lsp-local) at runtime; without one, a query returns the structured LSP_UNAVAILABLE error rather than changing the schema. |
@deepseek-ai/dsh-tool-ralph |
ralph |
ctx.tools, ctx.workflows, ctx.subagents, ctx.systemPrompt, a calling Agent (exec.agent parents every fresh round) |
tool/call, tool/result, workflow and child session events during execution |
- | A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap. |
@deepseek-ai/dsh-tool-skill |
skill |
ctx.tools, ctx.skills |
tool/call, tool/result |
- | - |
@deepseek-ai/dsh-tool-subagent |
subagent |
ctx.tools, ctx.subagents |
tool/call, tool/result, child session events through the chosen provider |
subagent, subagent_fork |
The registered tool name is the load-time toolName config (default subagent); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees subagent_fork (bound to the fork backend) with an identical schema — see examples/tui-agent/cordis.yml and examples/acp-agent/cordis.yml. |
@deepseek-ai/dsh-tool-tasks |
task_kill, task_list, task_output |
ctx.tools, ctx.tasks, ctx.systemPrompt |
tool/call, tool/result, context/message via agent.inject() for background completion notices |
- | The kind-agnostic background-task control surface: a background bash command and a background subagent are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' ctx.tasks.start(). |
@deepseek-ai/dsh-tool-todo |
todo_write |
ctx.tools, owning Agent session |
tool/call, todo/write, tool/result |
- | todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan. |
@deepseek-ai/dsh-tool-workflow |
workflow |
ctx.tools, ctx.workflows, ctx.systemPrompt, a calling Agent (exec.agent parents the script children) |
tool/call, tool/result |
- | - |
@deepseek-ai/dsh-tool-web |
web_fetch, web_search |
ctx.tools, ctx.web, ctx.systemPrompt |
tool/call, tool/result |
- | web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps. |
@deepseek-ai/dsh-tool-ask-user
ask_user_question
Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. Send one or more questions, each with a stable id that will be echoed in the answer.
{
"type": "object",
"properties": {
"questions": {
"type": "array",
"description": "Questions to ask the user before continuing.",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Stable id for this question; echoed in the answer."
},
"question": {
"type": "string",
"description": "The specific question to ask the user."
},
"header": {
"type": "string",
"description": "Optional short heading for the question, such as \"Confirm\" or \"Choose Mode\"."
},
"options": {
"type": "array",
"description": "Optional choices to show the user. If you recommend one, put it first and append \"(Recommended)\" to that label.",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"description": "Short user-facing option label."
},
"description": {
"type": "string",
"description": "One sentence explaining the tradeoff or impact."
}
},
"required": [
"label"
]
}
},
"multi_select": {
"type": "boolean",
"description": "Whether the user may select more than one option. Defaults to false."
}
},
"required": [
"id",
"question"
]
}
}
},
"required": [
"questions"
]
}
Source: packages/ui/tool-ask-user/src/index.ts
ask_user_question pauses the tool call until the active UI provider returns a human answer.
@deepseek-ai/dsh-tools
run_code
Execute a TypeScript program against the available tools. Write the BODY of an async function (erasable syntax only; top-level await and return work) and call tools as await tools.name(args) per the declarations in the system prompt. Only what you print or return comes back — curate it.
{
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "The program: the body of an async TypeScript function."
}
},
"required": [
"code"
]
}
Source: packages/core/tools/src/code-mode.ts
Owned by the tool registry as a reserved transport outside filterable capability layers under mode: code / mode: both (see the Code Mode Agent Note). Under code it is the registry's only wire contribution; the other visible capabilities are declared in a generated TypeScript SDK section, and a program calls them through serialized bindings that re-enter the complete guarded tool pipeline and link each nested execution to this outer result.
@deepseek-ai/dsh-tool-bash
bash
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.
{
"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."
}
},
"required": [
"command",
"description"
]
}
Source: packages/bash/tool-bash/src/index.ts
The bash tool is the model-facing consumer of the bash executor seam. A run_in_background run registers with the generic ctx.tasks runtime and is collected/stopped through the task_* tools from @deepseek-ai/dsh-tool-tasks; the enableRunInBackground config (default true) removes the parameter entirely when disabled.
@deepseek-ai/dsh-tool-cordis
cordis_inspect
Inspect the live cordis runtime that is running THIS agent. Read-only. Sections: services (every provided ctx service and the plugin fiber that owns it), plugins (a flat list of the loaded plugins with their lifecycle states), tools (the model-facing tools currently registered, i.e. what you can call), dynamic (plugins you mounted via cordis_mount: id, name, state, provided services, awaited services), api (method signatures AND argument/return type shapes for every LIVE service — read this before writing plugin code that calls a service), events (every harness event with its dispatch mode and exact signature — pick listener targets here). Omit what to get all six sections. With what:"api" or what:"events", pass an exact name to narrow to one service/event and include its original source JSDoc.
{
"type": "object",
"properties": {
"what": {
"type": "string",
"description": "Limit the report to one section. Omit for all sections.",
"enum": [
"services",
"plugins",
"tools",
"dynamic",
"api",
"events"
]
},
"name": {
"type": "string",
"description": "Exact service key or event name whose original JSDoc to include; valid only with what:\"api\" or what:\"events\"."
}
}
}
Source: packages/cordis/tool-cordis/src/index.ts
cordis_mount
Mount a NEW cordis plugin into the live runtime that is running THIS agent (self-modification). code runs as the body of an async JavaScript function in an isolated sandbox and MUST return a plugin. Two forms: FUNCTION form return (ctx) => { … } — declares no inject, so it can register tools, listen to events, and provide services, but reaching ANY service (e.g. ctx.bash) throws; use it only when you need no services. OBJECT form return { name?, inject: ['bash', 'llm', …], apply(ctx) { … } } — declares dependencies, and cordis activates the plugin only after the services exist; PREFER this form. You may reach ONLY the services you list in inject: an undeclared service throws even if it exists, because an undeclared dependency would not be cleaned up if its provider is unmounted. BEFORE calling a service from your code, read cordis_inspect what:"api" — it lists method signatures AND the type shapes of their arguments/returns (do not guess a field's type; e.g. a bash run's stdout is an object, not a string). Inside apply, use the standard cordis API: ctx.on(event, listener) to observe events (see cordis_inspect what:"events"), or call harness.registerTool(ctx, harness.defineTool({ name, description, parameters: { text: { type: 'string', required: true } }, async execute(args) { … } })) to give yourself a new tool — it becomes callable on your NEXT step. Tool parameters: each key IS a property — { type: 'string'|'number'|'boolean'|'object'|'array', required?: true, description?, enum?, items?, properties? }; a JSON-Schema-style { type: 'object', properties, required: […] } wrapper and type 'integer' are also accepted and normalized. A tool's execute MUST return an ARRAY of content blocks, e.g. return [{ type: 'text', text: someString }] — never a bare string. Mounts can COMPOSE: one plugin may ctx.provide('name', value) a service and another may declare inject: ['name'] to consume it — the consumer stays pending until the provider exists and returns to pending when the provider is unmounted. Everything registered inside apply is cleaned up automatically on unmount. Sandbox globals: console (tagged [cordis:<id>], writes through to the harness terminal), harness.defineTool, harness.registerTool, btoa, atob, TextEncoder, TextDecoder. Node APIs are DISABLED — do filesystem/network/timer work through the cordis services, never Node built-ins: require, setTimeout/setInterval, and fetch throw redirect errors; process and Buffer are undefined. Instead use inject: ['fs'] + ctx.fs for files, inject: ['web'] + ctx.web for HTTP, inject: ['bash'] + ctx.bash for processes, and inject: ['timer'] + ctx.setTimeout/ctx.setInterval for timing (fiber effects, auto-cleaned on unmount) — cordis_inspect what:"api" shows what THIS runtime provides. Write PLAIN JavaScript, not TypeScript (no as, no type annotations). Cautions: (1) waterfall events (e.g. tools/pre-execute) hand the listener a trailing next callback which MUST be called — returning without next() VETOES the call; prefer plain notification events unless you intend to intercept. (2) Never await something that only resolves after the current turn (your code runs INSIDE a tool call of that turn — it would deadlock). (3) Your ctx is a restricted façade: you can register tools, observe events, provide/consume services, and use timers, but framework internals (ctx.root, ctx.fiber, ctx.extend, ctx.plugin, …) are withheld. It is not a security boundary though — the services you inject (e.g. ctx.bash) reach the real runtime.
{
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Body of an async JS function; must `return` the plugin to mount."
}
},
"required": [
"code"
]
}
Source: packages/cordis/tool-cordis/src/index.ts
cordis_unmount
Dispose a plugin previously mounted with cordis_mount, by id. All its registrations (event listeners, tools, services) are cleaned up through the cordis effect lifecycle. Returns only after disposal has fully completed (quiescence, not just a request to stop).
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "The dynamic mount id returned by cordis_mount (e.g. \"dyn-1\")."
}
},
"required": [
"id"
]
}
Source: packages/cordis/tool-cordis/src/index.ts
Ships in examples/cordis-agent only (a deliberate opt-in — mounted code gets the real ctx, see .agents/notes/implemented/feature/2026-07-08-self-referential-cordis-toolset.md). Plugins the model mounts may register ADDITIONAL model-visible tools at runtime; a full changed request header logs those tool-set changes.
@deepseek-ai/dsh-tool-fs
edit
Edit an existing UTF-8 text file by replacing literal text.
{
"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."
}
},
"required": [
"file_path",
"old_string",
"new_string"
]
}
Source: packages/fs/tool-fs/src/index.ts
read
Read a UTF-8 text file and return line-numbered content.
{
"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"
]
}
Source: packages/fs/tool-fs/src/index.ts
write
Create or fully replace a UTF-8 text file.
{
"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."
}
},
"required": [
"file_path",
"content"
]
}
Source: packages/fs/tool-fs/src/index.ts
The read-before-write/edit policy is added by @deepseek-ai/dsh-fs-policy (an fs/* event-gate plugin, no schema change); a deployment that loads these tools is expected to also load it. The tool schemas above are identical with or without the policy plugin.
@deepseek-ai/dsh-tool-fs-search
glob
Find files whose paths match a glob pattern. Returns matching paths sorted by modification time, including hidden and ignored files (VCS metadata directories are excluded). Returns the first 100 paths inline; a capped result reports where the complete list was saved.
{
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\")."
},
"path": {
"type": "string",
"description": "Directory to search in. Defaults to the session workspace; a relative path resolves against it."
}
},
"required": [
"pattern"
]
}
Source: packages/fs/tool-fs-search/src/index.ts
grep
Search file contents with a ripgrep regular expression. Returns matching lines with line numbers, grouped by file. Returns the first 250 matches inline; a capped result reports where the complete match list was saved. Use read on a matched file for surrounding context.
{
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "Regular expression to search for (ripgrep syntax)."
},
"path": {
"type": "string",
"description": "File or directory to search. Defaults to the session workspace; a relative path resolves against it."
},
"include": {
"type": "string",
"description": "One glob filter for which files to search (e.g. \"*.ts\", \"*.{js,jsx}\"). Not a list; negation is not supported."
}
},
"required": [
"pattern"
]
}
Source: packages/fs/tool-fs-search/src/index.ts
glob and grep are conditional bash-backed discovery tools: they register only when ctx.bash can find rg, then run fixed ripgrep commands through ctx.bash as ordinary foreground calls (never background tasks). Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.
@deepseek-ai/dsh-tool-goal
create_goal
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.
{
"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"
]
}
Source: packages/goal/tool-goal/src/index.ts
get_goal
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.
{
"type": "object",
"properties": {}
}
Source: packages/goal/tool-goal/src/index.ts
update_goal
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.
{
"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"
]
}
Source: packages/goal/tool-goal/src/index.ts
create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds.
@deepseek-ai/dsh-tool-lsp
lsp
Query a language server for precise code navigation. operation is one of goToDefinition, findReferences, goToImplementation, hover. line and character are one-based UTF-16 cursor coordinates. findReferences includes the declaration.
{
"type": "object",
"properties": {
"operation": {
"type": "string",
"description": "goToDefinition, findReferences, goToImplementation, or hover.",
"enum": [
"goToDefinition",
"findReferences",
"goToImplementation",
"hover"
]
},
"file_path": {
"type": "string",
"description": "The source file to query, relative to the workspace or absolute."
},
"line": {
"type": "number",
"description": "One-based line of the cursor."
},
"character": {
"type": "number",
"description": "One-based UTF-16 column of the cursor."
}
},
"required": [
"operation",
"file_path",
"line",
"character"
]
}
Source: packages/lsp/tool-lsp/src/index.ts
The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. @deepseek-ai/dsh-lsp-local) at runtime; without one, a query returns the structured LSP_UNAVAILABLE error rather than changing the schema.
@deepseek-ai/dsh-tool-ralph
ralph
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.
{
"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"
]
}
Source: packages/workflow/tool-ralph/src/index.ts
A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap.
@deepseek-ai/dsh-tool-skill
skill
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.
{
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The exact skill name from the available skills list."
}
},
"required": [
"name"
]
}
Source: packages/skill/tool-skill/src/index.ts
@deepseek-ai/dsh-tool-subagent
subagent
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 return a task id; collect with task_output and stop with task_kill.
{
"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 task and return its id; collect with task_output or stop with task_kill."
}
},
"required": [
"description",
"prompt"
]
}
Source: packages/subagent/tool-subagent/src/index.ts
The registered tool name is the load-time toolName config (default subagent); the schema above is that default. The shipped example agents load this package once per subagent backend, so the model additionally sees subagent_fork (bound to the fork backend) with an identical schema — see examples/tui-agent/cordis.yml and examples/acp-agent/cordis.yml.
@deepseek-ai/dsh-tool-tasks
task_kill
Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops.
{
"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"
]
}
Source: packages/tasks/tool-tasks/src/index.ts
task_list
List your background tasks (running and finished) with their ids, kinds, and statuses.
{
"type": "object",
"properties": {}
}
Source: packages/tasks/tool-tasks/src/index.ts
task_output
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.
{
"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"
]
}
Source: packages/tasks/tool-tasks/src/index.ts
The kind-agnostic background-task control surface: a background bash command and a background subagent are read, listed, and killed through the same three tools. Loading the plugin attaches the control surface that arms producers' ctx.tasks.start().
@deepseek-ai/dsh-tool-todo
todo_write
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. Keep AT MOST ONE todo in_progress at a time; while work remains, exactly one active 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).
{
"type": "object",
"properties": {
"todos": {
"type": "array",
"description": "The COMPLETE task list, replacing any previous list.",
"items": {
"type": "object",
"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"
]
}
Source: packages/todo/tool-todo/src/index.ts
todo_write is session-owned state; UIs render the latest todo/write event as a checklist or ACP plan.
@deepseek-ai/dsh-tool-workflow
workflow
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.
The 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.
Script-body hooks:
agent(prompt, opts?): Promise<any>— run one subagent to completion. Withoutopts.schemait resolves to the child's final text; withopts.schema(an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const — no oneOf/pattern/format/numeric bounds) it resolves to the validated object. Resolvesnullwhen the child fails (filter with.filter(Boolean)). Other opts:label(display),phase(progress group), and independentprovider/modelLLM target overrides (either may be provided alone). Anything else (effort/isolation/agentType) is rejected loudly.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 tonulland skips its remaining stages.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 tonull.phase(title)— start a progress phase;log(message)— narrate progress;args— the tool call'sargsinput, verbatim.
Misused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item null.
Constraints: 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.
{
"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).",
"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",
"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\": [...]})."
}
},
"required": [
"script",
"meta"
]
}
Source: packages/workflow/tool-workflow/src/index.ts
@deepseek-ai/dsh-tool-web
web_fetch
Fetch the content of a specific HTTP(S) URL and return it decoded to text.
{
"type": "object",
"properties": {
"url": {
"type": "string",
"description": "The HTTP(S) URL to fetch."
}
},
"required": [
"url"
]
}
Source: packages/web/tool-web/src/index.ts
web_search
Search the web for current information. Returns an optional summary answer and a list of source URLs.
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query."
}
},
"required": [
"query"
]
}
Source: packages/web/tool-web/src/index.ts
web_search and web_fetch keep provider selection behind ctx.web so model-visible schemas stay stable across backend swaps.