Files
deepseek-harness/docs/tool-catalog.md
T

48 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-plan-mode exit_plan_mode ctx.tools, ctx.systemPrompt, ctx.userInteraction (execution time, opportunistic) tool/call, plan/mode inactive on an approved review, tool/result - exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-interaction seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary.
@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-pty terminal_close, terminal_list, terminal_open, terminal_read, terminal_send, terminal_signal ctx.tools, ctx.pty, ctx.systemPrompt, ctx.tasks at call time for run_in_background tool/call, tool/result - The six terminal tools are opt-in and complement one-shot bash/filesystem tools. terminal_send(run_in_background: true) registers with ctx.tasks; TUI, named key sequences, BEL, resize, auto-start, and cross-agent sharing are absent from the schema.
@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: background bash commands, PTY sends, and subagents 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",
        "additionalProperties": true,
        "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",
              "additionalProperties": true,
              "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-plan-mode

exit_plan_mode

Use only in plan mode. Present your plan for the user's review and, on approval, leave plan mode. Send the COMPLETE plan as markdown, starting with a # heading that names it. The user may approve (carry out the plan from your next step) or keep planning — their feedback comes back in the tool result; revise and present again.

{
  "type": "object",
  "properties": {
    "plan": {
      "type": "string",
      "description": "The complete plan, as markdown, starting with a # heading that names it."
    }
  },
  "required": [
    "plan"
  ]
}

Source: packages/plan/plan-mode/src/index.ts

exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-interaction seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary.

@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 } }, output: { schema: { type: 'string' }, render(_args, value) { return [{ type: 'text', text: value }] } }, async execute(args) { return args.text } })) to give yourself a new tool — it becomes callable on your NEXT step. Tool parameters: each key IS a property — { type: 'string'|'number'|'integer'|'boolean'|'null'|'object'|'array'|'json', required?: true, description?, enum?, const?, items?, properties? }; every direct DSL object declares additionalProperties: true|false, and oneOf: [schema, schema, ...] replaces type for an exact-one union. A raw JSON-Schema { type: 'object', properties, required?: […] } wrapper is also accepted with open-by-default objects. A tool's execute MUST return the lossless JSON value declared by output.schema; output.render(args, value) separately returns Native/model content blocks. 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.

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-pty

terminal_close

Close one persistent terminal and wait until its captured owned process tree is gone.

{
  "type": "object",
  "properties": {
    "sessionId": {
      "type": "string",
      "description": "Terminal session id."
    }
  },
  "required": [
    "sessionId"
  ]
}

Source: packages/pty/tool-pty/src/index.ts

terminal_list

List persistent terminal sessions owned by the current agent.

{
  "type": "object",
  "properties": {}
}

Source: packages/pty/tool-pty/src/index.ts

terminal_open

Create a persistent, owner-isolated terminal session from a registered backend type. Use this for shell or REPL state that must survive across tool calls.

{
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "description": "Registered terminal backend type, usually \"shell\"."
    },
    "name": {
      "type": "string",
      "description": "Optional owner-local display name such as \"main\" or \"gdb\"."
    },
    "cwd": {
      "type": "string",
      "description": "Initial working directory. Defaults to the deployment workspace root."
    }
  },
  "required": [
    "type"
  ]
}

Source: packages/pty/tool-pty/src/index.ts

terminal_read

Read a bounded page of retained output from a persistent terminal without sending input.

{
  "type": "object",
  "properties": {
    "sessionId": {
      "type": "string",
      "description": "Terminal session id."
    },
    "offset": {
      "type": "number",
      "description": "Newest-relative line offset (default 0)."
    },
    "count": {
      "type": "number",
      "description": "Requested line count (default 500; backend caps apply)."
    }
  },
  "required": [
    "sessionId"
  ]
}

Source: packages/pty/tool-pty/src/index.ts

terminal_send

Send text to a persistent terminal. By default Enter is submitted and the call waits for a prompt, stdin wait, output silence, timeout, or session exit. Background mode returns a task id for task_output/task_kill.

{
  "type": "object",
  "properties": {
    "sessionId": {
      "type": "string",
      "description": "Terminal session id returned by terminal_open or terminal_list."
    },
    "text": {
      "type": "string",
      "description": "UTF-8 text to write to the terminal."
    },
    "submit": {
      "type": "boolean",
      "description": "Submit Enter after text (default true). Set false for control characters or incomplete REPL input."
    },
    "run_in_background": {
      "type": "boolean",
      "description": "Return a task id immediately; collect with task_output or stop with task_kill."
    }
  },
  "required": [
    "sessionId",
    "text"
  ]
}

Source: packages/pty/tool-pty/src/index.ts

terminal_signal

Send an allowed signal to the current foreground process group of a persistent terminal.

{
  "type": "object",
  "properties": {
    "sessionId": {
      "type": "string",
      "description": "Terminal session id."
    },
    "signal": {
      "type": "string",
      "description": "Signal to deliver. Shell-targeted SIGKILL is rejected; use terminal_close.",
      "enum": [
        "SIGINT",
        "SIGTERM",
        "SIGKILL",
        "SIGTSTP",
        "SIGHUP"
      ]
    }
  },
  "required": [
    "sessionId",
    "signal"
  ]
}

Source: packages/pty/tool-pty/src/index.ts

The six terminal tools are opt-in and complement one-shot bash/filesystem tools. terminal_send(run_in_background: true) registers with ctx.tasks; TUI, named key sequences, BEL, resize, auto-start, and cross-agent sharing are absent from the schema.

@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: background bash commands, PTY sends, and subagents 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",
        "additionalProperties": true,
        "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. 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.
  • 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.
  • 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.
  • phase(title) — start a progress phase; log(message) — narrate progress; args — the tool call's args input, 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).",
      "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"
  ]
}

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

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.