# Conflicts: # docs/architecture.md # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/core-data-structures/bash.md # docs/event-producer-consumer.md # docs/rfc/implemented/feature/2026-07-06-sandbox.md # docs/rfc/proposed/architecture/2026-06-20-generic-long-running-tool-runtime.md # examples/AGENTS.md # examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl # examples/acp-agent/tests/snapshots/advanced-toolchain/system-prompt.golden.md # examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl # examples/acp-agent/tests/snapshots/both-mode-turn/system-prompt.golden.md # examples/acp-agent/tests/snapshots/code-mode-turn/system-prompt.golden.md # examples/acp-agent/tests/snapshots/permission-switching/system-prompt.golden.md # examples/acp-agent/tests/snapshots/skill-load/session.jsonl # examples/acp-agent/tests/snapshots/skill-load/system-prompt.golden.md # examples/acp-agent/tests/snapshots/text-turn/session.jsonl # examples/acp-agent/tests/snapshots/text-turn/system-prompt.golden.md # examples/coding-agent/cordis.yml # examples/sandbox-acp-agent/tests/snapshots/escalation-approved/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/escalation-rejected/session.jsonl # examples/sandbox-acp-agent/tests/snapshots/mode-switching/session.jsonl # packages/bash/bash-local/README.md # packages/bash/bash-local/src/index.ts # packages/bash/bash-local/tests/executor.spec.ts # packages/bash/bash-sandbox/README.md # packages/bash/bash-sandbox/src/index.ts # packages/bash/bash/README.md # packages/bash/bash/src/index.ts # packages/bash/bash/src/types.ts # packages/bash/tool-bash/README.md # packages/bash/tool-bash/src/index.ts # packages/bash/tool-bash/tests/tools.spec.ts # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-core/README.md # packages/core/agent-core/src/index.ts # packages/subagent/subagent/README.md # packages/subagent/tool-subagent/README.md # packages/subagent/tool-subagent/src/index.ts # packages/ui/acp-agent/README.md # packages/ui/acp/README.md # packages/ui/stdio-agent/README.md # packages/util/brand/src/index.ts # scripts/doc-budgets.manifest.json
4.3 KiB
@deepseek-ai/dsh-tool-tasks
The model-facing background task control surface over ctx.tasks: three kind-agnostic tools, the completion-notice injection, and the prompt section that teaches the background habit. Loading this plugin calls ctx.tasks.attachSurface('tool-tasks'), which is what arms producers' ctx.tasks.start().
Tools
task_output(task_id, wait?, timeout_ms?)— non-blocking read by default (stream kinds: the consuming delta since the previous read; final kinds: the final answer once terminal); every response ends with a[status: …]line (generic status + producer detail, e.g.[status: completed, exit code: 0]).wait: trueblocks until settlement, bounded bywaitTimeoutMs/maxWaitTimeoutMsconfig; a timed-out wait returns[status: running]and leaves the task alive.task_list()— the caller's tasks,<id> [<kind>] <status> — <label>per line.task_kill(task_id, reason?)— requests cancellation and returns immediately; the loggedreasonis forwarded to the producer. An already-terminal task is described via a non-consuming snapshot (never eats a pending delta).
ACP render intent: all three are generic cards (read/read/execute) — a task read is not a terminal.
Completion notices
On onTaskDone, injects background task <id> (<kind>: <label>) finished [status: …]. Read its output with task_output. into the owning agent's session (agent.inject() — durable context for the next request, not a wake-up). Suppressed when the snapshot is reported (the model already killed it, or a read/wait returned the end) — never a redundant "finished". The disposed-owner race is contained; a missing agent registry drops the notice.
Config
| key | default | meaning |
|---|---|---|
waitTimeoutMs |
30000 |
wait duration when task_output sets wait without timeout_ms |
maxWaitTimeoutMs |
600000 |
hard cap; larger model-supplied timeout_ms values are clamped |
A config whose default exceeds the cap fails loud at load.
Model Experience
System prompt
What the model sees: Every request in this plugin's registration scope contains the background-task guidance below. Agent-scoped tool filtering can hide the control schemas without removing this independently registered section.
Token effect: Small fixed input cost per request while the plugin is active.
Background-task guidance
Track every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.
Tool schemas
What the model sees: The model sees the generated task_output, task_list, and task_kill schemas while this control surface is visible.
Token effect: Fixed schema cost on each request where the tools are visible.
Task results and notices
What the model sees: Reads return a producer-owned output delta or (no new output), followed by [status: <status>] with optional producer detail. Listing returns (no background tasks) or one <id> [<kind>] <status> — <label> line per visible task. Kill returns requested cancellation of task <id> or task <id> had already finished [status: ...]. An unreported owned completion injects exactly background task <id> (<kind>: <label>) finished [status: ...]. Read its output with task_output.
Token effect: Results and completion notices are retained in the parent session until compaction; stream reads consume their cursor and do not repeat prior output.
Known Limitations and Deferred Work
- Completion notices do not wake idle agents — they become durable context for the next request; callers needing an immediate result must use
task_output. - Stream reads are single-consumer — this control surface exposes the task runtime's one consuming cursor rather than independent observers.
- Unowned tasks have no session fence — deployments exposing background starts outside an agent must provide their own caller policy or avoid ownerless tasks.