description: "The workflow orchestration capability: run a model-written script that fans out subagents, for users and maintainers choosing or building on ctx.workflowEngine."
English | 中文
dsh-workflow runs a plain-JavaScript orchestration script and gives the caller a live run whose result resolves with the script's final JSON value. The script can fan out subagents with agent(), combine independent work with parallel() and pipeline(), and narrate progress with phase() and log(); agents normally drive this through the workflow tool from dsh-tool-workflow. A run is holder-owned: its result never rejects, cancellation and disposal are bounded, and every child is attributed to the invoking agent. The package ships no execution engine — dsh-workflow-worker-thread is the current one — so a different isolation strategy can replace it without changing what callers or the model see.
Run a workflow when a task decomposes into many independent pieces that one script should coordinate — an audit across many files, a migration, multi-angle research — and the model explicitly asks for workflow-style orchestration. For one or two delegations, prefer a plain subagent call.
The model reaches the capability through the workflow tool from dsh-tool-workflow, which owns the call schema and result envelope; the engine supplies the execution underneath. A tool call submits meta, script, and optional args and returns { runId, agentsStarted, result } when the run completes. The tool blocks the parent turn until the whole workflow settles, so the model sees one final outcome, never intermediate child messages.
An orchestration script is a plain JavaScript body (not TypeScript) that runs with top-level await and ends with return <json-value>. The meta identity block and any args arrive as plain JSON data — never evaluated code. During execution the script calls the provided hooks: agent(prompt, opts) starts one subagent and resolves with its final text or, with a schema, a validated structured value; parallel() and pipeline() combine independent work; phase() and log() narrate progress for observers.
// Script body — runs with top-level await, ends with a JSON return value:
const reviews = await parallel([
() => agent('Review src/a.ts for correctness'),
() => agent('Review src/b.ts for correctness'),
])
return { reviewed: reviews.length }
When the script settles, the run's result resolves with the returned value, the stop reason, and the number of children started. A script that returns nothing yields null.
Plugin consumers can start a run directly: ctx.workflowEngine.start({ script, meta, args?, parent, signal? }). parent attributes every child to the invoking agent; signal cancels the run when aborted. start() validates the meta block and parses the script before a run exists, so a malformed request fails immediately with a violation list.
A returned run exposes id, meta, result, cancel(reason?), and dispose(). The result never rejects: a script failure resolves with stopReason: 'error', cancellation with 'cancelled'. The caller owns the run — call dispose() on every path; it cancels remaining work and waits for script and children to settle within a bounded grace.
A script that does not parse, a malformed meta block, an unavailable provider route, or an unsupported per-run limit is rejected synchronously before a run exists; the workflow tool reports these as errors the model can correct from. During execution, hook misuse — bad arguments, unknown options, unsupported schemas, tripped caps — kills the script loudly rather than dissolving into a per-item null. An ordinary child failure is not an infrastructure error: agent() resolves null and the script decides how to handle it.
Read these pages when the package-level contract is not enough. They move from the shared workflow model to the current engine and the model-facing consumers.
Indirectly, through its consumer dsh-tool-workflow and a workflow engine, which render the parent tool result and the child-agent requests.
No direct invalidation; the named consumer and engine own any request-prefix changes.
These limits define what the capability does not yet support. They are current constraints, not a task backlog.
workflow() hook for recursive orchestration.