This release is about not losing work. When one session hands off to another in the same Pi process, the new session now owns the old one's background runs: it can see them, wait on them, steer, stop and resume them, and answer their questions. Coordinators can give a finished worker another round instead of starting over with a fresh child and a re-sent brief. Forked subagents and long sessions also spend fewer tokens, and updating pi-subagents mid-session no longer switches subagent off.
Highlights
- Handoffs keep your background work. A session that takes over from another one in the same process gets that session's background runs, and a stopped workflow can be picked up with
options: { reuseFrom: "<run id>" }without running its finished children again. - Resume a finished worker. Resuming a finished background child, a forked child, or a child that was already resumed once now works instead of failing with "session file is not under that nested run's session directory".
- Fewer wasted tokens. Forked subagents reuse the parent's prompt cache (a fork's first request on a 60k-token Claude parent went from 9.6k to 60.8k cached tokens),
/subagent-costand other view-only reports no longer ride along in every request, and codemode no longer breaks the cache on every resume. subagentsurvives updates. Updating or reloading pi-subagents mid-session no longer leavessubagentorbg_waitturned off.- RPC hosts see more. Each background child's agent, run id, requested model, and the model that actually answered.
Need to know
- Children launched before this update still can't be resumed by a coordinator. Background and forked children now keep their session in a folder named by their run id, so only children started on 0.79.0 benefit.
- Children that use Pi's built-in MCP tools need Pi 0.99.2 or later.
Changelog
Highlights
- Handoffs keep your background work. A session that takes over from another one in the same Pi process can see, wait on, steer, stop and resume the old session's background runs, and a stopped workflow can be picked up with
reuseFromwithout running its finished children again. - Coordinators can give a finished worker another round. Resuming a finished background child, a forked child, or a child that was already resumed once now works instead of failing with "session file is not under that nested run's session directory".
- Fewer wasted tokens. Forked subagents reuse the parent's prompt cache,
/subagent-costand other view-only reports no longer ride along in every request, and codemode no longer breaks the cache on every resume. subagentsurvives updates. Updating or reloading pi-subagents mid-session no longer leavessubagentorbg_waitturned off.- RPC hosts see more: each background child's agent, run id, requested model, and the model that actually answered.
Added
- Inspect replies (
/subagents-inspect-rpc) now includeresolvedModel: { provider, model }, the provider and model of the inspected child's latest reply. Before, a host could only see the requested model. The field is missing until the child replies, for a whole workflow or multi-step run, and for runs from before this release. A forked child's inherited history never counts. Thanks to @koenvg for reporting #2922. - The RPC
subagent-asyncstatus snapshot now includes each background step'smodel,thinkinglevel andagent. A workflow lane's label names the lane, not the agent, so hosts could not name a running child until it finished. Thanks to @agustinsacco for #2893 (#2892) and @kevinsleegerslameco for #2912 (#2911). - In a workflow that waits for its result, a background child's
workflowChildrenrow now carries itsagentandrunIdfrom launch, and thatrunIdmatches the child's run in thesubagent-asyncsnapshot. Before, the row had only{ childId, state }until the child finished. Thanks to @kevinsleegerslameco for reporting #2911. - After a handoff, a relaunched workflow can reuse the work of a run that was stopped because the old session's runtime was replaced: pass
options: { reuseFrom: "<run id>" }. Finished children are reused and running ones are awaited, even from another session, as long as the run ran the same script in the same cwd. Before, relaunching ran every child again, so two writers could share one output. The stop notice names the option and the run id. Thanks to @wireknight for reporting #2909. - A session that takes over from another one in the same Pi process, for example after a handoff to a new session file, now owns the old session's background runs. They show in its run list, fleet view and RPC snapshot. It can wait on, steer, stop, resume and inspect them, and it gets their notices and answers their supervisor questions.
statusshowsOwner: inherited from session '…' (replaced in this process).for these runs. A fresh start,pi --continue, or another Pi process inherits nothing. Thanks to @wireknight for reporting #2909.
Changed
- The pi-subagents skill now says that
subagent,subagents_enable,subagent_supervisor,contact_supervisorandstructured_outputcan't be called from codemode scripts. With codemode on, models used to trytools.subagent_supervisor(...)and get a "does not exist" error. Thanks to @CovetingEpiphany2152 for the report. (#2887) - Replying to a supervisor question that was already answered, expired or withdrawn now lists the questions still pending, so the parent can answer a newer one without another
pendingcall. Thanks to @agustinsacco for #2895. (#2894)
Fixed
- A coordinator could not resume its own finished background child:
resumefailed with "session file is not under that nested run's session directory", so it had to start a new child and send the brief again. The same happened for forked children (context: "fork") and for a child that had already been resumed once. All three now resume, with the same ownership checks as before. Background and forked children now keep their session in a folder named by their run id. Children launched before this update still can't be resumed. Thanks to @mannainsect for reporting #2924 and @GeorgeLautenschlager for showing it outside workflows. (#2932) - A coordinator child (one that delegates its own work) stayed stuck in
bg_waitwhen one of its children asked it a question withcontact_supervisor, so the questions expired unanswered. Itsbg_waitnow returns as soon as a question is waiting, as the parent's already does. Thanks to @mannainsect for reporting #2923. - A child's
contact_supervisorquestion could expire before the parent saw it when the parent was busy in a long tool call, so the child gave up and asked again and the parent's late reply failed. The question's deadline now moves forward until the parent's model sees it. Thanks to @agustinsacco for the report. (#2894) - When a handoff or reload stopped an async workflow, its still-running children were listed as
stopped, its notice didn't say that reusing finished children works only from the same session, andstatusoffered aRevive:command to sessions that can't use it. The children now show asrunning, the notice explains how the same session and another session can reuse them, andstatusoffers resume only to the session that launched the run. Thanks to @wireknight for reporting #2909. - After pi-subagents was updated or reloaded mid-session,
subagentstayed off once the session started again (/reloadorpi --continue), andsubagents_enablewasn't offered either, so nothing could turn it back on. Pi records the unload as a removal of the extension's tools, and that was replayed as if you had turnedsubagentoff. An unload is now recognized and the tools come back as you had them. Thanks to @bkdinoop for reporting #2910. - After such an update, a session continued in a new process (
pi --continue) also keptbg_waitoff, and a second update in that session lostsubagentagain. The tools the update removed now come back when the session starts, unless they are excluded for this process. (#2916) - Forked subagents now reuse the parent's prompt cache. A fork used to replace the parent's system prompt and tools, so its first request re-read the whole conversation. It now sends the parent's system prompt, tools and messages unchanged, and gets its own instructions in a
<subagent_instructions>block at the start of its task. A fork also sees the parent's project context and skills even when its agent turns them off. On Claude with a 60k-token parent, a fork's first request went from 9.6k cached and 52k written tokens to 60.8k cached and 1.7k written. See tool-reference.md for the forks that still miss the cache. Fresh children are unchanged. Thanks to @gabsn for #2879. /subagent-costand other view-only slash reports no longer go into every later model request. They are still saved, shown in the TUI and sent to RPC callers./runresults stay in context because the model needs them. Reports saved before this update stay until compaction, so updating doesn't break a session's prompt cache. Thanks to @isjiajia01 for #2927 and @summer-tt for the report. (#2925)- With Pi's codemode on, every start or resume of a session added another copy of codemode's note to the
bg_waitdescription, so the provider re-read the conversation without its prompt cache. The note now appears once and a resumed session keeps its tool declaration. Thanks to @SamuelBadr for the report. (#2889) - When a provider stopped a child with an error, such as a content-filter refusal, the child reported only the stop code. Its error now includes the provider's reply text as
Reply text: …(up to 500 characters), so a refusal can be told apart from an outage. In a failed workflow, each failed child's line also starts with its own error instead of theRun fan-out: N/64 usedline. Thanks to @stanleytelehealth for #2913. - A read-only child running
grep … 2>/dev/nullcould be flaggedneeds_attention: the redirect counted as a file change, and output containing words like "error" counted as a failure. Redirects to/dev/nullno longer count as changes, and a tool call counts as failed only when its result saysisError: true. Calls blocked by a hardtoolBudgetdon't count either. Thanks to @Liaokc for the fix and @builtroller for the report. (#2878, #2884) - A notice about a long-running
bashcall in a child suggestedcommand.status,command.yieldandcommand.canceleven when the child had no command controller, so following it failed with "No command controller is available for this child." The notice now suggests them only when they work. Thanks to @G0-0000 for #2882. (#2880) - On Pi 1.2 and later, the watchdog took the system prompt of a run started by an extension message (for example a wake sent with
triggerTurn: true) as your prompt and showed it to its reviewer model as a real requirement. These runs no longer add to the watchdog's scope, and their edits are still reviewed against your earlier prompts. Thanks to @isjiajia01 for #2928 and @sternnick for reporting #2926. - A child using built-in MCP tools failed after 10 seconds even when its server was still connecting normally. It now waits for the connection, fails promptly when a connection fails, and stops waiting when the child is stopped or times out. This needs Pi 0.99.2 or later. Thanks to @qsgy-edge for #2904.
- An extension that force-exits a headless run on quit could kill the background runner when one of its children ended, so the run failed with "Async runner process N exited with code 0 before writing a result". The runner now sets
PI_SUBAGENT_RUNNER_PIDto its own pid, so an extension can compare it withprocess.pidand leave the runner alone. Processes spawned from a child inherit the value but have their own pid. See extension-api.md. Thanks to @hughy for #2885. (#2883) - After Pi auto-compacted in the middle of a turn, the async and Fleet status widgets stayed hidden until the whole run ended. They now come back as soon as the compaction finishes. Thanks to @agustinsacco for #2891. (#2890)
- When a background subagent finished, the parent often logged
Failed to acknowledge the mission observer ... Timed out waiting for another process to finish updatingwith a stack trace and stalled for 200 ms. It now retries quietly a moment later. Thanks to @RainbowXie for the report. (#2896) - A finished mission run from an earlier Pi process in the same session was synced to its mission again every 60 seconds, rewriting the mission record each time. It is now synced once. Thanks to @BelovedYaoo for reporting #2905.