[FEATURE] Expose typed per-agent cancellation handles in dynamic workflow JavaScript
Summary
Dynamic workflows expose agent() as an awaited result but do not expose a programmatic handle for inspecting or cancelling that specific in-flight agent. The interactive /workflows UI can stop and restart a selected agent, and TaskStop can stop background agents, so the runtime already has the underlying identity and cancellation capability.
Please expose equivalent typed control to workflow JavaScript.
Verified current behavior
Environment:
- Claude Code 2.1.220
- macOS
- first-party
claude-opus-5 - two parallel agents in one native dynamic workflow
I ran a disposable workflow with two concurrent agents:
stop-targetattempted a marked Bash call.control-peerransleep 2; printf 'ORCHESTRATOR_SCOPE_CONTROL_OK\n'.- A
PreToolUsehook returned:
{
"continue": false,
"stopReason": "Disposable scope probe stopped the target agent."
}
The hook payload correctly included the target workflow agent_id. The target agent stopped before its Bash command, while the already-running peer completed normally and returned ORCHESTRATOR_SCOPE_CONTROL_OK. This confirms that hook cancellation is scoped to the triggering workflow agent in this runtime.
However, Promise.allSettled() reported the stopped agent as fulfilled with an empty string, not rejected or cancelled:
{
"agent": "stop-target",
"status": "fulfilled",
"value": "",
"reason": null
}
That makes a deliberate cancellation indistinguishable from a legitimate empty result unless every workflow adds an out-of-band control ledger.
Missing capability
A workflow cannot currently:
- obtain the native
agent_idwhen launchingagent(); - inspect typed state or
lastProgressAtwhile the call is in flight; - cancel one in-flight
agent()from workflow policy code; - distinguish cancellation from a successful empty return value.
Hooks are not a full substitute because they only run at hook boundaries. They cannot asynchronously interrupt an agent that is currently waiting on a model response and has not reached another tool event.
Requested API
Either an agent handle:
const worker = agent.start(prompt, options)
worker.agentId
worker.status
worker.lastProgressAt
await worker.cancel({ reason: 'no progress for 10 minutes' })
const result = await worker.result
or AbortSignal support:
const controller = new AbortController()
const result = agent(prompt, { ...options, signal: controller.signal })
controller.abort('no progress')
await result
Cancellation should reject with a typed error such as WorkflowAgentCancelledError containing agentId and reason. It should preserve parallel peers and already-cached workflow results, matching the current /workflows selected-agent stop behavior.
Why this matters
Long-running research, security review, and adversarial verification workflows need deterministic no-progress policies. A typed cancellation primitive would let saved workflows implement watchdogs without creating a second scheduler or wrapping native Claude agents in external processes, preserving native workflow visibility, pause/resume, token telemetry, and cached progress.