Problem
Every agent() call today spawns a fresh in-memory Pi session with no prior context. For fan-out patterns where N subagents need the same expensive setup — reading a large file, walking a directory, building a mental model of a codebase — each one redoes the work from scratch.
A fork() primitive would let one subagent do the setup once, then branch N children that inherit its full state (messages, tools, cwd, model) and diverge from there.
Proposed shape
const base = await agent('Read src/ and build a mental model of the architecture.', {
label: 'baseline',
fork: true, // keep session alive so it can be forked
})
const reviews = await parallel(
['security', 'performance', 'API design'].map(angle => () =>
base.fork(`Now review the codebase from a ${angle} perspective.`, {
label: `${angle} review`,
})
)
)
Each base.fork(prompt) call:
- Deep-clones the parent session (messages, tool state, settings).
- Appends the new user prompt.
- Runs to completion independently. Children do not see each other.
- Returns text or structured output, same contract as
agent().
The parent session is disposed once no live forks remain (or explicitly via base.dispose()).
Why
- Cache reuse. Anthropic/OpenAI prompt caching keys off message prefix; forks share the prefix verbatim, so the expensive setup is paid once.
- Less token waste. No need to serialize parent output back into N child prompts as context.
- Cleaner scripts. The pattern "do setup, then fan out N variations" stops requiring manual prompt stitching.
Open questions
- API surface:
agent(..., { fork: true }) returning a handle vs. a dedicated fork() global. Handle feels more natural but is a bigger departure from the current "results are plain values" model.
- Determinism: forking is deterministic w.r.t. the parent's final state, but parent + N forks running concurrently is not. Probably require the parent to settle before fork() can be called.
- Abort: aborting the workflow should cascade to all live forks.
- Memory: deep-cloning sessions could get heavy; may need to share immutable message arrays by reference.
Implementation sketch
WorkflowAgent.run currently disposes the session in finally. A fork-capable variant would keep the session object alive and expose a fork(prompt, opts) method that:
- Snapshots
session.messages and settings.
- Calls
createAgentSession with those as initial state (or clones the underlying session if pi-coding-agent exposes that).
- Runs as today.
Depends on what @mariozechner/pi-coding-agent exposes for session cloning — may need an upstream affordance.
Problem
Every
agent()call today spawns a fresh in-memory Pi session with no prior context. For fan-out patterns where N subagents need the same expensive setup — reading a large file, walking a directory, building a mental model of a codebase — each one redoes the work from scratch.A
fork()primitive would let one subagent do the setup once, then branch N children that inherit its full state (messages, tools, cwd, model) and diverge from there.Proposed shape
Each
base.fork(prompt)call:agent().The parent session is disposed once no live forks remain (or explicitly via
base.dispose()).Why
Open questions
agent(..., { fork: true })returning a handle vs. a dedicatedfork()global. Handle feels more natural but is a bigger departure from the current "results are plain values" model.Implementation sketch
WorkflowAgent.runcurrently disposes the session infinally. A fork-capable variant would keep the session object alive and expose afork(prompt, opts)method that:session.messagesand settings.createAgentSessionwith those as initial state (or clones the underlying session if pi-coding-agent exposes that).Depends on what
@mariozechner/pi-coding-agentexposes for session cloning — may need an upstream affordance.