Repository navigation
v1.5.0
A reliability + UX release centered on the harness's intervention machinery. Issue #8 reproduced on 1.4.3 through a new mechanism, and chasing it down fixed a cluster of related symptoms: thinking never actually turning off after a budget breach, a spurious "empty response" nag after interrupts, and a noisy stack of warnings around every harness decision. Harness interventions now speak with one voice, and the thinking-budget cap is more generous.
Fixed
- Thinking-budget recovery no longer dies on a stale
pi(#8, second reproduction). The v1.0.0 fix deferred recovery (setThinkingLevel("off")+ the commit-to-an-implementation follow-up) to aturn_endhandler that ran, after asetImmediateyield, against the module-scopepi(ExtensionAPI). But the over-budgetctx.abort()makes pi'sagent_endrun auto-retry / auto-compaction (both enabled in.pi/settings.json;agent-session.js:761"compact before sending — catches aborted responses"), which replaces the session —dispose()→ExtensionRunner.invalidate()(agent-session.js:516) marks the capturedpistale. ThesetImmediateyield was exactly what let that replacement land before the deferred recovery, so the recovery touched a stalepiand threw ("This extension ctx is stale after session replacement or reload"). Net effect: thinking was never disabled (so the next step kept thinking) and the follow-up never reached the model (so the agent appeared to stop). The fix does the entire recovery synchronously insidemessage_update, beforectx.abort(), whilepiis still live — no deferred handler, nosetImmediate, nothing that can run against a stale reference. Thanks to the reporter on #8 for the minimal repro and the stale-ctxdiagnosis. - Thinking stays off across the forced restart turn. Even with recovery firing, the post-abort run could re-resolve the thinking level back to the profile default. A
forcedOfflatch now re-asserts"off"at the start of every turn from a budget breach until your next genuine prompt (theinputevent), at which point the level you actually had is restored — so a new task thinks normally and we don't leave thinking globally disabled. State is also cleared onsession_start(a new session //clearis a clean slate). - No more spurious "your previous response was empty" after an interrupt.
quality-monitorassessed everyturn_end, including turns the user interrupted with ESC or that the harness aborted (thinking-budget, turn-cap) — which carry partial/empty content andstopReason: "aborted". It then steered anempty_responsecorrection onto your next prompt. It now skipsstopReason: "aborted"turns entirely; genuinely-empty completed turns are still flagged. - Per-model profiles are no longer silently skipped on colon-style model ids.
benchmark-profilesprefix-matched model keys literally, so a hyphenated profile key (llamacpp/qwen3.6-35b-a3b) never matched a runtime id using a colon (llamacpp/qwen3.6:35b-a3b) and every such model fell back todefault_model_profile. Matching is now separator-insensitive (:≡-). - Existing files can no longer be silently overwritten via Write. pi ships a built-in
writetool that overwrites existing files (core/tools/write.js) and shadowed little-coder's custom guardedwrite, so the whole-file-rewrite guard the benchmark results depend on had stopped firing. The guard now runs on thetool_callevent — it catches whicheverwriteimplementation executes, normalizes the path in place, and blocks writes to existing files with a corrected Edit recipe (pi'sedittakesedits: [{oldText, newText}], notold_string/new_string).
Added
/clearcommand. Starts a fresh session as if little-coder were closed and relaunched — re-renders the banner, rebuilds the AGENTS.md/system-prompt context, and resets session-scoped extension state — viactx.newSession(). (pi's built-in equivalent is/new;/clearis the alias muscle-memory expects.)- One-line "harness intervention" UX. Every moment the scaffolding overrides or redirects the model — thinking-budget cap, write-guard redirect, turn-cap, finalize-warn, quality-monitor corrections, output-parser nudges — now surfaces a single, uniformly-worded line (
harness intervention: …) instead of each extension's own ad-hoc warning. Helper at.pi/extensions/_shared/intervention.ts. - pi's bare "Operation aborted" marker is suppressed. With harness interventions carrying their own line and a user ESC being self-evident, the stacked red marker was noise. pi is a normal dependency (not vendored), so this ships as an idempotent, dependency-free source patch (
scripts/patch-pi.mjs) applied onpostinstalland re-applied on every launch by the launcher — it self-heals if install scripts were skipped or pi was reinstalled, and fails safe: if a future pi changes that code the patch silently no-ops (you'd just see the marker again) rather than breaking install or launch. A test (scripts/patch-pi.test.mjs) fails loudly the moment the installed pi no longer matches, so a pi bump is a caught CI signal to refresh one string — never a silent regression.
Changed
- Thinking-budget cap raised 2048 → 4096 tokens across
default_model_profileand every per-model profile (theterminal_bench/gaiabenchmark overrides keep their tuned values). The hardcoded fallback in thethinking-budgetextension matches.
Notes for upgraders
- No CLI flag,
models.jsonshape, or per-model-profile schema changes. The only.pi/settings.jsonvalue change isthinking_budget(2048 → 4096); if you'd pinned it lower on purpose, re-set it in your own settings. - The custom
writetool thewrite-guardextension used to register is gone — writes go through pi's built-inwrite, guarded at thetool_callevent. If you depended on the old tool'sfile_patharg name in a fork, note pi's built-in usespath(both are accepted by the guard). - The pi source patch targets
@earendil-works/pi-coding-agent0.75.x. If you bundle a newer pi and the abort marker reappears, runnpx vitest run scripts/patch-pi.test.mjs— a failure tells you to refresh the find/replace inscripts/patch-pi.mjs.