pi-subagents 0.78.0 gets background subagents running on Pi 1.2.0 again; on 0.77.0 they failed to start there. Stopping the parent with Esc now keeps it stopped, and /subagents-stop can stop every running background subagent at once. Forked subagents, staged resumes, and the resume after /compact reuse the provider's prompt cache instead of re-reading the whole conversation. Resumed runs no longer overwrite earlier reports, and sessions that run workflows no longer run out of background slots.
Highlights
- Pi 1.2.0 works. Background subagents start on Pi 1.2.0 again.
Escmeans stop. A background subagent that finishes after you pressEscno longer starts the parent up again, and/subagents-stophas a "stop all" row.- Fewer cache misses. Forks, staged resumes, and the resume after
/compactreuse the prompt cache. - No lost reports. A resume saves a new report next to the old one, and a timed-out child's partial output reaches the parent.
- Two new options.
activeRunsPromptreminds the parent which background runs are still going, andpinToolDeclarations: falselets a long-running session pick up a changedsubagenttool description.
Need to know
| Who | What to do |
|---|---|
| Apps that run Pi in RPC mode and draw custom screens (such as pi-web) | Set PI_SUBAGENTS_COMPONENT_UI=1 to get the full /subagents-fleet, /subagents-stop, and picker screens.
|
Callers that relied on reviewer, oracle, or evidence-auditor acceptance reports
| Pass acceptance to still get one; without it those runs no longer write the report.
|
| Everyone | run-history.jsonl is no longer written. You can delete the old file from your Pi agent directory.
|
Highlights
- Background subagents work on Pi 1.2.0 again. With 0.77.0 they failed to start on that Pi version.
- Pressing
Escnow keeps the parent agent stopped: a background subagent that finishes a moment later no longer starts it up again./subagents-stopcan also stop every running background subagent at once. - Fewer tokens spent re-reading the conversation: forked subagents, staged resumes, and the resume after
/compactnow reuse the provider's prompt cache. - Reports are no longer lost. Resuming a run saves a new report next to the old one instead of overwriting it, and a timed-out child's partial output now reaches the parent.
- Sessions that run workflows no longer run out of background run slots after a child is stopped or fails to launch.
Added
- New
pinToolDeclarationsconfig key. A long-running session keeps thesubagenttool definition it started with, so an editedsubagent-tool-description.md, atoolDescriptionModechange, or a package upgrade did not reach it until you started a new session. SetpinToolDeclarations: false(the default istrue) and a resumed session picks up the current definitions on its next restart, at the cost of one prompt-cache miss. Thanks to @advaitpaliwal for #2853. (#2852) - New
activeRunsPromptconfig key, off by default. A parent agent heard about its background subagents only from completion notices or astatuscall, so a few turns later it could forget one was running, launch the same work again, or say nothing was running. With this on, the parent's system prompt lists each queued or running background run (id, agents, mode, and chain step) until the last one finishes. Thanks to @advaitpaliwal for the request. (#2831)
Changed
- After you press
Escto stop the parent, a background subagent that finishes or asks for input no longer starts a new parent turn, so the agent you just stopped stays stopped. Its result or request is added to the session, and the parent sees it with your next prompt. A completion notice added this way tells the parent not to resume the stopped task unless you ask. Background runs keep running, and the TUI shows how many are still active and points to/subagents-stop. /subagents-stopnow starts with a "stop all" row when two or more background runs from this session are active, so you can stop them all after one confirmation instead of one at a time. Scheduled runs are not paused.- A forked subagent's first request re-sent the parent's whole conversation without the prompt cache, because the child's tools and history differed from the parent's. A forked child now declares the same tools as its parent (a tool it may not use fails with "not found" if called) and inherits the history exactly as the parent sent it. A session with the
subagenttool also declarescontact_supervisor, which returns an error if the parent calls it. A fork launched throughsubagentcan now reuse the parent's prompt cache; one launched by a slash command beforesubagentis enabled still starts uncached. A session that already hassubagentwhen you update may miss the prompt cache once, on its next request. - The built-in
reviewer,oracle, andevidence-auditoragents are no longer asked to end with an acceptance report when the call has noacceptance, since that report is meant for agents that edit files. Passacceptanceto still get one. Without it these runs have no child report, so/subagents-refineno longer sees their review findings. Thanks to @8FAX for finding it. (#2865) - pi-subagents no longer writes
run-history.jsonl. The file got one line per run, grew without limit, and nothing read it. You can delete an existingrun-history.jsonlfrom your Pi agent directory (~/.pi/agent/orPI_CODING_AGENT_DIR). Thanks to @nycruslan for the report. (#2864) - The pi-subagents skill now says that
worktree: truerefuses to launch from a checkout with uncommitted or untracked changes, and what to do in a shared or dirty checkout. Agents used to learn this only from a failed launch. Thanks to @jmclaughlin724 for the report. (#2851)
Fixed
- Background subagents failed to start on Pi 1.2.0 with "does not provide @earendil-works/chord, @earendil-works/chord/context". Pi 1.2.0 no longer ships that package, but pi-subagents required it on every Pi from 0.85 on. It is now required only when the installed Pi declares it. Thanks to @tlitovsk and @amchen2310 for the reports, and to @ArdalanM for the fix. (#2854, #2855)
- A finished workflow kept its
maxActiveAsyncRunsPerSessionslot for the rest of the session, so later launches were rejected, when one of its background children had been stopped or had finished after the Pi process that launched it exited. Such a workflow is now released aftercapacity.abandonedSlotReleaseAfterMs, the same as one whose child failed. Top-level runs still release this way only when they failed. Thanks to @nycruslan and @builtroller for the reports. (#2863) - An async workflow kept its
maxActiveAsyncRunsPerSessionslot for as long as its session lived when one of its children failed to launch, for example because of an unknown model. The slot is now released once the workflow ends. Thanks to @nycruslan for the report and the fix. (#2856) - A child stopped by a hard
toolBudgetblock was not reported as blocked. A delegated foreground run saidcompletedinstead oftool_budget_exhausted, a workflow child got nobudget_exhaustedoutcome, a background run's live status never showed the block, and the notice about a child repeatedly failing to change files never appeared. Tool results are now read the way Pi reports them. Thanks to @adameq for the report and the diagnosis. (#2846) - A
toolBudgetstate reportedoutcome: "hard-blocked"as soon as the tool count passedhard, even whenblocklisted only some tools and none had been blocked. It now reportshard-reacheduntil a call is actually blocked, thenhard-blockedwithblockedTool. Thanks to @adameq for spotting it. (#2866) - A child resumed for a later stage with a new output path re-sent its whole conversation without the prompt cache, because the output path was part of its system prompt. Through
pi-claude-bridge, one staged run re-cached about 1.65M tokens this way. The output path now goes only in the task message, so the system prompt stays the same across stages. - After a manual
/compactwith background subagents still running, the parent's next turn went out without the prompt sections that extensions add at the start of a turn, so the provider re-read the whole conversation without its prompt cache. Pi reports the compaction before it is ready for a new turn (earendil-works/pi#5581). The resume now waits until the parent is idle, and it is dropped if another turn starts first, the session ends, or the parent is still busy after 10 seconds. Thanks to @rnavarro for #2812. subagent({ action: "resume", id, message })withoutoutputreused the original run's output path, so the revived run overwrote the earlier report. A resume without its ownoutput, including asteerthat revives a finished child, now saves to a new file next to the original, named with the new run id (for examplereport.1a2b3c4d.md), and its completion notice names that file. An explicitoutputstill wins, and resumes inside a workflow script still use the workflow's own output paths. (#2837)- A workflow stage that resumed a run started outside the workflow, without its own
output, wrote its report over that run's saved report. It now writes to its own default output path, so the earlier report is kept. (#2839) - A background run with a required
file-onlyoutput and valid structured output failed with "Required file-only output was not produced" when its final message held only an acceptance report, and the JSON was never written. It is now written. If a requiredfile-onlyoutput cannot be written, for example because of a permission error, the run now fails instead of finishing with the report inline. Thanks to @8FAX for the fix. (#2850) - When a background child timed out mid-reply, the completion notice and
statusshowed onlySubagent timed out after …ms., so the parent never saw the text the child had already written. The notice now shows the first 2,000 characters of that partial output and the path of the file with the full text, followed by the child's recovery summary. Thanks to @advaitpaliwal for #2830. (#2829) - Since 0.77.0, apps that run Pi in RPC mode and can draw custom screens, such as pi-web, got the plain-text version of
/subagents-fleet,/subagents-stop, and the/subagentsmodel and thinking pickers. Such an app can now setPI_SUBAGENTS_COMPONENT_UI=1to get the full screens; plain RPC keeps the text version. Thanks to @kushaldotdev for the report and the fix. (#2848) - Every workflow script that launched children in a loop or from its
argsprinted aworkflowScript contains dynamic child launcheswarning across Pi's input area on each run, although that is the normal way to write a fan-out. Launches no longer print it;subagent({ action: "validate" })still lists it underwarnings. Thanks to @builtroller for the report and the first fix, and to @VincentHanxiaoDu for removing it from launches. (#2835, #2847) - FleetView printed external-run problems, such as
Failed to inspect external jobs: …, straight over Pi's screen and repeated them on every refresh. In an interactive session each distinct message is now shown once as a TUI warning, and headless runs print it once. A malformed record is now named by its run id. (#2844) - Opening Pi in a directory whose path contains a non-breaking space logged
Failed to inspect external jobs: External-run snapshot sessionId must contain only display-safe text.on every refresh. Run and session ids may now contain any text except terminal control, bidirectional, and line-break characters. Thanks to @louisje for the report and the diagnosis. (#2841) - Workflow scripts that called
Promise.all,Promise.allSettled,Promise.race,Promise.any, orPromise.resolvefailed with a "Proxy handler's 'get' result …" error on Pi builds that freezePromise, such as pi-bolt. They now work there. Thanks to @albertgwo for #2828. - Stopping a background subagent with
subagent({ action: "stop" })produced a notice sayingSubagent stopped by user.even when the parent agent stopped it, so it read as if you had stepped in. Stop notices for background runs, external CLI runs, and workflow children now saySubagent stopped.,Placed external run stopped., orWorkflow child '<key>' stopped.(#2826) - When an extension in a background child switched its model, for example a model router acting on the first message, status and FleetView kept naming the launch model. In one session, 14 of 15 children ran on a different model than status showed. Status now names the model the child's replies came from. Reviving the child still starts it on the launch model. Thanks to @dodo-robot for the report and the fix. (#2824)
- A child launched on a virtual model registered with
pi.registerVirtualModel()failed with "model not found" when the extension that registers it was not loaded in the child, and the explanation suggested the model should have resolved. The explanation now says foreground children inherit providers but not virtual models, and names the fix,registerRequiredChildExtensions, next to the agent frontmatter options. Thanks to @toomanyopenfiles for the report. (#2820) - When a background run kept failing to read its control inbox, such as the
EPERMerrors seen on Windows, it wrote the same stack trace torunner.stderr.logon every retry and could fill gigabytes in minutes. A repeating error is now logged once, then once a minute with a repeat count. Retries continue, so queued requests still arrive once the inbox can be read. Thanks to @infectiousstupidity for the report. (#2811) - A child whose structured output used a value outside a schema's
enumorconstgot only "must be equal to one of the allowed values" or "must be equal to constant". The error now names the allowed values, up to 20 for a longenum. Thanks to @rtbe for the report. (#2813)