Files
deepseek-harness/packages/core/agent-tool-mode/README.md
T
Yichen Jiang 5d2c943d38 fix(tools): state the code-mode collapse in the prompt and the denial
The executor collapse landed without telling the model it exists. Every
tool contributes its own guidance section naming its tool, none of them
qualify how that tool is reached, and they all render before the SDK
(orders 100-199 against SDK_SECTION_ORDER 150), so the prompt said "Use
the read tool" eleven times and never said only run_code is callable.

A real session shows the consequence: the model emitted a native call,
read `unknown tool "read"` for a tool the same prompt declares, and
concluded the deployment was inconsistent rather than routing through
run_code.

The registry now contributes `tools:code-only` at order 99 -- ahead of
the guidance band -- stating the rule, registered wherever `tools:sdk`
is and rendering empty outside an effective `code`. `both` renders it
empty because its native calls do execute, which is also why
both-mode-turn no longer shares code-mode-turn's expected prompt. The
denial itself now names the route back, since a bare UNKNOWN_TOOL for a
declared tool is what misled the model.
2026-08-11 22:51:16 +08:00

2.8 KiB

dsh-agent-tool-mode

English | 中文

The row an agent preset carries to say which form of its tools the model sees: native (every schema), code (only run_code plus a generated TypeScript SDK), or both.

Why a row rather than a registry

The tool registry cannot move into a preset. Its consumers are all host-plane — dsh-agent-loop reads its scheduler, dsh-apiproxy reads its presenters to render tool cards, and every tool plugin registers into it — and a service only moves down when all of its consumers move with it.

What a preset can own is the presentation of that registry. ctx.tools.presentAs() declares it for the mounting agent alone, so a Code Mode session runs beside native ones in one process, each seeing its own catalog. The deployment's mode on the dsh-tools row remains the default that agents declaring nothing get.

What it does

native applies immediately. A code mode instead waits for ctx.codeRuntime, which is a host-plane service (dsh-code-runtime-worker): a preset selecting Code Mode against a deployment composing no runtime then holds this row pending, and dsh-agent-presets refuses the mount naming this id. The alternative — applying optimistically — moves the failure to the session's first request, where the operator can act on neither the preset nor the composition.

mode is required rather than defaulted, because a preset without this row already gets the deployment default; an omitted value would mean the row was composed for nothing.

One agent declares one presentation. A second declaration in the same composition is refused rather than merged: two answers to "which form does the model see" is a contradiction, not an override.

Model Experience

Indirectly, through the projection it selects in dsh-tools: code presents run_code plus a generated SDK section and the rule that only run_code may be called directly, native presents every tool schema. The selection also decides what may EXECUTE: under code the registry resolves a model-direct call naming any other tool to UNKNOWN_TOOL, so this row is what keeps the announced surface and the callable surface the same for every agent it covers (executor-collapse note).

KV Cache effect

No direct invalidation; the presentation is fixed when the agent is composed, so its request prefix is stable for the session's life.

Known Limitations and Deferred Work

  • The runtime stays host-plane — a preset can select Code Mode but cannot supply the TypeScript runtime it needs; a deployment that composes none can compose no code-mode preset.