Skip to content

v1.18.0

Choose a tag to compare

@github-actions github-actions released this 22 Aug 11:39
· 14 commits to main since this release

Fixed

  • A compaction that settles after the session is gone no longer takes the process down (#108 by @heinrichI). The context watchdog reported its result through the ctx captured at the turn that fired the compaction, and pi invalidates the whole extension runtime on dispose(), so ctx.ui and pi.sendUserMessage() throw This extension ctx is stale after session replacement or reload from that point on. pi invokes the compaction callbacks from a floating void (async () => …)(), which turned that throw into an unhandled rejection and a hard exit 1. heinrichI hit it in a sub-coder, where the headless child disposes the moment the agent settles, so a compaction fired near the end of a run was racing teardown by construction. The UI handle is now captured while the ctx is known-good and every post-compaction use of it is best-effort: nothing the watchdog wants to tell you is worth crashing over.
  • "Already compacted" no longer halts the run (#109 by @guppy42, #91 by @charly1r, also reported by @cal101). pi's compact() reports through one promise with two outcomes, and the #68 loop guard treated every rejection as "compaction is futile, pause". Two of the three rejection shapes are not that. Already compacted means another compaction landed first and pi's prepareCompaction found the branch's last entry is a compaction: the context is compacted, only our call lost the race, so pausing there stranded a mid-task run at the prompt behind a "could not proceed" warning until the user typed "resume". Compaction cancelled means the run was aborted or the session is being torn down, which is not a failure to report at all. Outcomes are classified now: Already compacted resumes the run exactly like a successful compaction, a cancellation is silent and leaves the watchdog armed, and only a real failure (Nothing to compact (session too small), a provider error) still pauses.
  • A turn boundary during an in-flight compaction can no longer start a second one (found while fixing the above; no issue). The compacting flag was cleared at every before_agent_start so a lost callback could not wedge the watchdog off permanently. But pi's compact() aborts the run and reconnects the agent while it is still summarizing, which makes a genuine in-flight compaction one of those boundaries. Clearing the flag there let a second compact() fire on top of the first, and the one that lost is exactly the Already compacted above. An outstanding-call counter now means only a stale flag is dropped.
  • A /dev/null redirect butted straight up against a chain operator is no longer read as a file write (#107 by @ashalliants, a regression of #87). The write guard's word splitter broke on whitespace and redirects but not on control operators, so find … 2>/dev/null; find … parsed its target as /dev/null; (not a path the device exemption knows), and an ordinary two-find one-liner was refused as an unsafe write. ashalliants' diagnosis was exact, and the same hole was open for 2>/dev/null&&, ||, |, and a closing subshell paren, none of which need a space in front of them. ;, &, |, ( and ) now end a word like > and < already did, with fd duplication (2>&1, >&2, >&-) checked on the raw text so it is still recognised for what it is.

Changed

  • Approving a plan saves it; /implement runs it (#105 by @dcazrael, for #98 by @heinrichI). heinrichI's report was that planning fills the context window and the model then loops once implementation starts on top of it. It does, and the fix has to be a fresh session, which pi exposes only to command handlers (ExtensionCommandContext.newSession), never to the agent_end handler where Plan Mode asks for approval. So approval now persists the plan to .pi/approved-plan.md and stops; /implement reads it back, switches to the action model, replaces the session with one seeded with the plan as a hidden context entry, and starts the work. Moving the phase handover from approval to /implement is worth having on its own: on a single llama.cpp backend a handover evicts and reloads weights, so approving a plan you then decide to rewrite used to cost you a reload for nothing.
  • manual permission mode actually asks (#90 by @marek2901). It blocked exactly what auto blocks and differed only in the wording of the refusal, which is not what the name promises. It now shows the command and prompts before running it, whitelisted or not: in manual mode the user is the whitelist. write-guard still runs on top, so a confirmed cat > existing.py is still caught. A session with no UI (headless) refuses everything in this mode, which is the right default for a mode built around a human in the loop, and sub-coders are unaffected because they are pinned to auto.