Two windows where settle() could read a live waiter count, mark the task reported (suppressing the completion notice), and then watch that waiter reject with 'wait aborted' delivering nothing — leaving the owning session with no terminal notification at all: - abort and settlement in the same tick, settle continuation ordered first: the waiter now un-counts itself SYNCHRONOUSLY inside onAbort (the finally decrement alone lands a microtask too late), so the settle path sees no live waiter and the notice fires; - abort landing after settlement but before the wait's resolve microtask: the wait now resolves and DELIVERS the terminal snapshot it owes instead of rejecting (settlement suppressed the notice on this waiter's behalf). Both windows pinned by deterministic tests that fail on the previous implementation; wait()'s abort contract updated in JSDoc/README/RFC.
tasks/ — background task capability family
The shared background-task runtime: ONE home for task ids, owner isolation, polling, cancellation, wait, and completion notification, so bash, subagents, and every future long-running tool expose the same model-facing habit instead of cloning a private task protocol each. Rationale and the full design: the background-task-runtime RFC.
| Package | ctx key | Role |
|---|---|---|
tasks (@deepseek-ai/dsh-tasks) |
ctx.tasks |
The registry service: branded <kind>-N ids, owner-fenced read/kill/wait/list, settlement bookkeeping, the awaited owner-cleanup path, and the attachSurface misconfiguration fence |
tool-tasks (@deepseek-ai/dsh-tool-tasks) |
— | The model-facing control surface: task_output, task_list, task_kill, the completion-notice injection, and the background-habit prompt section |
The split is the state/surface boundary: the registry holds task state (an HMR reload of any tool plugin never orphans or kills a running task), while the tool surface is stateless presentation. Producers (dsh-tool-bash, dsh-tool-subagent) hand their work to ctx.tasks.start (preflight, then the producer's starter, then an atomic commit) and keep their own execution concerns; whether a producer exposes run_in_background is that producer's own enableRunInBackground config, never rewritten by this family.