[FEATURE] Workflow scripts: a journaled run() hook so a deterministic command needs no agent
Feature request: let a workflow script run a command without spawning an agent
Problem
A dynamic workflow script has "no direct filesystem or shell access from the workflow itself". Agents read, write and run commands, and the script only coordinates them. So when a stage only needs a deterministic command's output (git diff --name-only, a repo CLI that prints JSON, gh pr view --json …), the script has to spawn an agent() whose whole job is to run that one command and echo its stdout back as structured output.
In our repo that pattern accounts for a large share of all workflow agent spawns. Each one:
- Costs a model session. Even a lean subagent definition costs about 4.8K prompt tokens before it reads its brief, and a default subagent about 49K.
- Adds latency. A cold agent boot sits in front of a command that takes milliseconds.
- Can be wrong. The model may reformat, summarize, truncate or skip the output, or act on text in it. We had to build per-stage fidelity checks that diff each agent's return against the command's own stdout in the transcript, just to trust these stages.
What we do today
We run what we can before launch and pass the output in through args, and split workflows at mid-run commands. That works for preflight commands, but a command that depends on an earlier stage's result still needs an agent or a second workflow.
Proposal
Add a script hook, for example:
const out = await run(['node', 'scripts/list.js', '--json'], { cwd, timeoutMs, stdin })
// → { exitCode, stdout, stderr }
- Journaled like
agent()returns, so a relaunch replays the recorded result rather than re-executing. That keeps resume deterministic, which we assume is why shell access was left out. - Permission-checked like a Bash tool call, with the session's allow and deny rules, PreToolUse hooks, sandboxing and auto-mode classifier applying exactly as they do to an agent's Bash call.
argvarray only, with no shell string, so interpolated data cannot become shell syntax.
A read-only file read (readFile(path)) under the same rules would cover the other common echo stage.
Related
anthropics/claude-code#100380 asks for agent() to return a typed death cause instead of null.