github nicobailon/pi-subagents v0.78.0

13 hours ago

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.
  • Esc means stop. A background subagent that finishes after you press Esc no longer starts the parent up again, and /subagents-stop has a "stop all" row.
  • Fewer cache misses. Forks, staged resumes, and the resume after /compact reuse 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. activeRunsPrompt reminds the parent which background runs are still going, and pinToolDeclarations: false lets a long-running session pick up a changed subagent tool 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 Esc now keeps the parent agent stopped: a background subagent that finishes a moment later no longer starts it up again. /subagents-stop can also stop every running background subagent at once.
  • Fewer tokens spent re-reading the conversation: forked subagents, staged resumes, and the resume after /compact now 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 pinToolDeclarations config key. A long-running session keeps the subagent tool definition it started with, so an edited subagent-tool-description.md, a toolDescriptionMode change, or a package upgrade did not reach it until you started a new session. Set pinToolDeclarations: false (the default is true) 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 activeRunsPrompt config key, off by default. A parent agent heard about its background subagents only from completion notices or a status call, 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 Esc to 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-stop now 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 subagent tool also declares contact_supervisor, which returns an error if the parent calls it. A fork launched through subagent can now reuse the parent's prompt cache; one launched by a slash command before subagent is enabled still starts uncached. A session that already has subagent when you update may miss the prompt cache once, on its next request.
  • The built-in reviewer, oracle, and evidence-auditor agents are no longer asked to end with an acceptance report when the call has no acceptance, since that report is meant for agents that edit files. Pass acceptance to still get one. Without it these runs have no child report, so /subagents-refine no 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 existing run-history.jsonl from your Pi agent directory (~/.pi/agent/ or PI_CODING_AGENT_DIR). Thanks to @nycruslan for the report. (#2864)
  • The pi-subagents skill now says that worktree: true refuses 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 maxActiveAsyncRunsPerSession slot 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 after capacity.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 maxActiveAsyncRunsPerSession slot 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 toolBudget block was not reported as blocked. A delegated foreground run said completed instead of tool_budget_exhausted, a workflow child got no budget_exhausted outcome, 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 toolBudget state reported outcome: "hard-blocked" as soon as the tool count passed hard, even when block listed only some tools and none had been blocked. It now reports hard-reached until a call is actually blocked, then hard-blocked with blockedTool. 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 /compact with 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 }) without output reused the original run's output path, so the revived run overwrote the earlier report. A resume without its own output, including a steer that revives a finished child, now saves to a new file next to the original, named with the new run id (for example report.1a2b3c4d.md), and its completion notice names that file. An explicit output still 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-only output 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 required file-only output 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 status showed only Subagent 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 /subagents model and thinking pickers. Such an app can now set PI_SUBAGENTS_COMPONENT_UI=1 to 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 args printed a workflowScript contains dynamic child launches warning 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 under warnings. 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, or Promise.resolve failed with a "Proxy handler's 'get' result …" error on Pi builds that freeze Promise, such as pi-bolt. They now work there. Thanks to @albertgwo for #2828.
  • Stopping a background subagent with subagent({ action: "stop" }) produced a notice saying Subagent 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 say Subagent stopped., Placed external run stopped., or Workflow 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 EPERM errors seen on Windows, it wrote the same stack trace to runner.stderr.log on 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 enum or const got 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 long enum. Thanks to @rtbe for the report. (#2813)

Don't miss a new pi-subagents release

NewReleases is sending notifications on new releases.