docs(tasks): condense background task prose
The background-task change repeated its lifecycle design across implemented RFCs, package READMEs, JSDoc, test commentary, and model-visible schemas. That repetition obscured the contracts that maintainers must preserve and added avoidable prompt tokens. Rewrite the implemented RFCs around the current design, keep authorization, exact-owner cleanup, wait/abort ordering, producer quiescence, and teardown-failure guarantees at their owning surfaces, and remove peer surveys, review history, control-flow narration, and emphatic restatement. Shorten the task and subagent schema wording, synchronize the bilingual tool cookbook, and regenerate the config, service, RFC, tool, and replay snapshot derivatives. Runtime behavior is unchanged; test edits update prose-only assertions and descriptions.
This commit is contained in:
@@ -1,63 +1,51 @@
|
||||
# @deepseek-ai/dsh-tool-subagent
|
||||
|
||||
The `subagent` tool lets the model delegate one self-contained task and collect the child's final output. It is a thin consumer of `ctx.subagents`; changing the configured provider changes the transport without changing the model-facing execution contract.
|
||||
The model-facing delegation tool over one configured `ctx.subagents` provider. Changing the provider changes transport without changing the execution contract.
|
||||
|
||||
## Provider selection
|
||||
## Provider selection and lifecycle
|
||||
|
||||
This plugin binds to **exactly one** provider (`Config.provider`). The model sees only `{ description, prompt, run_in_background? }` — there is no provider/type parameter in the schema. To expose more than one transport, load the plugin more than once, each bound to a different provider **and a distinct `toolName`** (the tool registry rejects a duplicate name, so a second load that kept the default `subagent` name would throw). Keeping selection in config (not the schema) is the deliberate split: the *service* holds a multi-provider registry; the *tool* picks one.
|
||||
Each plugin instance binds one `provider` to one `toolName`; the model receives no provider selector. Load another distinctly named instance to expose another transport. The tool registers only while its provider exists, avoiding sibling load-order and provider-reload dependencies. Its description follows `provider.inheritsParentContext`: fresh children require standalone prompts, while forked children already see completed parent turns.
|
||||
|
||||
The description is derived from `provider.inheritsParentContext`: spawn and ACP tell the model to provide a standalone prompt, while fork says the child already sees completed conversation turns. The plugin follows `subagent/provider-added` and `subagent/provider-removed`, so concurrent Cordis plugin loading does not create a registration-order dependency.
|
||||
A foreground call passes the execution signal through startup and execution, awaits `run.result`, and always awaits `run.dispose()` before returning. Only `completed` returns final text; abort, refusal, token limit, and other failures become errored tool results without partial output.
|
||||
|
||||
## Lifecycle
|
||||
With `run_in_background: true`, the tool registers the parent-owned task before starting the provider. A task-owned signal covers pending startup and the child after the starting call returns. `task_kill` and owner disposal abort it. Settlement awaits startup rollback or child disposal, then maps completed final text, abort to `killed`, and other failures to `failed`. The task has no incremental read; generic task tools own later status, collection, cancellation, and notices. See the [background subagent RFC](../../../docs/rfc/implemented/feature/2026-07-08-background-subagent-tasks.md).
|
||||
|
||||
Foreground `execute` passes the tool execution's abort signal when present, otherwise supplies an inert signal to satisfy the required `SubagentStartRequest.signal`. It awaits `ctx.subagents.start(...)`, then awaits `run.result` inside a `try/finally` that always calls `run.dispose()`. The selected signal therefore covers startup and live execution, while disposal guarantees quiescence on success, failure, and abort.
|
||||
|
||||
A non-`completed` stop reason becomes an `isError` tool result; partial child output is never reported as success. With `run_in_background`, an independent task-owned signal covers both asynchronous startup and the ready child, while collection moves to the generic task tools.
|
||||
`toolFilter` changes the child's global tool layer but is not a parent-derived authority ceiling. See the [agent-scope security non-goal](../../../docs/rfc/implemented/architecture/2026-07-08-agent-scope-contexts.md#security-and-authority-are-non-goals).
|
||||
|
||||
## Config
|
||||
|
||||
| Key | Meaning |
|
||||
|---|---|
|
||||
| `provider` (required) | The `ctx.subagents` provider name to start runs on (`spawn`, `fork`, `acp`, …). |
|
||||
| `toolName` | The model-facing tool name to register (default `subagent`). Set a distinct value per load when exposing multiple providers, e.g. `subagent` + `subagent_acp`. |
|
||||
| `enableRunInBackground` | Expose `run_in_background` in this instance's schema (default `true`). Disabled, the parameter is absent entirely AND a caller that forces the key anyway is refused at execution time (the arg validator allows undeclared keys) — delegation through this instance stays strictly synchronous. |
|
||||
| `agentOptions` | Default child agent options, currently including `model`. |
|
||||
| `provider` (required) | Provider name (`spawn`, `fork`, `acp`, ...). |
|
||||
| `toolName` | Model-facing name, default `subagent`; distinct for every loaded instance. |
|
||||
| `enableRunInBackground` | Exposes background mode, default `true`; disabling also rejects forced background calls. |
|
||||
| `agentOptions` | Default child options, currently including `model`. |
|
||||
| `persona` | Per-child persona; requires provider `persona` capability. |
|
||||
| `toolFilter` | Per-child global-tool restriction; requires provider `toolFilter` capability. |
|
||||
| `maxDepth` | Absolute delegation-depth cap; requires provider `depthLimit` capability. |
|
||||
|
||||
## Foreground lifecycle (synchronous collect)
|
||||
|
||||
`execute` awaits a ready run from the configured provider and then **awaits `run.result` inside a `try/finally` that always `dispose()`s the run** — the owned child agent/session is torn down on every path (success, error, abort), never leaked. The required request signal is the canonical cancellation path across startup and live execution. A non-`completed` stop reason (aborted/error/max-tokens/refusal) maps to an `isError` tool result rather than returning partial output as success.
|
||||
|
||||
## Background delegation (a generic task)
|
||||
|
||||
`run_in_background: true` refuses an already-aborted `exec.signal`, synchronously registers `{ kind: 'subagent', label: description, owner: parent, cancel, done }` with `ctx.tasks`, and returns `started background subagent task <id>`. The starter immediately calls async `ctx.subagents.start()` with an independent `AbortController`; `task_kill` and owner-scope teardown abort that signal whether startup is still pending or the child is ready. The task is final-output-only, and `done` settles only after startup rollback or `run.dispose()` reaches quiescence. Mapping: `runOutcome` turns `completed` into final output, `aborted` into `killed`, and other terminal reasons into `failed`; `settleRun` contains infrastructure and disposal failures. A missing task runtime fails loud. See the [background subagent tasks RFC](../../../docs/rfc/implemented/feature/2026-07-08-background-subagent-tasks.md).
|
||||
|
||||
`toolFilter` changes the child's visible global tool layer; it is not a parent-derived authority ceiling. See the [agent-scope security non-goal](../../../docs/rfc/implemented/architecture/2026-07-08-agent-scope-contexts.md#security-and-authority-are-non-goals).
|
||||
| `toolFilter` | Per-child global-tool restriction; requires `toolFilter` capability. |
|
||||
| `maxDepth` | Absolute delegation-depth cap; requires `depthLimit` capability. |
|
||||
|
||||
## Model Experience
|
||||
|
||||
### Tool schemas
|
||||
### Tool schema
|
||||
|
||||
**What the model sees**: While the configured provider exists, the model sees the generated default [`subagent` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent) under this instance's configured `toolName`. Fresh-context and inherited-context providers change the tool and `prompt` descriptions; `enableRunInBackground: true` adds `run_in_background` and its generic-task guidance.
|
||||
**What the model sees**: The generated default [`subagent` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent) under this instance's configured name while its provider exists. Provider context inheritance changes the tool and prompt descriptions; enabled background mode adds `run_in_background`.
|
||||
|
||||
**Token effect**: Fixed schema cost per parent request while mounted; each additional provider instance contributes one independently named schema.
|
||||
**Token effect**: Fixed schema cost per parent request; each provider instance adds one schema.
|
||||
|
||||
### Foreground result
|
||||
|
||||
**What the model sees**: The parent tool call retains the task description and prompt. Success contains only the child's data-dependent final text; non-completed stop reasons and infrastructure failures become `Error: <message>`. Intermediate child steps never enter the parent.
|
||||
**What the model sees**: The call retains the description and prompt. Success contains only the child's final text; other outcomes become `Error: <message>`. Intermediate child steps stay out of the parent.
|
||||
|
||||
**Token effect**: The prompt and final result remain in parent history until compaction; child working context is paid only in the child.
|
||||
**Token effect**: The prompt and result remain in parent history until compaction; child working context remains in the child.
|
||||
|
||||
### Background task result
|
||||
|
||||
**What the model sees**: Start returns exactly `started background subagent task <id>`. The generic task control surface owns later status, final output, cancellation responses, and completion notices; the child still contributes only its final text on successful collection.
|
||||
**What the model sees**: Start returns exactly `started background subagent task <id>`. The generic task surface provides later status, final output, cancellation responses, and notices.
|
||||
|
||||
**Token effect**: The start acknowledgement is small and retained. Final output and generic task status enter parent history only when collected or injected by `dsh-tool-tasks`.
|
||||
**Token effect**: The acknowledgement is retained; final output enters parent history only when collected or injected.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Background runs expose final output only** — intermediate child steps remain in the child session and cannot be streamed through `task_output`.
|
||||
- **Duplicate `toolName` across waiting loads is detected late** (`TODO(subagent-dup-toolname)`) — two instances waiting on providers collide only when a provider arrives; config-time detection needs a cross-fiber registry of intended names.
|
||||
- **Child policy is fixed per tool registration** — model, persona, tool filter, and depth cap come from plugin config; another policy requires another distinctly named tool.
|
||||
- **Background runs expose final output only** — intermediate child steps stay in the child session.
|
||||
- **Duplicate names across waiting instances are detected late** (`TODO(subagent-dup-toolname)`) — preventing provider-registration rollback requires a registry of intended names.
|
||||
- **Child policy is fixed per instance** — another model, persona, tool filter, or depth cap requires another distinctly named tool.
|
||||
|
||||
Reference in New Issue
Block a user