Files
deepseek-harness/packages/core/agent-tool-mode
Yichen Jiang 1cfbafab6a fix(preset): keep the token meter host-plane and name unjoined agents
Moving the agent plane behind presets left two readers on the wrong side of
the host/agent line.

`dsh-token-meter` was disabled on the host and mounted inside each preset's
`compaction` realm, but its three projection units register into the
process-wide `sessionProjections` table. A unit registered from one preset
answers for every session, so whether a `minimal` session showed a context
meter depended on whether some other session had mounted `standard` since
boot, and a process that only ever ran `minimal` showed none. The meter takes
no configuration, keys every fold by Session, and registers no tool or prompt
section, so it returns to the host composition and leaves the presets'
`isolate` map; the realm and `compact-basic` stay, because what a preset
chooses is whether its agent compacts, not whether its tokens are counted.

Nothing named an agent that joined no preset. The join is a scope-parent link,
and without it the tools, prompt-section, and skill views resolve the empty
global layer: the agent publishes, the turn runs, and the model receives
nothing. `AgentPresets` now logs one warning per such agent while a roster is
configured, and the invariant companion fails outright — at
`system-prompt/assemble` rather than at publication, because an unjoined agent
is legal until it addresses a model and `recompose` binds exactly such an
agent. The warning stays advisory: a synchronous `agent/created` throw vetoes
publication, and the ACP bridge, SDK server, and headless bundle all create an
unjoined agent today.

Three limits are recorded rather than fixed: projection key presence is not a
per-session capability signal, a superseded standing generation is never
reclaimed, and a `cordis_mount` temporary plugin belongs to the composition
rather than the session that mounted it.

Fixes #2203
2026-08-10 22:36:06 +08:00
..

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, native presents every tool schema.

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.