@firespoon/pi-antigravity-bridge 0.0.0-stage → 1.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +908 -0
  2. package/LICENSE +21 -0
  3. package/README.md +235 -3
  4. package/extensions/index.ts +1993 -0
  5. package/package.json +71 -6
  6. package/src/acp/connection.ts +623 -0
  7. package/src/acp/driver.ts +953 -0
  8. package/src/acp/events.ts +331 -0
  9. package/src/acp/home.ts +238 -0
  10. package/src/acp/jsonrpc.ts +232 -0
  11. package/src/acp/setup.ts +107 -0
  12. package/src/acp/usage-estimate.ts +61 -0
  13. package/src/agents.ts +64 -0
  14. package/src/agy-install.ts +85 -0
  15. package/src/agy-paths.ts +30 -0
  16. package/src/agy-setup.ts +72 -0
  17. package/src/agy-version.ts +283 -0
  18. package/src/approval-detect.ts +146 -0
  19. package/src/approval-gate.ts +208 -0
  20. package/src/approval-hook.ts +270 -0
  21. package/src/artifacts-ui.ts +88 -0
  22. package/src/artifacts.ts +144 -0
  23. package/src/ask-tool.ts +1222 -0
  24. package/src/background-watch.ts +225 -0
  25. package/src/bridge-catalog.ts +27 -0
  26. package/src/config.ts +485 -0
  27. package/src/daily-log.ts +196 -0
  28. package/src/dialog-lock.ts +48 -0
  29. package/src/diff-render.ts +205 -0
  30. package/src/driver-types.ts +172 -0
  31. package/src/driver.ts +907 -0
  32. package/src/engine-picker.ts +160 -0
  33. package/src/frame-guard.ts +38 -0
  34. package/src/mcp-registration.ts +382 -0
  35. package/src/mcp-server.ts +620 -0
  36. package/src/models.ts +407 -0
  37. package/src/native-tools.ts +104 -0
  38. package/src/parked-turn.ts +125 -0
  39. package/src/patch-cleanup.ts +519 -0
  40. package/src/poll-fold.ts +55 -0
  41. package/src/prompt-images.ts +92 -0
  42. package/src/provider.ts +2080 -0
  43. package/src/queued-messages.ts +152 -0
  44. package/src/redact.ts +27 -0
  45. package/src/sessions.ts +185 -0
  46. package/src/skills.ts +192 -0
  47. package/src/stream-events.ts +122 -0
  48. package/src/stream-sanitizer.ts +180 -0
  49. package/src/subagent-roster.ts +245 -0
  50. package/src/sysprompt-agent.ts +163 -0
  51. package/src/tasks-ui.ts +90 -0
  52. package/src/tasks.ts +174 -0
  53. package/src/usage.ts +174 -0
  54. package/src/web-tools.ts +320 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,908 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.10.8] - 2026-11-15
6
+
7
+ ### Fixed
8
+
9
+ - **Pi default identity prompt compatibility with `agy` turns.** When `systemPrompt` is enabled, `agy` turns triggered repeated `RESOURCE_EXHAUSTED` (429) responses when sent Pi's unmodified default leading identity sentence (`You are a coding assistant running inside pi, a coding agent harness.`). Both the ACP carrier path and the `stream-json` path (staged carrier and inline fallback) now replace only that exact leading sentence with `You are a coding assistant.`, preserving all subsequent system instructions, `AGENTS.md` context, skills, and the live MCP tool catalog unchanged. Custom system prompts without that exact leading sentence and `systemPrompt: false` behavior are unaffected.
10
+
11
+ ## [1.10.7] - 2026-11-15
12
+
13
+ ### Changed
14
+
15
+ - **ACP provider runs as a generated restricted custom agent with private MCP frontmatter, live tool catalog, and Pi system prompt in the agent body.** `agy acp` spawns with `--agent generated` and `--include-default-customizations=false` (`excludeDefaultComponents: true`, `tools: []`, `inheritMcp: true`, `inheritCustomizations: false`, empty `skills`/`rules`/`agents`/`hooks`/`plugins`), declares an explicit list-of-objects `pi-bridge` MCP spec for the current session in the private agent frontmatter (no foreign entries; `session/new`/`session/load` `mcpServers` stays in place and global MCP config stays untouched), and places a concise live MCP tool catalog + JSON Schemas + `call_mcp_tool` calling convention alongside Pi's `systemPrompt` in the agent body instead of the first ACP user message (`systemPrompt: false` omits Pi text only while retaining the bridge protocol, tool catalog, and restricted profile). Component exclusion removes workspace native file/shell tools but does not make agy a bare model: foundational `call_mcp_tool` (required MCP dispatcher), `list_resources`, `read_resource`, and `manage_task` remain in the runtime, while Pi provides file/shell/user tools via `ToolRoundTrips` (`toolUse -> Pi executes -> toolResult completes MCP`). ACP uses a new versioned session key so old conversations are preserved and not reused automatically — start a new Pi conversation to use the new profile. Native ACP step rendering stays in place as event compatibility, and `stream-json` remains the fallback engine with native tools, staged Pi carrier, and `/agy agent` (stream-json only).
16
+ - **`bridgeTools: "all"` now includes active Pi builtin tools.** Removed the `sourceInfo.source !== "builtin"` exclusion in `bridgedPiTools` while preserving active-set, `/agy tools` hidden-set, and internal/self-recursion filters. `bridgeTools: "mcp"` remains limited to active `pi-mcp-adapter` tools (+ skills), and `bridgeTools: "none"` disables the bridge — on the restricted ACP agent, `mcp` leaves no Pi builtins or non-adapter extensions and `none` bridges no Pi tools (leaving only agy's retained foundational runtime tools).
17
+
18
+ ## [1.10.6] - 2026-11-13
19
+
20
+ ### Fixed
21
+
22
+ - **stream-json sessions get the same cross-session MCP isolation ACP already had.** Every pi session wrote a `pi-bridge-<pid>` entry into the global `~/.gemini/config/mcp_config.json`, and agy hot-reloads the watched config, so ANY agy on the machine could discover and call ANY session's bridge: `tools/list` ignores turn state, so a foreign call hit an idle provider (fail-closed "no active antigravity turn") or parked inside another session's live turn (issue #14; thanks @leofranjav for the report and the `--gemini_dir` spike that proved the flag). The stream-json driver now stages a per-session MCP home - the global servers minus every foreign bridge, plus this session's own entry written ENABLED straight into the staged copy, because the delegation-suppression marker protects foreign readers of the global file and must never disable our own home - and spawns agy with the undocumented top-level `--gemini_dir <home>` flag. A quota-free capability probe (one `--gemini_dir <tmp> --version` spawn, memoized per binary) gates the path: flag refusal, probe timeout, or spawn failure falls back to the legacy global registration plus suppression with a one-time warning (the toast names which), a version above the live-verified floor (1.5.8) still isolates but logs an unverified-version notice (a parse probe cannot detect a flag that is accepted but ignored), and an `AGY_AGENTS_ROOT` sandbox override skips even the probe and keeps the legacy path (carriers stage outside the home's live agents view). If staging itself fails, the global registration is restored so the bridge stays reachable, and a bridge enabled after the home was staged writes its own entry straight into the staged copy. The home is removed only after the child's process GROUP is confirmed dead - leader death is not group death, and late agy subprocess writes would recreate a removed home; the session-start sweep accepts both engine prefixes, and a recycle restages so the next process picks up a fresh global-config snapshot. ACP behavior is unchanged. The stream-json driver now also honors `AGY_BIN` for its spawn (previously it always ran PATH `agy`; the probe, doctor, and ask-tool already resolved the override, so with it set the probe tested a different binary than the one that ran). Applies from the next pi process start. Live-verified 2026-11-13 on agy 1.5.8: a real agy turn under a staged home discovered the staged own entry, authenticated with the bridge token, and completed a full MCP `tools/call` round-trip while the global config stayed byte-identical.
23
+ - **stream-json carrier sessions have their write/edit/command tools back.** The staged system-prompt agent (`pi-bridge-sys-*`, the G10 carrier that keeps pi's system prompt off the ~25KB prompt-line cap) omitted the `tools:` frontmatter key on the assumption that omission keeps agy's full default toolset. agy reads an omitted `tools:` as a small read-only default set instead (official migrate-agent-settings guidance: always declare it), so carrier sessions could not `write_file`, `edit_file`, or `run_command` and reported only inspection tools (issue #13). The carrier now declares the productive toolset (`read_file`, `edit_file`, `write_file`, `run_command`, `manage_task`, `search_web`) in block style, the same shape the web delegates already prove agy parses; agy appends the resource/MCP tools itself, so bridge G9 round-trips are unaffected. Three tools that rode the broken read-only default (`generate_image`, `send_message`, `schedule`) are now undeclared: the first is marginal for a provider session (images ride the bridge), the latter two are subagent machinery the carrier masks anyway. Only the carrier changes - the web delegates keep their search-only `tools:` sandbox and the ask-tool reviewer keeps stripping everything, both intentional. Applies from the next pi process start: staged carriers are per-process dirs and old ones are pruned by the pid sweep; a conversation resumed across that restart gets the new agent name and the driver recycles on the drift.
24
+
25
+ ## [1.10.5] - 2026-11-13
26
+
27
+ ### Changed
28
+
29
+ - **The AskAntigravity default model is now `gemini-4-hydrogen` (was `flash`).** The default resolves as a catalog tiered base: the tier suffix comes from `AGY_DEFAULT_THINKING` or the call's thinking param (`gemini-4-hydrogen-medium` at the default medium), so the default no longer tracks the newest Flash generation. To make that resolution possible the shared resolver (`@estebanforge/pi-ask-shared`, also consumed by pi-ask-antigravity) now resolves a single-base catalog tiered slug BEFORE the bare-"gemini" Flash fallthrough — before, any unrecognized `gemini-<line>` base silently resolved as Flash; version pins across mixed bases (`gemini-3.6`) keep the Flash family semantics. Sequencing: publish shared first, then bump this package's shared floor to the version carrying that change and run npm install, then publish this package (a floor must point at a version npm has). The offline fallback catalog lists `gemini-4-hydrogen` first so the default stays resolvable when `agy models` fails, and the `/agy model` picker offers the exact slug alongside the aliases.
30
+
31
+ ## [1.10.4] - 2026-11-10
32
+
33
+ ### Fixed
34
+
35
+ - **ACP turns no longer report a multiplied context length that fired compaction at a fraction of the real occupancy.** agy's PromptResponse.usage sums input + cacheRead + output over EVERY inference of the prompt (billing totals), and pi reads `Usage.totalTokens` as the live context length, so each tool round-trip re-counted the whole cached prefix: an 18-step turn gauged 107% of the window at ~7% real, and pi's auto-compaction destroyed the session history for no reason (issue #12). The driver keeps the billing fields but replaces the total with the occupancy-shaped value pi expects: the observed `usage_update` occupancy when the server sends it (absent on native agy 1.5.4, probe-calibrated 2026-11-10), otherwise the derived Σinput + Σoutput + ceil(ΣcacheRead / (toolBatches + 1)) where toolBatches counts tool_call batches (a batch opens on the first tool_call after model output and closes on the next output frame, so parallel calls and out-of-order completions cannot inflate the divisor) - exact at one inference (the raw aggregate), and every miscount residual shrinks the divisor, which over-reads, the safe direction; calibrated within +11%..+57% of the real occupancy on live probe turns where the raw aggregate ran +99% (the reported 18-step sessions gauged 107-135% and would land in the low teens). `scripts/probe-acp-usage.mjs` recalibrates the formula against live frames.
36
+
37
+ ## [1.10.3] - 2026-11-10
38
+
39
+ ### Added
40
+
41
+ - **The Antigravity provider dialog and the /agy artifacts and tasks browsers report blocked-on-user-input while open.** Pi 1.5.1 lets overlay dialogs declare a blocked program status (OSC 7501); all three now pass it, so terminal automation watching the session sees pi waiting on input instead of idle. Pi dependency floor moved to ^1.5.1.
42
+
43
+ ## [1.10.2] - 2026-11-09
44
+
45
+ ### Added
46
+
47
+ - **agy-native background tasks now wake the model when they go quiet.** agy backgrounds shell work itself (`run_command` with `WaitMsBeforeAsync`), announces it, and ends its turn; its RPC never reports the task and a live probe (2026-11-09) confirmed it emits no post-turn `session/update` on completion either, so the model's announced follow-up plan never ran and the session sat idle until the user typed. Turns whose activities carry the `WaitMsBeforeAsync` shape now arm a filesystem watcher over the conversation's `.system_generated/tasks/` logs (turn-window filtered, so the cumulative dir cannot re-report older work). When every watched log goes quiet - no `lsof` holder and no bytes/mtime change across a 30s window - a custom `agy-background-task` message wakes the session (starts a turn when idle, queues as follow-up when busy). Honesty rules: the wake says "no writer observed", never "finished" (a reader holds fds too, and a log unlinked while held reads idle); tails ride the shared `UNTRUSTED_BANNER` and are secret-redacted; a watch that never saw a task log stops silently instead of noise-waking; a 30-minute cap wakes with gave-up wording instead of hanging. Armed/settled `pi.appendEntry` records survive module reloads (/new, /resume, /fork, /reload): `session_start` re-arms records without a settled twin, and the settled twin prevents any double wake. Requires `@estebanforge/pi-ask-shared` 0.1.4 (`UNTRUSTED_BANNER` export); publish shared first.
48
+
49
+ ## [1.10.1] - 2026-11-09
50
+
51
+ ### Fixed
52
+
53
+ - **Windows: the bridge now finds the installed `agy` CLI.** `isAgyInstalled` probed bare names with a plain `stat`, which never applies `PATHEXT`, so `agy.exe` was invisible on Windows and every start reported the CLI as missing (also looping the self-service installer, whose verify uses the same probe). Bare names on `PATH` now probe `agy.exe` only, the one Windows artifact Google ships: spawns run with `shell: false` and Node throws `EINVAL` on `.bat`/`.cmd` shims (CVE-2024-27980), so a `.cmd` wrapper does not count as installed, and neither does an extensionless file. A bare `AGY_BIN=agy.exe` finds the real file instead of probing `agy.exe.EXE`, and an `AGY_BIN` set with backslashes (`C:\...\agy.exe`) is treated as a file path instead of being mangled into a `PATH` segment (issue #11).
54
+ - **Windows and fresh CLI installs: ACP turns no longer abort on the CLI's empty `mcp_config.json`.** The agy CLI's initial state is a 0-byte `%USERPROFILE%\.gemini\config\mcp_config.json`; staging the isolated ACP home tolerated only a missing file and turned the empty file into `Cannot isolate inherited MCP config: Unexpected end of JSON input`, killing every turn. Empty and whitespace-only files now stage as `{ "mcpServers": {} }`; a leading UTF-8 BOM (a common Windows-editor artifact) is stripped instead of breaking the parse; a file with contents that are not a JSON object still fails closed (issue #11). The same tolerant read now covers stream-json bridge registration, which previously refused to touch an empty or BOM-prefixed file and silently skipped registering the bridge.
55
+
56
+ ### Changed
57
+
58
+ - **The legacy ACP binary override cleanup is now automatic, and the warning speaks plainly.** Startup strips the retired `acp.bin` key from `~/.pi/agent/antigravity-bridge/config.json` in place, keeping the rest of the file untouched (a symlinked config is written through, not replaced), so the usual case never shows a warning. Only a set `AGY_ACP_BIN` environment variable still warns (the bridge cannot edit shell config), with exact removal instructions. A failed config rewrite warns with the file path and the manual fix instead of failing silently.
59
+
60
+ ## [1.10.0] - 2026-11-08
61
+
62
+ ### Changed
63
+
64
+ - **Both engines now use only the agy CLI.** ACP turns spawn `agy acp` (agy 1.5.3+) and share the CLI's cached Google credentials. The standalone ACP server download, separate OAuth flow, `AGY_ACP_BIN`, `acp.bin`, and `/agy acp-bin` are removed. Startup deletes only retired builds with managed-install evidence under `~/.local/opt/agy-acp`; unmarked binaries and unrelated files stay untouched. Missing or outdated CLI installations trigger a confirmed official-installer flow on Linux, macOS, and Windows, followed by instructions to authenticate with `agy` and restart pi. Native ACP model/thinking options, exact usage, and notification-based cancellation are supported (issue #10).
65
+
66
+ ### Fixed
67
+
68
+ - **CLI setup cannot stall startup or report a failed download as success.** Unix installation uses `pipefail`, verifies agy afterward, and kills the whole installer process group on timeout. Prompts are deduplicated once per process, concurrent failures are contained, bad `AGY_BIN` overrides get correction instructions, and `/agy engine acp` checks the selected engine's CLI floor immediately.
69
+
70
+ - **Native ACP sessions cannot inherit another pi session's MCP bridge.** Each connection stages a private MCP home that filters all global bridge registrations while sharing CLI credentials, settings, and conversation state. Native tool-name parsing is restricted to known bridge prefixes, and fixed-family model slugs work without a thought-level option.
71
+
72
+ - **Retired-install cleanup handles interrupted downloads and concurrent starters.** Registry-named builds, partial unpacks, absolute or relative managed `current` links, and temporary archives are covered without touching unmarked files. Legacy binary overrides now produce a warning. Retired standalone credentials remain untouched and are identified by `/agy doctor` for manual removal. Both engines now write queued-message settings only to their shared CLI settings file.
73
+
74
+ - **Native ACP shutdown and cancellation no longer depend on teardown.** Staged homes live outside MCP cleanup and survive until agy exits, crash leftovers are swept by pid, and aborting a parked permission returns a cancelled outcome without killing the connection. Broken config symlinks are skipped, empty legacy binary defaults do not warn, and first-run setup checks the engine the user selected.
75
+
76
+ ## [1.9.3] - 2026-11-06
77
+
78
+ ### Fixed
79
+
80
+ - **A silently dying ACP server now reports its exit code and signal.** The exit handler received the child's code and signal but dropped them (hardcoded null), so a `.par` that died before answering initialize surfaced only as "process gone" with no cause (issue #9). The code and signal now reach the abort reason and the user-facing mid-turn failure, and the `connection-exited` log line records them alongside the stderr tail.
81
+
82
+ - **The registry's declared `distribution.binary.args` are now honored.** The installer read only `archive` and `cmd`, so a Linux-only arg like `--uid=` was silently dropped and the server never received it (issue #9). Args are validated as a flat string array, persisted to a sidecar `args.json` next to `zip.sha256` (so the already-installed early-return path recovers them), and resolved by path identity against the managed `<root>/current` binary: setup persists that path into `acp.bin`, so a source-label check would wrongly treat it as a user override on the next turn. Env and config overrides (a user shim such as `agy acp`) own their argv and get no registry args. Both `/agy auth` flows and the per-connection driver receive them.
83
+
84
+ - **A ToS-disabled Google account is detected at sign-in instead of on the first real prompt.** The credential probe used only `session/new`, which a disabled account passes, so it wrote a sticky verified marker and `/agy doctor` kept reporting healthy (issue #9). The probe now sends one bounded `session/prompt` and scans the streamed reply for the disablement notice (Google returns it as normal content, not a -32000 auth error). A disabled account fails auth immediately with appeal guidance and writes no marker; the probe is time-capped so a hollow or hung server fails rather than hanging `/agy auth`, and the reply head is logged so a change in Google's wording is diagnosable. The probe is scoped to the token-absent branch to avoid a model call on every normal browser sign-in.
85
+
86
+ ## [1.9.2] - 2026-11-02
87
+
88
+ ### Fixed
89
+ - The AskAntigravity tool's expand hint now comes from the render context pi injects (`context.keyHint`) instead of the package import. pi 1.5 blinds an imported `keyHint` to the host keybindings (app.* ids format as ""), so the hint rendered as "to expand" with no key. Peer floor moves to `^1.5.0` accordingly.
90
+
91
+ ## [1.9.1] - 2026-10-29
92
+
93
+ ### Fixed
94
+
95
+ - **A retry-dropped attempt no longer stalls the session with "No user message to send to agy."** When pi retries after a failed turn, pi core drops the failed attempt from the projected context (`_omitRecoveryAttempt`), leaving a `toolResult` or assistant tail with no user prompt, no pending park, and nothing late. The fresh-pass guard used to hard-error there, and pi does not retry that error, so an unattended session stalled for ~6 hours on it (issue #8). With a bound conversation, the provider now re-opens it with a synthetic resume prompt (telling agy the previous attempt was discarded and not to repeat completed actions) instead of erroring. The digest is skipped on resume turns, the guard stays for unbound conversations and empty transcripts, and the `no-user-message` turn error now logs at error level (it was debug, so the failure left no trace in the error-only daily log).
96
+
97
+ - **A first-ever turn that parks now binds its conversation immediately.** The binding used to be written only on a completed OK pass, so a first turn that parked a bridge call and then died (failed continuation, process restart) left no record, and the retry-dropped tail above still hit the hard error. A park is positive evidence that agy received the prompt, so the provider persists the snapshot's conversation id at park time (first binding only, staged agent moving with it). A later ERROR keeps the park-time binding, except the issue #2 signatures that prove the conversation dead (silent drop / deadline kill with no model output seen), which still clear it by design.
98
+
99
+ ### Changed
100
+
101
+ - **The stream-json overall cap now pauses on parks, matching the ACP engine.** `turnTimeoutMin` used to be armed once and cleared only at settle on stream-json, so parked pi-tool time and human-gated escalations consumed the turn cap (with a 15m cap, a 16m escalation died mid-turn). The cap now carries a wall-clock deadline plus a remaining-budget capture, pauses at the first park, and resumes with the remainder on unpark; a 0 cap stays disabled across park cycles. Turn durations on stream-json can now exceed `turnTimeoutMin` by the parked span. This makes the documented "escalated calls get their own 30-minute budget" claim true on both engines.
102
+
103
+ ### Known limits
104
+
105
+ - **A tool result whose tombstone died with the process is lost, not delivered.** Tombstones are in-memory. After a process restart, a retry-dropped tail whose tool result never reached agy resumes the conversation without that output; agy may repeat the call or proceed without it.
106
+
107
+ ## [1.9.0] - 2026-10-27
108
+
109
+ ### Fixed
110
+
111
+ - **`/agy-stop` no longer discards an answer that already finished computing.** Between the agy process closing and the run settling (conversation discovery + classification, up to ~500ms), a stop settled the run failed and silently dropped the computed answer. The stop handler now consults a finishing guard (the new opt-in `guard` option on pi-ask-shared's `createStopHandler`) and refuses with "already finished; its result is being delivered" while that window is open; the wake then delivers normally.
112
+
113
+ - **`details.stderr` is redacted at the boundary.** Tool-result details are persisted in session files, and raw stderr can carry auth material; the stored field now passes through the bridge's redactor. The blocking answer text stays raw, like every other ask tool. This rides the same change as the background feature, which introduced the background-run stderr wake path that made the asymmetry visible.
114
+
115
+ - **The suppression refcount survives `/reload`.** It lived in module state, so a runtime replacement reset it to zero and the old runtime's late release (its kill path is async) could re-enable the bridge's global entries while the new runtime's delegation was still live. It now lives in a `Symbol.for`-keyed `globalThis` slot, shared by every runtime instance in the process.
116
+
117
+ ### Changed
118
+
119
+ - **Refusal results render with the error glyph.** Refusal paths returned `exitCode: 0`, so pi showed "✓ AskAntigravity" for calls that never ran. They now carry `details.refused`, and `renderResult` flips to ✗. Details only; refusal text is unchanged.
120
+
121
+ ### Added
122
+
123
+ - **`AskAntigravity` supports background runs, 1:1 with the standalone pi-ask-antigravity extension.** The old `background: true` refusal is gone: the call returns a `bg_` runId at once and the full answer arrives as an UNTRUSTED-tagged wake message (customType `ask-antigravity-result`, secret-redacted through the bridge's redactor) when the detached `agy -p` tree settles, carrying the conversationId resume handle. New `/agy-stop [runId]` command kills a run by id prefix, or the only running one with no argument. Shared `pi-ask-shared` machinery gives the same semantics as every other ask tool: concurrency cap 4 with a visible refusal, settle-once so a stop and a natural close can never double-wake, TTL sweep, and `session_shutdown` killing in-flight runs silently (pi's `/new`, `/resume`, `/fork`, `/reload` wipe module state and take the runs with them). Background is refused in print/json mode, where the process exits before the wake could arrive. Two behavior fixes ride along: the MCP-suppression window now spans background runs too (execute() returns while agy is still alive, so the release moved into the run lifecycle and fires exactly once per run), and the per-conversation lock (one run per conversationId, since two runs resuming the same conversation interleave turns inside agy's SQLite state) now also covers blocking calls. The delegation path itself is unchanged and engine-independent: always a fresh one-shot `agy -p`, never the ACP or stream-json turn engine. Review hardening riding in the same change: the detached run now settles on stream close instead of process exit (an exit-time settle could truncate the answer and disarmed the SIGKILL escalation while group members were still dying), stdout/stderr buffers carry valves (1M/64k) so a runaway child cannot grow memory over a days-long timeout, the timeout wake's partial-output slice is redacted before cutting (a sliced secret can fall under the redactor's length thresholds), and the suppression release is exactly-once at the call site, not just inside the callee's refcount guard.
124
+
125
+ ## [1.8.8] - 2026-10-27
126
+
127
+ ### Fixed
128
+
129
+ - **Short-circuit observational-memory background turns instead of letting Gemini hunt the missing observer tool.** pi-observational-memory's turn-end observer falls back to the session model, and its prompt demands a private `record_observations` tool the bridge never sends; on antigravity models that spawned autonomous `find`/`grep`/`sed` shell sweeps (~500 ms per cycle) hunting the tool before reporting "observer found nothing new in this chunk". `streamSimple` now detects the observer context structurally (its `record_observations`/`record_reflections`/`drop_observations` tool names) and finalizes with a clean empty stop, which engages the observer's empty-backoff instead of erroring every turn; a one-time per-process `observational-memory-unsupported` warning explains the gap.
130
+
131
+ - **Strip Antigravity's echoed `<SYSTEM_MESSAGE>` notifications and default Queued Messages to send-immediately.** The backend injects async background-task notifications wrapped in `<SYSTEM_MESSAGE>` blocks, and Gemini (notably 3.8 Flash) echoes them verbatim as assistant text; a stateful streaming sanitizer (`src/stream-sanitizer.ts`) now suppresses the exact preamble+block pattern in both engines' text paths, failing open at EOF so legitimate text is never lost. Separately, the engines' default "Queued Messages" behavior buffered user input until turn end while pi sends interactive input mid-turn; the bridge now writes `"queuedMessages": "send-immediately"` to each engine's settings.json when unset (atomic, existing config respected, corrupt files never touched).
132
+
133
+ - **Deliver image pixels across all the paths that used to lose them.** Verified live 2026-10-22 (couch session): gemini-3.8-flash read a screenshot, the parked bridge call escalated, the model never polled `bridge_poll_result`, and the stranded redelivery carried text only — the image never reached it and the model fell back to hunting OCR libraries. Late/stranded redeliveries now carry image blocks via `driver.run` (ACP native prompt blocks; stream-json stages them), user-attached images on the stream-json engine stage to temp files with a prompt note pointing at the bridge read tool (the CLI turn input is text-only; `src/prompt-images.ts`), and both engines now advertise `text+image` input. The tool-priority note states outright that `read` returns visible images (models refused screenshots from priors) and mandates polling `bridge_poll_result` before other work after a STILL RUNNING ack. Staging failures degrade to an explicit "image(s) could not be delivered" note instead of dropping silently.
134
+
135
+ ## [1.8.7] - 2026-10-21
136
+
137
+ ### Changed
138
+
139
+ - **Consecutive `bridge_poll_result` polls collapse into one counted transcript line.** Each poll used to append its own display-only entry (observed live: 60 `Poll git_commit` entries from a single wait, flushed in bursts when the parked round-trip resolved). Poll events now fold into a single ♊︎-marked `agy-poll-run` entry per consecutive run (`♊︎ bridge_poll_result Poll git_commit (x30)`; expanded view shows the poll span) with a live dim status counter while a run is open. Runs close on a target switch, the next non-poll display event, turn end, and agent end. Bridge footer statuses (`agy-native`, `agy-usage`, the poll counter) now render muted, matching pi-hostname's dim treatment.
140
+
141
+ ## [1.8.6] - 2026-10-20
142
+
143
+ ### Changed
144
+
145
+ - **Declare native subagent masking explicitly in the staged system-prompt carrier (`subagent: false`, `agents: []`; issue #7).** Agent-file presence already suppresses the native subagent tools, but that was an accident of staging; the frontmatter now states it so provider sessions do not depend on an undocumented engine behavior (probe attribution of presence vs keys is untested). Sessions without the carrier (`systemPrompt: false` or a custom agent) keep native tools; the tool-priority note steers the model to bridge delegation where a delegation tool is listed.
146
+
147
+ ## [1.8.5] - 2026-10-20
148
+
149
+ ### Fixed
150
+
151
+ - **Skip `call_mcp_tool` replay and guard synthetic `antigravity` toolUse against inactive whitelist in subagents (issue #6).** In subagent sessions with strict tool whitelists, unconditional wrap replay caused 39 consecutive `Tool antigravity not found` errors. In parent sessions, Antigravity's internal `call_mcp_tool` steps generated duplicate dummy tool calls returning `(agy recorded no output)`. The bridge now drops `call_mcp_tool` from wrap replay and checks `feats.nativeActive("antigravity")` before emitting synthetic toolUse.
152
+
153
+ ### Changed
154
+
155
+ - **Subagent delegation routes through pi and holds a longer synchronous window (issue #6).** Antigravity orchestrators picked agy's native `invoke_subagent`/`run_subagent`/`manage_subagents`, whose spawns run inside agy, never surface in pi, and leave pi-side harnesses at "0 active / 0 finished". The tool-priority note now mandates bridged delegation tools when one exists (conditional, so a session without one keeps the native fallback), and delegation-shaped bridge calls (`subagent`/`subagent_run`-style tools, the Claude Code-style exact `Agent`, pi's `agent_spawn`/`agent_wait` family, `*delegate*`) hold the synchronous window 120s instead of 20s: the 30-120s subagent range now returns a plain tool result while staying under agy's ~180s `tools/call` abandonment, and longer runs escalate to the existing poll path. `escalateLongTools` in config.json (env `AGY_ESCALATE_LONG_TOOLS`, comma-separated) adds custom names; `escalateAfterMs: 0` still disables the early ack.
156
+
157
+ ## [1.8.4] - 2026-10-15
158
+
159
+ ### Security
160
+
161
+ - **The MCP SDK floor now carries the OAuth credential-routing fix.**
162
+ `@modelcontextprotocol/sdk` 1.12.0-1.30.1 lets a malicious MCP server
163
+ redirect client OAuth credentials to an attacker-chosen authorization
164
+ server (GHSA-6qxp-vccf-f47h). The bridge runs the SDK server-side only
165
+ (`Server` + `StreamableHTTPServerTransport`) and never acts as an MCP
166
+ OAuth client, so its own exposure was nil, but the floor now points at
167
+ 1.31.0, the first fixed version, so installs stop tripping audit. The
168
+ protocol-version clamp is unaffected: it rewrites any unsupported
169
+ `MCP-Protocol-Version` header to the SDK's own `LATEST_PROTOCOL_VERSION`,
170
+ whatever ceiling the new version ships. Lockfiles resolve 1.32.1.
171
+
172
+ ## [1.8.3] - 2026-10-13
173
+
174
+ ### Fixed
175
+
176
+ - **The G10 carrier agent no longer dies on invalid YAML frontmatter (issue #5).** `syspromptAgentMd()` wrote the `description` value as an unquoted plain scalar containing a second `: `, which is invalid YAML. agy silently skips an agent whose frontmatter does not parse, so every stream-json turn with the staged carrier (default `systemPrompt: true`, no user-set `config.agent`) failed with `unknown agent "pi-bridge-sys-…"`; the bug has been present since 1.7.8. The value is now quoted, and all three agent.md generators (sysprompt carrier, web delegate, ask-tool reviewer) are pinned by tests that parse the generated frontmatter with a real YAML parser, so a future wording edit cannot reintroduce the silent drop. Leftover broken `pi-bridge-sys-*` dirs need no manual cleanup: the stale-agent sweep prunes them on next staging.
177
+
178
+ ## [1.8.2] - 2026-10-13
179
+
180
+ ### Fixed
181
+
182
+ - **Released the dependency floor the 1.8.1 code already required.** 1.8.1 imports `dialogChoice` from `@estebanforge/pi-tui-dialogs` but still declared `^0.1.1` — legal semver, yet that version predates the export, so any consumer whose lockfile still held 0.1.1 loaded the new engine picker against the old library and `/agy engine` and the first-run picker died with `(0, _piTuiDialogs.dialogChoice) is not a function`. The floor is now `^0.1.2` (the first version carrying `dialogChoice`, already on npm), so updating the bridge lifts the library everywhere and a stale lock fails at install time instead of at runtime.
183
+
184
+ ## [1.8.1] - 2026-10-13
185
+
186
+ ### Changed
187
+
188
+ - **The engine picker rides the shared dialog kit.** `showEnginePicker` now renders through `@estebanforge/pi-tui-dialogs`' new `dialogChoice` primitive (extracted from this picker) instead of a hand-rolled SelectList overlay: same title, intro, options, preselect, and esc-means-decide-later semantics, now drawn in the kit's framed style. Esc resolves `undefined` instead of `null`; both call sites are updated.
189
+
190
+ ### Fixed
191
+
192
+ - **An unrelated config save no longer promotes a legacy install to the acp default.** `saveConfig` merged `DEFAULTS` over the raw file, so any save (mode, digest, the patch-cleanup notice) on a config file predating the engine key wrote `engine: "acp"` to disk, defeating the legacy continuity rule on the next start. `saveConfig` now persists the resolved legacy engine (`stream-json`) for key-less files, honors explicit `patch.engine`, and never bakes `AGY_ENGINE` into the file.
193
+
194
+ - **The choice modal windows its option list.** `dialogChoice` computed its SelectList window at construction and handed the frame the full list, so on short terminals or with a long description the frame clipped trailing options while the selection could still move into the invisible region. The list viewport now follows the frame height per render (the settings menu's rule), with room for SelectList's `(n/m)` scroll-position line, and the tests pin a 10-option dialog on a 12-row terminal.
195
+
196
+ - **New installs default to the ACP engine; stream-json is the fallback.** `config.engine` unset (no file key, no `AGY_ENGINE`) now resolves to `acp` (`DEFAULT_ENGINE` in config.ts), and stale/garbage stored values fall back to that same default instead of stream-json. One carve-out for continuity: an existing config file that predates the engine key (no `engine` field at all) keeps `stream-json` — the acp default is for new installs, and an existing install must not silently start the ~1.5 GB binary download. The first-run picker preselects acp and explains the trade in that order; the existing startup self-heal covers the rest (binary install + sign-in chain on engine acp). Both engines stay fully supported and every existing config with a saved engine is untouched.
197
+
198
+ - **The `/agy` settings picker refuses plan against the on-disk engine too.** The plan-mode guard checked only the session-latched engine; since the Switch engine row can change the on-disk engine mid-session without a restart, it now blocks on either (latched or saved) so plan + ACP can never land on disk through the settings surface.
199
+
200
+ ### Added
201
+
202
+ - **The `/agy` settings picker has a Switch engine row again** (first row, showing the active engine as its value). It is deliberately not a cycle row: Enter on it closes settings and opens the same picker modal `/agy engine` uses — explanations, plan-blocks-acp guard, and the acp pick's self-service setup + sign-in chain all come from the one shared path. Esc in the picker changes nothing.
203
+
204
+ ### Fixed
205
+
206
+ - **ACP sign-in no longer fails on servers that authenticate without writing `acp_token.json`.** Server 1.1.1 can answer `authenticate` OK from legacy `~/.gemini` credential artifacts (observed on darwin/arm64: instant OK, no browser, no token file, working turns) — but `/agy auth` treated the absent token file as a distinct failure and the session-start self-heal nagged login-pending forever on a fully working setup. The token file is a server-internal detail, not a contract. `/agy auth` now verifies the token-absent branch with a live `session/new` probe on the same connection: probe success is a real sign-in (result carries a note, the success toast says so), probe failure is an honest "no usable credentials" error that covers the lazily started browser flow. Both verified paths record a bridge-side marker (`~/.pi/agent/antigravity-bridge/acp-auth-verified.json`, a boolean plus timestamp, never a credential); `ensureAcpReady` treats the marker as login-done, `/agy doctor` shows `oauth-personal (verified, no token file)`, and any server-side `-32000` clears the marker so a stale one never masks a real logout.
207
+
208
+ ## [1.8.0] - 2026-10-12
209
+
210
+ ### Changed
211
+ - The `/agy` settings picker now renders on the shared `@estebanforge/pi-tui-dialogs` framed dialog kit instead of the bare pi-tui `SettingsList`: bordered rows, a help panel per setting, type-to-search, and the config file path under the title. Same eleven rows, same stage-then-save-on-close behavior.
212
+
213
+ ## [1.7.13] - 2026-10-12
214
+
215
+ ### Changed
216
+
217
+ - The `AskAntigravity` tool's default `timeoutMinutes` is now 30 (was 10). Delegations carry feedback, research, review, and analysis work that legitimately runs long; a short default kills the run mid-task and the caller re-pays the whole prompt at a higher timeout. Callers still override per call, lower or higher. The provider's separate `turnTimeoutMin` cap is unchanged.
218
+
219
+ ## [1.7.12] - 2026-10-12
220
+
221
+ ### Fixed
222
+
223
+ - **Unpolled escalated results are re-delivered instead of stranding.** Delivery of an escalated bridge call depends on the model polling `bridge_poll_result`, and the model sometimes ends its turn after the `STILL RUNNING` ack instead — observed live with `ask_user_question`: the user's answer settled into the escalation registry, the bridge printed a human-only `[bridge] ... available via bridge_poll_result` note, and the conversation dead-ended (`git_commit` worked only because the model happened to poll). The ack itself licensed stopping with a promise the bridge did not implement ("if you stop polling, the bridge re-delivers the result in a later turn"). Three changes close it: the ack now forbids ending the turn while a call is unresolved; the escalation registry tracks delivery (`markDelivered` when a poll returns a terminal answer, `stampConversation` at turn end so first-turn parks — which return before the provider's `store.set` — still know their conversation, first stamp wins) and exposes a single-use registry-wide sweep for done-but-undelivered results; and the provider runs that sweep at every turn boundary — the toolResult pass when the turn is dead, after a live turn's outcome, and any later pass, so a strand cannot outlive the next one — re-delivering as a late prompt in the stamped conversation through the existing `buildLateResultPrompt` path. A result whose conversation is unknowable settles quietly and stays pollable instead of prompting a brand-new conversation, and a delivery whose `driver.run` fails to start releases its claim for the next pass. Claims are single-use and entries stay pollable, so a compliant model sees no change; late delivery stays text-only on this path (image blocks are dropped, same as dead-park late delivery).
224
+
225
+ ## [1.7.11] - 2026-10-10
226
+
227
+ ### Added
228
+
229
+ - AskAntigravity prompts now carry a `[TIME BUDGET]` notice matching the resolved timeout (default 10 minutes), placed before the plan-mode guard banners so the safety constraints stay the last text the model reads. `timeoutMinutes` values that cannot arm a real timer (non-positive, non-finite, above the 35791-minute ceiling) are rejected with a clear error instead of arming an instant-kill watchdog.
230
+
231
+ ### Changed
232
+
233
+ - **AskAntigravity dedup onto `pi-ask-shared` (behavior-preserving).** The tool's copy of the agy model grammar, tiered alias resolution, plan-mode guard prompts, prompt assembly, and argv construction now imports the shared peer adapter (new dependency `@estebanforge/pi-ask-shared`); `src/discovery.ts` is deleted — its conversation-bind technique (snapshot/diff + /proc FD scan, including the `onAmbiguous` hook, now upstream in the shared adapter) was the last consumer. The process block is deliberately kept bridge-local: it settles on close AND exit, leaves stdout/stderr uncapped, and threads the MCP-entry suppression release into cleanup — all three diverge from the shared `runProcess` contract on purpose. Shared `newConversationId` gained the `onAmbiguous` callback and exports `procTreeOpenDbResolver` (both absorbed from discovery.ts; `conversationDbPath` was unused and died with the module).
234
+
235
+ ## [1.7.10] - 2026-10-07
236
+
237
+ ### Added
238
+
239
+ - **`readOnly` config key (fail-closed provider kill switch).** `AGY_READONLY` env and `/agy readonly on|off` set it. On: every provider turn is refused with a visible error instead of running, because the agy CLI has no review-only enforcement on any path (probed 2026-10-07, [google-antigravity/antigravity-cli#1181](https://github.com/google-antigravity/antigravity-cli/issues/1181): plan mode stages a plan and executes the write in the same turn on both the `-p` and stream-json paths, with `permission_mode: "request-review"` reported and no permission event for the write; with `--disable-slash-commands` plan is a warned no-op). Built for tool-less callers (`pi -p --no-tools --model antigravity/...`) that must never mutate a workspace. While the knob is on, the `AskAntigravity` tool is refused as well; nothing in this package runs read-only, because the CLI cannot enforce it.
240
+ - **`AskAntigravity` mode default now follows the config.** Omitting `mode` used to hardcode `accept-edits`, so a `mode: "plan"` config still left the delegation tool write-capable by default (issue #3 request 3). The default is now plan when the configured mode is plan; an explicit `mode` param still wins.
241
+
242
+ ### Fixed
243
+
244
+ - **Plan-shaped provider turns refuse visibly instead of running write-capable.** `/agy mode plan` on the stream-json engine was silently a no-op (the engine warns plan has no effect with slash expansion disabled), and the `-p` path is no better: probed 2026-10-07, plan stages a plan, asks for approval, and executes the write in the same turn on both paths ([#1181](https://github.com/google-antigravity/antigravity-cli/issues/1181)). Provider turns with `mode: "plan"` or `readOnly` on now fail with an error pointing at host-side enforcement and the knob to lift. The README no longer claims provider plan mode "genuinely executes nothing" or that the one-shot path enforces plan.
245
+ - **`readOnly` refusals cover the `AskAntigravity` tool too, and plan runs never carry the skip flag.** `AskAntigravity` is refused while `readOnly` is on (a `mode` param cannot bypass the kill switch), and the tool's plan runs no longer pass `--dangerously-skip-permissions` even when the reviewer agent stages: the CLI does not gate writes under plan mode ([#1181](https://github.com/google-antigravity/antigravity-cli/issues/1181)), so an auto-approved plan run is a write-capable run. Analysis commands in plan runs now fail visibly (headless auto-deny) instead of running approved; `accept-edits` runs keep the flag.
246
+ - **Delegation tool refuses `background: true` (fail-closed).** The param is advertised and always refused with an explanation: a wake from a nested delegation would land in the pi session, and long delegations already have the bridge's own early-ack plus poll flow. No behavior change for existing calls.
247
+
248
+
249
+ ## [1.7.9] - 2026-10-06
250
+
251
+ ### Changed
252
+
253
+ - **Breaking: Claude models are effort-tiered now, and the ids moved to the Claude 5.5 family.** Google retired Claude 4.6 on Antigravity and ships Claude Opus 5.5 (2026-09-22) and Claude Sonnet 5.5 (2026-09-28) in its place, each with low/medium/high thinking tiers - the same shape Gemini already had. Claude joins the verified effort-capable families (probed live 2026-10: base slug + `--effort` accepted; a bare base is rejected with "requires --effort"), so `claude-opus-5-5` and `claude-sonnet-5-5` register as single toggleable models whose turns always send `--effort`, instead of per-tier fixed ids. The catalog fixtures mirror the live listing and the no-`agy models` fallback carries its head (3.8 flash, 3.1 pro, sonnet 5.5). A pattern pinned to `antigravity/claude-sonnet-4-6` (settings enabledModels, `--model`, or a scoped-model config) matches nothing after agy's upgrade and must move to `antigravity/claude-sonnet-5-5`; the bare base id itself resolves through the delegation tool to the nearest tier variant.
254
+ - **The `gpt-oss-120b-medium` family is dropped now, not next month.** Upstream announced its removal; nobody used the fixed-thinking single slug and it loses to the cheaper effort-tiered options, so the provider catalog and the delegation tool catalog both filter it before that date lands. The `gpt-oss` alias stops resolving: unrecognized input passes raw to agy and fails loudly there, instead of the tool offering a dying model.
255
+
256
+ ## [1.7.8] - 2026-09-30
257
+
258
+ ### Fixed
259
+
260
+ - **The system prompt no longer rides the prompt line on stream-json.** Probed on agy 1.2.14: the CLI silently drops any single prompt past a ~25KB input cap. The drop reports SUCCESS with an empty response, zero usage, and no model step, and a conversation whose turn was dropped never answers again - resuming it hangs with no result frame. The G10 system-prompt block shipped inline on the first prompt of every fresh conversation by default and routinely crossed the cap, so provider sessions answered boilerplate while the user's message never ran (issue #2). The block now stages as an agy agent file under the agents discovery root and rides `--agent`: the CLI reads agent files from disk, so the cap never applies, and the prompt line carries only the user message. Staging is content-addressed - one dir per distinct body, write-once, pid-marked, orphan-swept - so two conversations in one process can never overwrite each other's carrier (the round-2 review blocked the original per-process slot for exactly that cross-contamination). A user-configured `config.agent` always wins, with the inline block as fallback and a one-time warning. ACP keeps the inline block: the JSON-RPC transport has no cap and no `--agent` slot.
261
+ - **A dropped or dead turn now fails loudly instead of poisoning the session.** The stream-json driver settles every turn with model-output and result-frame evidence. An OK outcome with no model output - the exact signature of the silent over-cap drop - fails the turn with the cause and the remedy, and never stores the conversation binding; a deadline on a bound conversation with no result frame clears the binding so the next turn starts a fresh conversation instead of hanging forever on a dead one. Real terse turns (tool steps, thinking, any usage) are untouched; a deadline that recovered a withheld answer keeps settling OK.
262
+ - **Regression coverage pins the whole failure chain at both layers.** The fake agy binary grew a SILENT-DROP branch reproducing the exact wire signature; driver tests pin the evidence fields; provider tests cover the drop, the poisoned resume, a deadline-with-result keeping its binding, terse-turn false positives, carrier isolation across interleaved conversations, and deadline-flavored error copy. 1343 tests, tsc clean; round-2 adversarial review signed off after the carrier-isolation blocker fix.
263
+
264
+ Thanks @Jamsa for the report (#2).
265
+
266
+ ## [1.7.7] - 2026-09-30
267
+
268
+ ### Fixed
269
+
270
+ - **A recreated agy conversation no longer starves the context digest.** When `session/load` failed, the ACP engine silently fell back to a fresh conversation (`session-load-failed-creating-fresh`), but the end-of-turn write-back kept advancing the stored watermark, so the new conversation never received the backlog: the digest delivered only the current prompt for the rest of the session, and the G10 system-prompt gate stayed closed too, because the surviving record always carried a conversation id. The write-back now compares the outcome's conversation id against the one stored before the turn and drops the record on a mismatch: the next turn re-delivers the full digest backlog and re-arms the system prompt, on both engines, with no protocol change. `SessionStore` gained a per-key `delete()` whose removal persists exactly once (the removal set prunes after a durable write, so a later persist can never destroy a record another process wrote in the meantime), preserving the multi-process merge invariant.
271
+ - **Failed and aborted turns no longer advance the digest watermark.** The write-back recorded `lastMessageCount` on any turn that reported a conversation id, ERROR and aborts included. Whether the prompt actually landed in the conversation's history is not observable from the provider, and advancing on that ambiguity starves the digest of the missed turn (the same failure class as the recreation bug above). The watermark now advances only on a clean, non-aborted turn; failed, aborted, and unknown outcomes leave the pre-turn record untouched, so the next digest re-delivers the missed turn bounded by the existing `maxChars` cap (default 8000) instead of losing it. With the digest off (the default) neither fix changes observable behavior.
272
+ - **Regression coverage pins both behaviors at the provider layer.** Eight new tests: record drop, backlog re-delivery, and system-prompt re-arm across the id swap; unchanged watermark across failed, aborted, and unknown outcomes on the same conversation; and the session-store delete and multi-process semantics, including a reclaimed key surviving an unrelated persist from another process. 1329 tests, tsc clean.
273
+
274
+ Thanks @fishfuuu for the report (#1).
275
+
276
+ ## [1.7.6] - 2026-09-28
277
+
278
+ ### Fixed
279
+
280
+ - **Plan-mode delegations enforce read-only instead of promising it.** The old design withheld the skip flag and relied on a prompt asking nicely; the field disagreed once — a plan delegation with the flag re-injected through the environment still edited files, because a prompt guard is a request, not a wall. Plan-mode runs now stage a temporary agy agent under `~/.gemini/config/agents/pi-bridge-ask-*` whose `tools:` list carries only `view_file` and `run_command`: with no file-editing tool in the toolset the model cannot emit an edit call at all (probed on agy 1.2.12 with and without `--dangerously-skip-permissions` — the restricted toolset reports "none" for edits both ways). The staged agent's `commandExecutionPolicy: auto` is what keeps reviews useful: analysis commands (git, rg, test runners) run headless for every user, with no `permissions.allow` edits — the per-user-config approach was rejected as unshippable for a product. With the agent staged, the skip flag returns under the same `skipPermissions` knob (default on): its only real danger was auto-approving edit calls, and none exist to approve. The prompt guard switches to a mutation-forbidding text (read-only commands allowed), and the tool description now states the true residual plainly: file-editing tools are hard-blocked, arbitrary shell execution under the flag stays prompt-guarded alone. Staging failure degrades fail-closed — no agent, no flag, the stricter all-commands-forbidden guard, with an env-injected skip flag stripped from `AGY_EXTRA_ARGS` on that path only. Agent dirs are nonce-named, pid-marked, removed in the run's finally, and orphans swept at registration (the web-delegate sweep gained a prefix parameter so both doctrines share one implementation).
281
+ - **Headless plan prompts say what they cannot do.** `agy -p` cannot answer permission prompts, and in plan mode a shell-command attempt is soft-denied: the run ends cleanly with nothing on stdout and the reason only on stderr. Plan prompts now append an explicit constraint — no shell commands, answer from the material in the prompt, state exactly what is missing — converting the silent death into a visible missing-content request (probed: the previously-fatal prompt shape now answers "Missing information: ..." in about ten seconds). With the staged reviewer agent above, this guard yields to the mutation-forbidding variant; it remains the rule on the staging-failure fallback path.
282
+
283
+ ## [1.7.5] - 2026-09-25
284
+
285
+ ### Fixed
286
+
287
+ - **Exit 0 with no output is a failure, not a silent success.** A headless `agy -p` run can end cleanly with nothing on stdout when a delegated tool call hits a permission gate the headless run cannot prompt for. The plan-mode fix (withholding the skip flag in plan) makes the command gate the common case: the call is auto-denied, the reason goes only to stderr, agy exits 0, and the execute path fell through to success, returning just the conversation footer. The delegating model read that as "returned only a conversationId, zero output, no error" (silent-failure handoff, 2026-09-25). An empty answer now returns a loud failure note carrying the redacted stderr plus the remedy (allow-list the command under `permissions.allow` in the CLI settings, or rerun outside plan mode), the ask-end log marks the run warn with an `empty` flag so error-only default logging records what the chat saw, and a new `details.empty` marker flips the TUI result row to its error glyph instead of a green checkmark. Three new tests pin both stderr shapes and the glyph flip through the fake-binary stub.
288
+
289
+ ## [1.7.4] - 2026-09-25
290
+
291
+ ### Added
292
+
293
+ - **`/agy agent <name|off>` — select a custom agy agent for stream-json turns.** The agent list comes from a one-shot `agy agent` spawn through the same bounded raw-spawn helper as the model catalog (8 s watchdog, 1 MiB cap, fail-closed to an empty list), parsed line-oriented: banner and help noise skipped, first whitespace token per line taken, filesystem-safe names validated, duplicates dropped. The choice persists in config with an `AGY_AGENT` env override, rides `--agent` on stream-json spawns, and recycles the process when it changes. ACP has no protocol slot for agent selection, so the command refuses it there with a stream-json-only message instead of silently ignoring the knob.
294
+ - **`/agy subagents` — live subagent roster.** agy's subagent steps were invisible from pi: spawn, message, and manage activity now folds from the engine-agnostic activity stream into an in-memory roster (spawn tools open entries, `send_message` counts messages, `manage_subagents` kills mark entries killed, done/error closes them). Entries key by tool-call id on ACP and step id on stream-json, with name, status, duration, message count, detail, and last error rendered on demand. Works on both engines; deliberately never persisted — agy's conversation database is the durable record, and telemetry must never be able to fail a turn.
295
+ - **`/agy quota` — subscription quota view.** One-shot `agy --print /usage` fetch rendered as bars per window, 5h ranked before weekly, with reset times. The fetch carries its own 30 s budget (the live call answers in ~10 s) and the wrapper watchdog is padded 5 s past agy's own `--print-timeout` so agy's clean self-timeout always wins the race over a SIGKILL. Quota reflects the CLI's Google account identity, so the reading is equally true under both engines; a CLI-less ACP-only install gets a fail-closed pointer to `/agy doctor` rather than a raw spawn error.
296
+ - **`/agy artifacts [open <n|name>]` — browse the conversation brain dir.** Non-recursive scan of the conversation root, `.tempmediaStorage`, and `.user_uploaded`, deduped by canonical path, newest first, with kind/size/last-write shown. `open` resolves by index, exact name, or unique substring and launches the OS opener detached. Path containment canonicalizes the scan root once and re-realpaths every entry before opening, so symlinked ancestors (macOS `/var` → `/private/var`, symlinked homes) cannot silently empty the listing — the first containment gate compared raw paths and did exactly that on such machines.
297
+ - **Parked-answer recovery on stream-json deadline guards.** When the stall or total-timeout guard fires, the driver now kills the child first (bumping the generation so a racing exit handler cannot settle the turn ERROR mid-probe), then probes the conversation's brain transcript: if the NEWEST step written during the turn is a clean, tool-free DONE MODEL response, the turn settles OK with the withheld answer instead of discarding finished work. The newest-step rule is the load-bearing part — a corpus sweep found 96.6% of PLANNER_RESPONSE transcript steps carry tool_calls; they are intermediate plan-and-act steps whose content is status text, not answers, and an older scan that accepted any matching step could settle a turn with "Running test..." as the answer. ACP reports turn completion itself, so the probe is stream-json-only by construction.
298
+ - **`/agy tasks [tail <id>]` — read-only background-task dashboard.** Scans the conversation's task logs (newest first, 128 cap, missing dirs degrade to an empty list) and marks a task ACTIVE while any process holds its log open, via one batched `lsof -nP -Fpn` call — a reader counts too, so the state is a watch, not a fact. Watch-only by design: modern agy pipes task output through itself, making log ownership unprovable, so nothing is ever stopped from here and a missing lsof shows `liveness?` instead of a guess. `tail <id>` resolves against the scanned list and prints the last 40 lines.
299
+ - **Agy CLI version awareness.** A warn-once session toast fires when the CLI reports older than the 1.1.22 stream-json floor; development builds pass, unparseable output classifies as invalid, and a failed spawn maps to a distinct unavailable status rather than a fake verdict. `/agy doctor` gained a CLI version line, labeled "(optional here; AskAntigravity delegations use the CLI)" on ACP-only installs because the CLI matters on both engines. Warn-only by design: a hard spawn gate would block turns on a slow `--version` in odd environments, and an old agy already fails turns with parse noise the warning explains. ACP server versions stay display-only — `agy_acp_server_1.1.1` and CLI `1.2.10` are unrelated streams, and no floor is enforced against the server until its versioning is understood.
300
+
301
+ ### Removed
302
+
303
+ - **`/agy status`.** It was never a real subcommand — the old argument guard let the literal name fall through to the settings picker while the printed summary read as a broken second status surface. `/agy doctor` is now the single status surface: bridge state, driver counters, CLI and ACP versions, lifecycle, plus the settings rows folded in (nothing duplicated — rows doctor already rendered stay out of the shared block). Typing the retired name gets a one-line pointer to doctor; bare `/agy` keeps its settings picker.
304
+
305
+ ### Fixed
306
+
307
+ - **The agy models discovery spawn can no longer balloon memory.** It accumulated child stdout with no cap, so a runaway agy could exhaust the whole pi session. Output is now capped at 1 MiB (decoded-length proxy, documented); past the cap the child is SIGKILLed and the result fails closed to empty, which engages the baked-in fallback catalog — and a capped empty result never overwrites a good on-disk cache.
308
+ - **Plan mode never receives `--dangerously-skip-permissions`.** agy's plan-mode no-write gate IS a permission request ("File creation requires plan approval"), and the skip flag auto-approves every permission request — so passing the flag alongside `--mode plan` silently turned review-only into full write access inside the `--add-dir` grant. Probed both directions on 2026-09-25: with the flag a plan run wrote files; without it, file-write and run_command attempts end exit 0 in ~20-30 s with a "confirm plan" message — fail-visible, never the upstream #318 prompt-hang. Both spawn surfaces (the AskAntigravity one-shot and the stream-json driver) now withhold the flag in plan mode through one shared helper; the recycle profile stores the effective value and compares effective-vs-effective so plan sessions keep process reuse. README and ENGINES.md now state the enforcement instead of the aspirational claim.
309
+
310
+ ## [1.7.3] - 2026-09-24
311
+
312
+ ### Fixed
313
+
314
+ - **Collapsed ACP tool cards no longer repeat the tool name.** The `agy-native-event` renderer filled the muted detail slot with the tool name itself whenever an event carried neither a path nor a recognized command argument, so `ls` and `exec_command` collapsed to "✓ ls ls" / "✓ exec_command exec_command". The detail now prefers the path basename, then the command's first line, then agy's own display text (the first line of the completed call's output, e.g. "Run make test in wicket-cli-atlas"), and stays empty when none exists; the name always stands alone. ACP tool_call frames carry no per-tool reasoning, so agy's display text is the closest honest signal.
315
+
316
+ ## [1.7.2] - 2026-09-24
317
+
318
+ ### Added
319
+
320
+ - **ACP permission requests park on a human decision.** `session/request_permission` was answered silently in-process (skip turns auto-allowed, everything else auto-denied), making ACP turns all-or-nothing with no per-tool human say. Interactive turns now hold the JSON-RPC request open and surface the server's options as a pi select dialog under the dialog lock, with the turn budget paused while the human decides: 480 s park, deny on timeout/esc/throw/connection death, and a cancel instead of an answer when the server offers no reject option (a deny must never degrade into picking an allow). `skipPermissions` keeps the synchronous auto-allow; `approvals.mode allow` maps to it, deny and headless stay fail-closed. `allow_always`/`reject_always` answers are remembered per connection for identical later requests, and first settle wins: a late dialog answer after a timeout is forgotten. Nine new tests pin the behavior, including a scenario proving a late answer writes nothing. tsc clean.
321
+
322
+ ### Changed
323
+
324
+ - **Removed the dead `acp.permissions` config key.** It documented the in-connection auto policy but never had a consumer. Configs carrying it keep loading (unknown `acp` keys are ignored) and it drops out of the file on the next save.
325
+
326
+ ## [1.7.1] - 2026-09-23
327
+
328
+ ### Fixed
329
+
330
+ - **Subprocess stderr can no longer leak secrets into logs or the chat.** The `agy` CLI can print auth material (API keys, OAuth tokens, credential headers) to stderr, and six surfaces carried that text verbatim: the daily support log users attach to bug reports, the stream-json turn-failure error, the ACP connection-exit rejection reasons and `/agy doctor` tail, the web tools' failure note, and the AskAntigravity delegation exit note. A key in an auth error therefore landed on disk and in the transcript unchanged. Redaction now runs on the reassembled stderr tail at exit and getter boundaries - never per chunk - so a token split across two pipe events still matches, and the raw tail stays internal. New `redactText()` scrubs four secret-shaped pattern classes (API-key prefixes, OAuth tokens, credential headers, token-ish JSON fields), anchored with a word boundary so ordinary text like `desk-123456789012` survives, and carries no length cap because callers already bound their own output sizes. The daily log scrubber additionally scans string values, not just secret-shaped keys, and routes `Error.message` through the same pass; values are scanned before truncation so a cut token cannot lose the characters a pattern needs. Best-effort by design: a last line against leak paths, not a sandbox. Ten new tests pin the behavior, two of them spawning real processes (a secret-bearing crash and a token split across stderr chunks). 1200 tests, tsc clean.
331
+
332
+ ## [1.7.0] - 2026-09-23
333
+
334
+ ### Added
335
+
336
+ - **Opt-in web tools for any provider's model** (`agy_web_search`, `agy_read_url`). Pi ships no web tools and no MCP by default, so sessions driven by non-Antigravity models had no way to search or fetch. With `webTools: on` (`AGY_WEB_TOOLS`, `/agy web on`; off by default because Antigravity sessions already have native web tools on both engines), each call spawns a one-shot search-only `agy` agent (`--mode plan`, bridge MCP inheritance off, per-call agent dir under `~/.gemini/config/agents/pi-bridge-web-*` removed in `finally`), enforces a 120 s deadline and a 2 MiB output cap, tolerates agy's `OK`/`SUCCESS` result-status drift, requires an observed native `search_web`/`read_url_content` step before trusting the answer, and refuses the call if the agent touches any disallowed tool - the init frame advertises a broad tool list regardless of the agent's `tools:` restriction, so the response-side gate is the real anti-hallucination device. Stale agent dirs from crashed calls are swept by pid liveness at registration (marker-less dirs after a 24 h grace); dir names carry a random nonce so same-millisecond parallel calls cannot collide. URLs are scheme-allowlisted (http/https); queries cap at 2000 chars. Every call spends Antigravity subscription quota.
337
+ - **ACP native tool display cards.** The ACP engine had no card story: agy's own tool steps surfaced only as thinking-stream labels. agy's tool events now render as display-only pi entries (status icon plus the file path with colored diff lines, the first command line, or the captured output; persisted content bounded at 4000/12000/500 chars), streamed as steps start and complete. Events attributed to `mcpServer: "pi-bridge"` are filtered out, so bridge round-trips that already produce genuine pi tool cards never double-render. Stream-json keeps its stronger native re-exec cards.
338
+ - **Live ACP usage estimates.** The ACP engine showed zeros until a turn settled (Gate B). Estimates now stream live: the turn's prompt is tokenized once, each text/thought delta updates the running counts, and an `ACP ≈ X in / Y out` status line tracks the turn (suppressed when `acp.usageEstimate` is `off`). Estimates are superseded only by a real `PromptResponse.usage` the day the server sends one; diagnostic usage-shaped frames (context windows, occupancy) never disable them.
339
+ - **`/agy tools` session-only catalog control.** `hide <name>` / `show <name>` / `reset` filter the bridge catalog for the current session only (multi-word names OK; names validated against pi's registered tools; saved config never touched). Bare `/agy tools` lists exposed and hidden tools.
340
+
341
+ ### Changed
342
+
343
+ - **The bridge catalog is computed live and guarded at dispatch.** The exposed set used to be computed once at registration; `/mcp` or `pi.setActiveTools()` changes mid-session left stale tools callable. The catalog is now derived from pi's live active set on every `tools/list` AND re-checked immediately before every `tools/call` - a call for anything outside the fresh set is rejected (`a cached MCP catalog is not authorization`). The bridge's own internal tools (AskAntigravity, the display-only `antigravity` wrapper, `activate_skill`, `bridge_poll_result`, `agy_web_search`/`agy_read_url`) are excluded from the catalog, closing a gap where the wrapper tool was exposed. `/agy bridge` mode changes apply to a running server immediately; a stopped one picks them up on the next start.
344
+ - **Global MCP registration is stream-json-only; hygiene is universal.** Registering and unregistering the bridge's per-pid entry in `~/.gemini/config/mcp_config.json` now happens only for the stream-json engine (ACP supplies the bridge per-session and never touches the global config), while the stale-entry sweep and the stuck-suppression heal run for BOTH engines - an ACP-only session no longer leaves dead entries or a stuck suppression flag behind by a crashed stream-json session.
345
+
346
+ ### Fixed
347
+
348
+ - **Concurrent native tool results no longer cross-pair on ACP.** When several of agy's tools ran at once, a completed event for one could attach its diff/output to a different pending call. Adoption now happens only when exactly one call is pending, finished and errored ids leave the tracking map, and a known-acceptable residual is documented at the site: a done event for a genuinely unrelated call can still pair with the single pending entry.
349
+ - **Diagnostic usage frames can no longer kill ACP estimates.** The old suppression latch tripped on ANY usage-shaped frame and lived for the whole connection, so one context-window notice in turn one silenced the live estimates for every later turn, with no exact data ever replacing them. Suppression now keys on a real per-turn `PromptResponse.usage` only; a two-turn regression test pins that estimates survive the diagnostic frames.
350
+
351
+ ## [1.6.3] - 2026-09-22
352
+
353
+ ### Fixed
354
+
355
+ - **The approval gate no longer leaks into standalone sessions, and its verdicts parse.** Thanks @explesy for the report (issue #5). Two defects in the generated hook script and its staging. Every terminal path printed the bare decision scalar (`"deny"` / `"allow"`) instead of a JSON object, so any session that loaded a staged gate failed hook parsing outright (`protojson: syntax error`): the direct-POST and polling branches now print the full response object guarded by a `typeof json.decision === "string"` check, fall back to a fail-closed deny object on malformed bodies, and read ticket and poll status through `json?.`. The gate group itself was staged into the shared workspace `.agents/hooks.json`, so any standalone IDE or CLI session in that workspace loaded pi's gate: tool calls were denied with "no active antigravity turn" while pi sat idle, and mid-turn they parked into pi's approval queue. A live probe of the CLI settled the discovery rule (hooks load from `--add-dir` directories in stream-json and interactive sessions; print mode reads only the global config), and the gate now stages `.agents/hooks.json` into the session-private per-pid bridge dir beside `mcp_config.json`, which only this session's agy receives as an extra `--add-dir`. Staging into a pid-private file dropped the merge/backup/symlink surface to a plain atomic replace; the driver passes the dir when the gate is staged even without an MCP config; a legacy sweep at session start strips groups that dead sessions left in workspace files (live and foreign groups are never touched). The hook also gained per-fetch aborts (`AbortSignal.timeout`, POST capped at 10s, polls bounded by the remaining park budget) so a hung bridge request can never outlive the park deadline into an ungated soft-pass. Seven stub-server hook-execution regression tests pin the object contract; the staging suite covers the private-dir semantics and the sweep. 364 tests, tsc clean.
356
+
357
+ ## [1.6.2] - 2026-09-19
358
+
359
+ ### Fixed
360
+
361
+ - **Compatibility with host 0.86.0's normalized provider stream input.** Stream entry points now receive a branded `TranscriptContext` (`{ messages }` only): the `context.systemPrompt` / `context.tools` fields are gone, and only `normalizeContext()` produces the new type. G10 fresh-conversation delivery read `context.systemPrompt`, so every fresh agy conversation silently lost the system prompt block (operating instructions, AGENTS.md project context, tool-priority note); the prompt now comes from the transcript via `getCurrentSystemPrompt(context.messages)`. `emitToolUse` arguments are restricted to `Record<string, JsonValue>` per the tightened `ToolCall.arguments` contract (driver args arrive JSON-decoded off the wire, so the two call-site casts are sound). Test fixtures in `provider-{sysprompt,streaming,late-result,escalation}.test.ts` and the `scripts/test-provider.ts` smoke context wrap raw objects with `normalizeContext()`. Dev pins `@earendil-works/*` raised `^0.85.0` to `^0.86.0`; `tsc` clean, 360 tests green.
362
+
363
+ ## [1.6.1] - 2026-09-18
364
+
365
+ ### Added
366
+
367
+ - **`thinking` / `effort` params on AskAntigravity (synonyms).** Delegation calls had no direct tier knob: the only lever was tier sugar inside `model` ("flash high"), so a caller told "peer review on high thinking" fell through to the configured default (medium) with no signal anything was dropped. The tool now takes `thinking` (pi vocabulary: minimal|low|medium|high|xhigh|max, clamped to agy's low|medium|high; unknown values fall back to low) plus `effort` as the same knob under agy's own name, so calling models reach for either word. An explicit level beats a tier embedded in `model` and the config default; passing both with different values errors; fixed-thinking families (Claude, GPT-OSS) ignore it because agy rejects `--effort` for them. `toAgyEffort` moved from `src/provider.ts` to `src/models.ts` so provider turns and the delegation tool share one clamp.
368
+
369
+ ## [1.6.0] - 2026-09-14
370
+
371
+ ### Added
372
+
373
+ - Turn-cap knobs (`turnTimeoutMin`, `inactivityTimeoutMin`) with TTY-aware defaults. The "ACP turn exceeded the 10m deadline" error was the bridge's own overall turn cap - not a Google server limit: the ACP server binary exposes no timeout flag (`--helpfull` lists only debug/notices), and the stream-json engine carried the identical 10m cap ("agy exceeded the 10m turn timeout"). The cap never refreshed on activity, so healthy long turns died mid-task at exactly 10m. The default is now `0` (no cap) on an interactive pi - the user aborts with Esc and is the better backstop - and `20` minutes headless, where nobody can abort and one runaway turn blocks the drivers' serialized turn queue. Opt into a timed gate with 1-1440 minutes: free type via `config.json` or the new `/agy timeout <1-1440|off>` subcommand, or pick a preset (0/1/5/10/15/30/60/120/360/720/1440) in the `/agy` settings picker; `0` disables explicitly; garbage, negative, or >1440 falls back to the TTY-aware default. The 5m inactivity stall guard is unchanged, so a silent hung server still dies. Env `AGY_TURN_TIMEOUT_MIN` / `AGY_INACTIVITY_TIMEOUT_MIN` win over the file.
374
+
375
+ ## [1.5.3] - 2026-09-10
376
+
377
+ ### Fixed
378
+
379
+ - **Parallel agy tool approvals no longer clobber each other.** The shadow-tool `GatePolicy` asked through pi's `ui.confirm`, but pi's TUI shows ONE extension dialog at a time and an overlapping call replaces the live dialog without settling it: with several agy bash/write/edit calls in flight, approvals were silently lost (the park timeout eventually resolved them as declines). The confirm now holds the same shared cross-extension dialog lock as the pi-*-me gates (`withDialogLock`, `Symbol.for("pi-me.dialog-lock")`), so approvals queue and render in turn; the park timeout still bounds each dialog and is held inside the lock. New `src/dialog-lock.ts` module plus contract tests (FIFO order, throw-release, shared key).
380
+
381
+ ## [1.5.2] - 2026-09-09
382
+
383
+ ### Fixed
384
+
385
+ - Delegation suppression now spans the whole delegated run. The 1.5.1 suppression window ended after 5 seconds, but agy watches `mcp_config.json` and hot-reloads MCP servers on every file change (`ReloadMcpConfig` in the binary): the window's rewrite poked the live delegation into reconnecting, and a delegated peer review hit the fail-closed "no active antigravity turn" deny ~50 seconds in (observed live, reproduced on demand through `AskAntigravity`). The bridge entries are now hidden until the delegated process actually closes; the release fires on process close or error only, and a regression test pins "exactly one release, at close, never on a timer" with a fake `AGY_BIN` that outlives the old grace (plus the spawn-error route).
386
+ - Suppression is coordinated across pi processes through a shared marker file (`suppression.json` in the bridge's extensions-data dir, `{pid: since}` per live delegation, 0600/0700, atomic rename). Release and the session-start heal re-enable the entries only when no live delegator remains, so two sessions delegating concurrently - or a second pi window opening mid-delegation, which the old blind heal did unconditionally - no longer un-hide each other's entries, and a session starting mid-delegation registers its own entry disabled. Dead delegators are pruned by pid liveness with a 24h age bound against pid reuse; re-enable decisions read the marker fresh at the flip, so a racing acquire keeps its suppression. The marker is coordination only (the disabled flags in `mcp_config.json` stay the gate), lives outside agy's watched config dir, and its writes are best-effort: a lost entry in the syscall-scale read-modify-write window degrades to the status-quo fail-closed deny, and any residual interleave is bounded and self-heals at the next release or heal.
387
+
388
+ ### Changed
389
+
390
+ - README's recursion-safety paragraph, the DEVELOPMENT regression list, and the AGENTS.md module lines describe the shipped mechanism: whole-run suppression, cross-process marker, marker-aware heal and registration guard.
391
+
392
+ ## [1.5.1] - 2026-09-15
393
+
394
+ ### Fixed
395
+
396
+ - Delegated agy no longer sees the pi tool bridge. `AskAntigravity` spawns `agy -p`, and any agy on the machine discovers MCP servers from the global `~/.gemini/config/mcp_config.json`, which carries a live `pi-bridge-*` entry for every running pi session. A delegation could therefore connect to a host bridge and call tools (observed live: `memory_search`), which the round-trip store denies fail-closed with "no active antigravity turn" the moment no provider turn is streaming - a dead end for the delegation and an error toast in the host session. The bridge entries are now suppressed while a delegated agy starts: disabled before the spawn, re-enabled on process close or after a 5s grace, whichever lands first. Suppression is reference-counted, so overlapping delegations cannot re-enable early; session start re-enables any entries a crashed delegation left disabled. Foreign MCP servers in the file are never touched. Residual race, documented in code: another session's provider agy respawning inside the window reads the entries disabled and that process lacks bridge tools until its next recycle.
397
+ - Real bridge tool-call failures now land on disk in default mode. The daily log records only errors, and `call-tool-fail` was logged at warn tier (toast only), so a failure like the one above left no durable trace. Genuine rejections now log at error tier; routine turn-end and shutdown aborts stay silent, with the reasons shared as constants between the emit sites and the log classifier so the two cannot drift.
398
+
399
+ ### Changed
400
+
401
+ - README corrected: the bridge mechanism section described only the dir-scoped discovery and claimed the user's global agy config is never touched. Global per-pid registration shipped earlier and is now documented, along with the delegation suppression and its window.
402
+
403
+ ## [1.5.0] - 2026-09-08
404
+
405
+ ### Added
406
+
407
+ - First-run engine picker. The choice of engine is left to the user: on a fresh install (no `config.json`, no `AGY_ENGINE`), the first interactive pi start opens a modal that explains both engines - stream-json needs the `agy` CLI installed and authenticated; ACP needs a second Google sign-in plus a ~1.5 GB server binary downloaded from Google. stream-json is preselected as the default; `esc` defers (nothing is written, the modal reappears next start). Headless sessions and existing installs are never asked.
408
+ - The ACP pick chains setup immediately instead of waiting for a restart: the ~1.5 GB server binary downloads right away (live percent in the status bar, phase milestones as chat lines), the Google sign-in opens when it lands, and a restart applies the engine. The next start's self-heal sees binary + auth settled and stays silent.
409
+ - `/agy engine` with no arguments (TUI) opens the same picker modal with identical semantics: plan mode blocks acp, an acp pick chains the download + sign-in, same-engine picks ack, esc acknowledges. Direct `/agy engine acp|stream-json` and the headless usage line are unchanged.
410
+ - Missing-CLI warning: while the stream-json engine is active and the `agy` binary cannot be found (PATH or `AGY_BIN`), every pi start warns with the official install URL (toast in the TUI, stderr headless) until the binary is detected. Auth state is not checked, presence only.
411
+
412
+ ### Fixed
413
+
414
+ - The picker's ACP line no longer claims "adds image input and native diffs": tool-result images ride both engines (probe-verified 2026-09-07) and stream-json has its own diff rendering. A negative test pin keeps the claim from returning.
415
+
416
+ ### Changed
417
+
418
+ - README streamlined: the engine capability table, switching, and setup/auth details moved to docs/ENGINES.md; the approval-gate mechanics, configuration, and sample extension moved to docs/APPROVAL-GATE.md. Both stay linked from the README, which now carries the quick surface and states that engine choice is the user's.
419
+ - Dependencies: fast-uri 3.1.5 → 3.1.7 clears four high-severity advisories (repeated hostname percent-decoding SSRF, malformed IPv6 normalization SSRF, percent-encoded scheme and IDN host confusion; in-range lockfile bump under ajv's `^3.0.1`), qs 6.15.3 → 6.16.0 clears a moderate pair (via express, dev surface). npm audit reports zero.
420
+
421
+ ## [1.4.10] - 2026-09-07
422
+
423
+ ### Added
424
+
425
+ - Approval gate for agy native tool calls. agy runs its own agent loop, and its mutating native tools (`run_command`, `create_file`, `edit_file`, ...) executed with no pi involvement: pi's permission extensions never saw them. The gate routes the calls through a pi-side approval in builtin shape, so the existing permission-extension ecosystem gates them with zero changes: a staged `.agents/hooks.json` `PreToolUse` hook parks the call in the bridge, the provider emits a pi `toolUse` for a builtin-shaped shadow tool (`bash`/`write`/`edit`), and the decision - allow, or deny with a reason the model sees - travels back to the hook. Marker calls never execute locally and are verified against the bridge's pending-ticket set (a forged marker denies even in allow mode); non-marker calls delegate to factory twins of the real builtins, so normal pi behavior is unchanged. Read-only agy tools stay ungated. Opt-in via `approvals.gateMode` (`auto`, the default, keeps it off until a known pi permission extension is detected; `shadow` forces on; `off` forces off) and `approvals.mode` (`ask`, the default, shows a pi confirm dialog and denies headless; `allow`/`deny` skip the dialog). Timeout, an unwired gate, and shutdown all deny fail-closed. Every decision lands in the daily log with tool names, source, and latency. README has the full section plus a sample gate extension.
426
+
427
+ ### Fixed
428
+
429
+ - Concurrent pi sessions on the same workspace no longer disable each other's approval gate. The staged `hooks.json` group was one shared key, so a second session resolving the gate to `off` deleted the first session's live gate, and two gate-on sessions overwrote each other's port/token/script. Groups are now keyed per session (`pi-bridge-gate-<pid>`), shutdown removes only the session's own group, and the next staging sweeps groups whose owning session is dead (found by an external pre-release audit).
430
+ - The shared `~/.gemini/config/mcp_config.json` now lands `0600` (dir `0700`). It carries the bridge's shared-secret token and previously landed at the umask default (typically world-readable) (found by an external pre-release audit).
431
+ - Bridge tools now register for the stream-json engine. That engine registered nothing: the agy CLI discovers MCP servers from `~/.gemini/config/mcp_config.json`, which the bridge never wrote (only the per-invocation `--add-dir` config existed), and the gap was masked because the daily engine is ACP. The bridge now registers itself there per-pid (`pi-bridge-<pid>`) at session start, sweeps stale entries left by crashed sessions, and unregisters on shutdown. Foreign servers in the shared file are preserved; a corrupt file is refused, never rewritten. Probe-verified live: agy discovers the server and completes tool calls through its native `call_mcp_tool` wrapper.
432
+ - Image blocks in tool results now reach the model on the stream-json engine too (they were ACP-only). The engine gate downgraded pixels to a text label over an unverified transport concern; a live probe settled it - the CLI's MCP client delivers tool-result image content, and the model named a two-tone PNG's halves from the tool result alone with no decoders in the frame trail. `read` on an image file now gives agy real pixels on both engines. Stream-json prompt attachments and the late-delivery prompt stay text-only by design.
433
+
434
+ ## [1.4.9] - 2026-09-05
435
+
436
+ ### Added
437
+
438
+ - Warn toasts in the pi UI: bridge tool failures, stalls, timeouts, process exits, and other warnings surface as native warning notifications the moment they happen, so users see them and can report them. Over SSH or without a UI the same text falls back to stderr. Deliberate aborts, connection exits (the turn's own error block carries real crashes), and `call-tool-fail` (same-instant duplicate of `round-trip-fail`) stay silent.
439
+ - Gate B watch in `/agy doctor`: the ACP connection latches the first `session/update` payload that carries usage/token fields, and doctor prints one "acp tokens: AVAILABLE" line when that happens. Silent until then; the day the line appears, wiring real token counts becomes a small mapping job. Auth-style string `"token"` keys inside tool frames cannot trip the latch.
440
+
441
+ ### Fixed
442
+
443
+ - Esc-abort on the ACP engine no longer dumps the dying server's raw stderr tail (a google3 stack trace) into the transcript. Teardown exits (abort kill, shutdown, idle recycle) are marked expected and never render; a genuine mid-turn crash surfaces as the turn's own clean error block instead. Raw tails still land in the file log for post-mortems.
444
+
445
+ ### Changed
446
+
447
+ - The ACP engine is now beta. Parity is verified live against RC01 (text streaming, multi-turn resume, bridge tools, effort switching, serialization, abort recovery - see `scripts/parity-live.mjs`), so it graduates from opt-in-experimental to a supported alternative engine. It stays behind `config.engine` for now; the two known RC01 gaps (no usage fields, kill+reload abort) are documented in the README.
448
+ - Daily log volume: the default tier now writes ONLY errors, so a regular session costs the disk nothing. Warns live in the UI (see above); `AGY_DEBUG=1` restores the full debug/info/warn/error trail for reproducing a problem. Docs: README debug-logs section, `/agy doctor` hint.
449
+ - The tool-priority note now steers agy's native `view_file` (artifact-sandboxed on RC01) to the Pi Bridge file tools (`read`, `ls`, `grep`, `find`, `edit`, `execute`) for any real filesystem path. Observed live: repeated `invalid_args` rejections on `operator/pkg/tmux/client.go` before the model fell back to `edit`.
450
+
451
+ ## [1.4.8] - 2026-09-05
452
+
453
+ ### Added
454
+
455
+ - Early-ack + poll for long bridge calls. agy's MCP client abandons a `tools/call` request at a flat ~180s (observed twice at exactly 180.000s), so any pi tool that ran longer died with "agy disconnected before the tool result arrived": the 225.7s `AskClaude` peer review that exposed it never reached agy, and agy salvaged its turn without the result. Now a call still running after ~20 seconds settles the HTTP request with a `STILL RUNNING` answer carrying a `callId`, and the new bridge-local `bridge_poll_result` tool returns the result when it lands (or "still running" on the way). pi keeps executing the whole time; fast calls never see any of this. An escalated park re-arms its own timeout to 30 minutes, so human-gated tools (commit previews, permission dialogs) can take as long as the human takes.
456
+ - Late tool-result delivery as a backstop: a park that does fail (abort, timeout, recycle) leaves a bounded tombstone, and when the toolResult arrives anyway the provider re-routes it to agy as a new prompt in the same conversation ("Late tool delivery: ...") instead of erroring the turn. A late result that lands while another park still anchors the pass is deferred to the next pass (`late-result-deferred`), never dropped.
457
+ - A `progress-token` probe in the bridge server: if agy's requests ever carry `_meta.progressToken`, MCP progress notifications become a testable zero-UX fix for the deadline. The exact-180s signature says it likely never fires; one log line settles it.
458
+
459
+ ### Fixed
460
+
461
+ - A toolResult whose park already died no longer misclassifies the turn as "No user message to send to agy." That was the second half of the incident, and it turned a recoverable late delivery into a hard error.
462
+ - `failAll` (fired on every turn end, OK turns included) no longer marks escalated calls failed: an escalated call outlives its agy turn by design, and the poll handle must not lie about a still-running tool (peer-review blocker).
463
+ - `EscalationRegistry` eviction can no longer strand a running call: only settled entries evict, so a saturated cap grows instead of losing an in-flight result.
464
+
465
+ ### Changed
466
+
467
+ - The tool-priority note now also teaches the poll pattern: long bridge calls answer `STILL RUNNING` + `bridge_poll_result`, and work that is known-long should use `exec_command`'s session output or background agents.
468
+ - New daily-log events: `call-tool-escalated`, `poll-tool`, `late-result` (with a `freshConversation` flag), `late-result-deferred`, `progress-token`. Docs: README bridge section, ACP-PROTOCOL-REFERENCE timing table.
469
+
470
+ ## [1.4.7] - 2026-09-05
471
+
472
+ ### Added
473
+
474
+ - Daily debug log on disk, sorted by day: `~/.pi/extensions-data/estebanforge/pi-antigravity-bridge/logs/<YYYY-MM-DD>.ndjson`, one JSON record per line, 14-day retention. Built for support: when something breaks, the last days' files replay the failure (engine, session, bridge call, error) without reproducing it. Both engines and everything around them feed it: turn start/outcome with error text, driver failures (stall, abort, timeout, nonzero exit), ACP session load/new and connection exits, bridge tool calls and round-trip failures, AskAntigravity runs, `/agy` commands, ACP setup/self-heal/auth, and the login URL (query string stripped). Secret-shaped values (tokens, API keys, credentials, header blocks) are redacted; prompt text, tool arguments, and tool output never land in the log; an unwritable directory is skipped silently and retried. `/agy doctor` prints the directory.
475
+ - Two verbosity tiers keep the disk cost negligible: by default only the info/warn/error skeleton is written (a handful of records per turn). `AGY_DEBUG=1` (or `true`/`on`) restores the full per-event trail: spawn/exit, session load/new, unparks, recycle causes, raw bridge chatter. Turn it on to reproduce, then off.
476
+ - Pre-dispatch turn errors now reach the log (previously they only surfaced as a one-line pi error and were lost): driver start failures, stray tool results with no active turn, the ACP plan-mode refusal, and a miswired extension.
477
+ - The engine capabilities comparison table in the README: 20 rows comparing `stream-json` vs `acp` (thinking text, token usage, image/audio input, plan mode, slash-command handling, model/effort switching, process lifecycle, session resume, abort, bridge routing, edit diffs, permissions, digest and system-prompt delivery, auth, wire protocol, doctor diagnostics), peer-reviewed against the code and the live probes.
478
+
479
+ ### Changed
480
+
481
+ - `/agy doctor` prints the log directory with a hint to attach recent days' files when reporting issues.
482
+ - README, architecture module map, and the regression-test list document the logger, its support flow, and the `AGY_DEBUG` gate.
483
+
484
+ ## [1.4.6] - 2026-09-04
485
+
486
+ ### Added
487
+
488
+ - Tool-priority note in the system prompt block (`systemPrompt` setting, on by default): agy runs embedded in pi, so its native interactive tools (the live example was `ask_question` vs the bridge's `ask_user_question`) never reach the user. The note tells agy to always prefer the Pi Bridge tool when one covers the same purpose. The old preamble sentence that allowed "your own tools or the pi tool bridge" is gone, since that ambiguity was the cause.
489
+
490
+ ### Fixed
491
+
492
+ - `/agy auth` reports a distinct failure instead of a false "Signed in." when the server accepts the sign-in but `acp_token.json` never appears within the 5 s post-authenticate grace window (peer-review find; the poll result was never read).
493
+ - Test-infra: the fake ACP server's request log was lost on SIGTERM (buffered write stream + Gate D teardown), which flaked the Gate D abort test ~1-in-3 suite runs; the log now appends synchronously.
494
+
495
+ ### Changed
496
+
497
+ - The sign-in URL toast fences the URL with blank lines so it stays readable and copyable next to the ssh port-forward hint.
498
+
499
+ ## [1.4.5] - 2026-09-04
500
+
501
+ ### Added
502
+
503
+ - Tool-priority note in the system prompt block (`systemPrompt` setting, on by default): agy runs embedded in pi, so its native interactive tools (the live example was `ask_question` vs the bridge's `ask_user_question`) never reach the user. The note tells agy to always prefer the Pi Bridge tool when one covers the same purpose. The old preamble sentence that allowed "your own tools or the pi tool bridge" is gone, since that ambiguity was the cause.
504
+
505
+ - `/agy auth` subcommand: runs the antigravity-acp sign-in on demand (spawns the server, sends `authenticate`, waits for the browser round-trip to complete; minutes-scale). Signing in no longer rides the first Antigravity message, and both login-pending moments now point at `/agy auth`. The sign-in URL toast (and its ssh port-forward hint) fires during `/agy auth` exactly as it does during turns, and a second concurrent `/agy auth` fails fast instead of racing the token write.
506
+
507
+ ### Changed
508
+
509
+ - `/agy acp-auth` renamed to `/agy auth-manual` (no alias): it prints the manual credential setup for those who want it. Auth-error remediation, MANUAL_SETUP, README, and docs now point at `/agy auth` / `/agy auth-manual`, and the stale first-message login wording is gone from the manual steps.
510
+
511
+ ## [1.4.4] - 2026-09-04
512
+
513
+ ### Added
514
+
515
+ - The Google sign-in URL now surfaces in pi. The ACP server hands the OAuth URL only to the browser-open call (nothing on stdout/stderr, headless or not), so a `BROWSER` wrapper script records the URL and forwards to the real opener; the connection watches the record file and logs it as an `auth-url` event. The extension toasts it at warning level with the ssh port-forward command for the redirect port, so logins work from SSH sessions on remote machines; local users keep the automatic browser open and get the URL as a fallback. An existing `BROWSER` setting is chained, not replaced.
516
+
517
+ ### Fixed
518
+
519
+ - ACP login pending was reported only at the moment setup wrote `settings.json`, so after the restart, with `settings.json` in place but the browser login never completed, every check went silent: no toast, no hint, while the first ACP message had no token to use. `needsLogin` now tracks the token file (stat only, never read): `oauth-personal` without `acp_token.json` is login-pending, so the switch-time toast, the picker, and the `session_start` self-heal keep saying so on every start until the login is done.
520
+
521
+ ### Changed
522
+
523
+ - Engine switching is command-only now: `/agy engine stream-json|acp`. The bare-`/agy` settings picker no longer carries an Engine row (nor the post-save setup run), so the switch surface cannot be hit accidentally; `/agy engine acp` keeps the self-service setup (binary + auth) and the login warning. The picker's plan+acp (RC01) guard stays in a narrower form: the mode row alone can still produce `plan` while the engine is `acp`.
524
+ - ACP login-pending messages rewritten for end users: what happens (the server opens the Google sign-in page in your browser on the first Antigravity message), when (after the restart or immediately, per moment), and which account (your Antigravity subscription, the same Google account as the `agy` CLI). No URL to open manually. All remaining moments (engine switch, session start) now toast at warning level so the pending action stands out.
525
+ - The "Turn engine" wording became "Engine" in the README env table, the config comment, and the architecture doc heading; the picker row itself is gone (engine switching is command-only, see above).
526
+
527
+ ## [1.4.3] - 2026-09-04
528
+
529
+ ### Added
530
+
531
+ - AskAntigravity tool toggle: `/agy ask on|off`, an "AskAntigravity tool" row in the bare-`/agy` picker, and the `askTool` config key (env `AGY_ASK_TOOL`). Default on; `off` skips registering the AskAntigravity one-shot delegation tool entirely, for users who want only the provider and its models and no delegation tool in the model's window context. When the separate pi-ask-antigravity package is also installed, `off` means no delegation tool from either package (no fallback). Takes effect on the next pi start (or /reload); `/agy status` shows the state.
532
+
533
+ ### Changed
534
+ ### Changed
535
+
536
+ - The bare-`/agy` settings picker gains a Turn engine row (`stream-json`/`acp`). Saving `acp` persists the switch, then runs the same self-service setup as `/agy engine acp` (binary install + auth bootstrap, `acp.bin` updated). The plan+acp RC01 guard now evaluates the effective engine AND mode after the save, so a config already in `plan` plus a fresh `acp` pick is refused instead of persisting an invalid combination (peer-review find).
537
+ - "Tool default model/thinking" renamed to "AskAntigravity model/thinking" across the picker, `/agy status`, and toasts; descriptions and README now state the values are fallbacks that callers may override per call.
538
+ - All ACP login-pending messages share one wording that names the antigravity-acp server and its place in Google's Antigravity suite (agy desktop, agy editor, agy cli, agy acp), and clarifies the login: same Google account as the agy CLI, separate login with its own token file.
539
+
540
+ ## [1.4.2] - 2026-09-04
541
+
542
+ ### Changed
543
+
544
+ - `bridgeTools` now defaults to `all` (every registered non-builtin pi tool) instead of `mcp`. The `mcp` surface filters to pi-mcp-adapter tools and serves an empty catalog on installs without that adapter, which left the bridge registered but tool-less from the model's point of view. `none` still opts out entirely; explicit `"bridgeTools": "mcp"` in an existing config keeps pinning the narrow surface.
545
+ - Skill discovery now mirrors pi's directory-based scan (docs/skills.md): global `~/.pi/agent/skills` AND `~/.agents/skills`, project `.pi/skills` plus `.agents/skills` in cwd and ancestors up to the git root (project dirs only when pi has trusted the project, same gate pi applies), recursive SKILL.md discovery with grouping folders, per-style root/`.md` rules, hidden entries skipped, and description-less skills dropped. Pi's other skill sources (`skills` settings array, `package.json`, `--skill`) are not mirrored. Previously only one flat level of two directories was scanned, and `~/.agents/skills` (where pi actually reads most skills) was missing entirely, so `activate_skill` never appeared.
546
+ - `/agy` now exposes the full runtime config surface: new `/agy bridge all|mcp|none` (which pi tools the MCP bridge hands to agy) and `/agy acp-bin <path|auto>` (target a specific ACP server binary; applies on the next ACP turn), plus Bridge tools, Context digest, and System prompt rows in the bare `/agy` settings picker. Usage strings and `/agy status` list every knob.
547
+
548
+ ## [1.4.1] - 2026-09-04
549
+
550
+ ### Added
551
+
552
+ - Self-service ACP setup (`src/acp/setup.ts`): `/agy engine acp` now prepares the whole engine instead of printing instructions. It installs the official server binary from the antigravity-acp registry entry (`agentclientprotocol/registry`) into the pinned layout `~/.local/opt/agy-acp/<build>/` with a `current` symlink and the zip sha256 recorded (no upstream checksums exist; plan §12), points `acp.bin` at it, and prepares the login: `oauth-personal` by default, which is the user's own Antigravity subscription (the same Google account and plan as the `agy` CLI; the server opens the browser on the first ACP message, tokens persist). Credential values are never read or written (`acp_token.json` is only stat()ed); `gemini-api-key` stays a manual headless option, never the default. A `session_start` self-heal repeats the check silently when everything is ready and surfaces the manual steps only on failure; `AcpDriverOptions.bin` accepts a resolver so a mid-session install is picked up by the next turn without a restart; `/agy doctor` shows binary source and auth type. Tests inject the registry, archive, and unpacker, so the suite stays offline.
553
+
554
+ ### Fixed
555
+
556
+ - Stream-json frames split across pipe chunks are now reassembled instead of dropped: `AgyDriver` buffers the trailing partial stdout line (the scheme `JsonRpcSession.feed` already used on the ACP engine). Previously a large `tool` frame or the `result` frame split by a pipe boundary was lost whole, which could turn a successful turn into a bogus "agy exited with status 0" error. Regression test drives a fake agy whose reply is deliberately split mid-line.
557
+ - Both drivers attach an `'error'` listener on the child's stdin: an async pipe failure (EPIPE when agy/the ACP server dies mid-write) now fails the turn (ACP: tears the connection down) instead of escaping as an uncaught exception that kills the whole pi process.
558
+ - ACP permission answering is fail-closed: `session/request_permission` selects the first reject option unless the turn runs with `skipPermissions`. Previously every request was auto-approved regardless of the `permissions` setting shown by `/agy status`. The unsupported `engine=acp` + `mode=plan` combination is now refused at `/agy engine`, `/agy mode`, and the settings picker, and fails the turn with a visible error instead of silently running non-plan (ACP has no review-only mode in RC01).
559
+ - Startup-log fix hardening: four genuine ACP failure events (`session-load-failed-creating-fresh`, `connection-exited`, `cancel-failed`, `unsupported-server-request`) surface again; the dead MCP entries (`capability-missing`, `self-patch-error`) are gone; `call-tool-fail` no longer toasts for the routine fail-all on turn end / session shutdown.
560
+ - Startup log leaks: the ACP driver log sink now forwards only genuine failures (`start-failed`, `spawn-error`, `parse-error`, `write-failed`, `mode-apply-failed`, `timeout`, `stall`, `auth-required`) to stderr; routine lifecycle (`driver-created`, spawn, session new/load) stays in the `#lifecycle` ring buffer under `/agy doctor`. The MCP bridge logger no longer toasts (or headless-stderr-logs) normal startup/teardown events (`listening`, `bridge-config-written`, `bridge-config-removed`, `closed`); only failures surface, as warning toasts. The stream-json engine already had no terminal sink.
561
+
562
+ ## [1.4.0] - 2026-09-04
563
+
564
+ ### Added
565
+
566
+ - The official-server ACP engine as a second turn engine behind `config.engine` (env `AGY_ENGINE`, `/agy engine acp|stream-json`), default off: the tested stream-json engine stays default until upstream ships usage fields. New modules: `src/acp/jsonrpc.ts` (NDJSON JSON-RPC session with line buffering - stdio chunks are not newline-aligned), `src/acp/connection.ts` (initialize, session/new and load, config options, in-connection auto permissions, cancel probing), `src/acp/events.ts` (update mapping), `src/acp/driver.ts` (AcpDriver over the shared `TurnDriver` contract in `src/driver-types.ts`). Sessions are engine-scoped (`sid:<id>@acp`) so switching engines never crosses conversations.
567
+ - `/agy engine acp|stream-json` command and `/agy acp-auth` (one-time credential setup for the ACP server, which keeps its own auth state). `/agy doctor` is engine-aware.
568
+ - `scripts/smoke-acp.mjs` (live ACP smoke), `scripts/smoke-acp-bridge.mjs` (live Gate F bridge e2e), `scripts/parity-live.mjs` (live parity run: 7 scenarios through BOTH engines). All quota-gated via `AGY_ACP_LIVE=1`.
569
+ - `docs/ACP-ADOPTION-PLAN.md` (adoption plan, gates, progress tracking) and `docs/ACP-PROTOCOL-REFERENCE.md` (captured wire shapes of the ACP server).
570
+ - Image prompt support on the ACP engine: pi image attachments ride as typed content blocks (`DriverTurnRequest.images`, forwarded by `connection.prompt` ahead of the text block). Models advertise `input: ["text","image"]` only when the engine is `acp` (read at extension load; engine switches require a restart), so the text-only legacy CLI never offers an attach button it cannot honor. Verified live: a 64x64 two-tone PNG answered correctly through the full driver stack (`scripts/smoke-acp-image.mjs`, quota-gated) and in the phase-2 probe (`scripts/probe-acp-phase2.mjs`).
571
+ - `scripts/probe-acp-phase2.mjs`: live probe capturing thought-chunk sparsity, image end-to-end, full tool_call/tool_call_update frames, and the `/plan` command flow (raw frames in `probe-logs/`).
572
+
573
+ ### Changed
574
+
575
+ - `McpServerHandle` exposes the shared-secret token (`token`), and `TOKEN_HEADER` is exported: engines other than the stream-json discovery file need the header to reach the bridge (the ACP registration was silently sending no header and would have been 403'd).
576
+ - Driver exit handling is connection-scoped in `AcpDriver`: a killed connection's late exit (the current ACP build intercepts SIGTERM and can outlive its replacement) no longer clobbers the live connection or fails the recovery turn.
577
+ - `tool_call_update` display: the completed call's output now prefers the `content[]` text over `rawOutput`, which the server fills with a display title ("Call bridge_echo") rather than the result. The result itself reaches the model out-of-band; only the activity display was wrong.
578
+ - Tool-frame mapping fixes from the phase-2 probe: MCP `rawInput` args unwrap the `arguments` envelope, and the tool name prefers `_meta.mcp.tool` over the "<server>_<tool>" title.
579
+ - Gate C consolidation on the ACP engine: `native-tools.ts` re-exec and `WrapperReplay` parking are retired for ACP turns (tool steps render as thinking labels; `bridge_call` round-trips unaffected); ACP-native edit diffs from `tool_call` `content[]` render directly into the thinking stream via the new in-memory `formatInlineDiff` (same line-numbered format, zero git subprocesses). The git-sourced `diff-render.ts` path remains solely for the `stream-json` engine.
580
+ - G1 digest delivery split by engine: on ACP the digest ships as a native `embeddedContext` resource block in the prompt array (`promptCapabilities.embeddedContext` verified live: a resource block with a secret word was read and answered correctly); `stream-json` keeps the inline text default.
581
+ - `/agy doctor` (ACP): shows server reconnects (connections beyond the first = Gate D kills + replacements) and the handshake `agentInfo` name/title next to the server version.
582
+
583
+ ### Fixed
584
+
585
+ - `tool_call_update` failed frames whose `rawOutput` matches the RC01
586
+ approved-but-never-executed sentinel are dropped instead of rendering as a
587
+ bogus tool error next to a successful edit (run 6, finding 7).
588
+ - `AcpDriver` kills a leaked server process when the initialize handshake
589
+ fails (spawn succeeded, init timed out) - the detached process would
590
+ otherwise outlive pi.
591
+ - `AcpDriver` snapshot prefers the active session id over the last settled
592
+ one, so `/agy doctor` during a live turn shows the correct session.
593
+ - Text dedupe guard: the cumulative-mode flip now requires a respectable
594
+ accumulation (32+ chars), and a cumulative frame that stops extending the
595
+ accumulator falls back to append mode - a short markdown opener (`**`,
596
+ `#`) followed by an ordinary delta no longer corrupts the remaining output
597
+ (round-7 review, applies to both engines).
598
+
599
+ - No usage fields anywhere: token display shows zero (Gate B; the stream-json engine stays default until upstream ships usage).
600
+ - No `session/cancel` (-32601): abort tears the connection down and reloads on the next turn; `cancelSupported` is probed once and shown in `/agy doctor`.
601
+ - `tool_call` activity output shows the server's display string, not the MCP result content (the model receives the result out-of-band; only the activity display lacks it).
602
+ - `agent_thought_chunk` is sparse on RC01 (a step-by-step prompt produced zero; reasoning ships as plain message text). The thinking pipeline handles deltas when they occur.
603
+ - ACP has NO review-only mode: the three modes are permission modes only, and the server-intercepted `/plan` command writes its artifact under auto policy. Plan delegations keep the legacy `agy -p --mode plan` path (committed exception).
604
+
605
+ ## [1.3.3] - 2026-09-01
606
+
607
+ ### Added
608
+
609
+ - pi system prompt injection (G10): pi's composed system prompt - operating instructions plus the global agent-dir `AGENTS.md` and ancestor `AGENTS.md`/`CLAUDE.md` files - is prepended as a delimited block to the first prompt of each NEW agy conversation. agy has no system-prompt flag, so the prompt text is the only delivery path; the block is sent once per conversation and stays byte-identical afterwards, so agy's server-side prompt cache keeps hitting (unlike the per-turn G1 digest, which stays off by default for that reason). On by default (`config.systemPrompt`, `AGY_SYSTEM_PROMPT`, `/agy system-prompt on|off`); existing conversations keep the version they started with.
610
+
611
+ ## [1.3.2] - 2026-09-01
612
+
613
+ ### Removed
614
+
615
+ - The `legacy-sqlite` fallback engine: `src/runner.ts`, `src/poller.ts`, `src/protobuf.ts`, the `run-agy` and `decode-db` scripts, and the `engine` config key / `AGY_ENGINE` env var. agy 1.1.18 changed step-row storage to a two-phase write (a placeholder row first, grown in place later); the polling engine read each row once as an empty placeholder and never re-read it, so turns completed with the full reply in the database and zero text in pi (issue #1). The engine decoded an undocumented storage format, so every agy storage change risked repeating that failure silently. The stream-json engine shares none of that code path; verified live against agy 1.1.18-era storage (1.1.23 installed). A stale `engine` value in an existing `config.json` is ignored.
616
+ Reported by @imatimba in #1. Thanks for the exact repro and the root-cause analysis; the report drove this removal.
617
+
618
+ ### Changed
619
+
620
+ - `scripts/test-provider.ts` wires the stream-json driver explicitly (it exercised the legacy path implicitly before).
621
+ - `tests/provider-streaming.test.ts` covers effort mapping against a fake driver. The legacy event-mapping tests died with the engine; stream-json event coverage lives in `tests/stream-roundtrip.test.ts`.
622
+
623
+ ## [1.3.1] - 2026-08-31
624
+
625
+ ### Changed
626
+
627
+ - Docs-only release. README and package description now describe the 1.3.0 reality: `stream-json` engine as default, no-patch tool bridge, `/agy doctor` + `/agy patch-cleanup`, live token usage. The 1.3.0 tarball shipped the pre-rewrite README, so npm and the pi.dev package gallery still showed the patch-era docs; this republish refreshes the registry metadata.
628
+
629
+
630
+ ## [1.3.0] - 2026-08-31
631
+
632
+ ### Added
633
+
634
+ - Stream-json engine: one persistent `agy --input-format stream-json` process per provider; conversation binding from the `init` event (no more SQLite snapshot diffing), native tool-step events (no protobuf decoding), and token usage mapped onto pi's usage when agy reports it. `AGY_ENGINE=legacy-sqlite` keeps the old engine for one release.
635
+ - No-patch MCP tool bridge: bridge calls park in a round-trip store; the provider emits them as real pi `toolUse` turns, pi executes with native cards/permissions/hooks, and the toolResult completes the parked MCP response on the next stream call. `bridgeTools` config selects the surface: `none` / `mcp` (default) / `all`.
636
+ - Native re-execution of agy read-only tools as real pi builtins (native cards, live output).
637
+ - Display-only `antigravity` wrapper tool: mutating agy steps land as real toolCall/toolResult pairs via recorded-output replay.
638
+ - Skills bridge: `activate_skill` tool exposing the pi Agent Skills catalog to agy, answered by the bridge directly.
639
+ - `/agy doctor`: engine, bridge, driver counters, and lifecycle tail, zero tokens.
640
+ - Legacy patch cleanup: `src/patch-cleanup.ts` detects a leftover invokeTool patch, one-time notice on session start, and `/agy patch-cleanup` restores the original files from the versioned backup.
641
+
642
+ ### Changed
643
+
644
+ - The MCP tool bridge no longer patches pi. The `pi.invokeTool` round-trip is replaced by the provider-owned park/emit/resolve flow above.
645
+
646
+ ### Fixed
647
+
648
+ - Live stream-json protocol shapes against real agy: terminal status is `SUCCESS` (not `OK`) and agent text arrives as `text_delta`. The first burn-in turn failed on both; both are pinned by a regression test.
649
+ - Native re-exec tool calls include the `reasoning` argument pi requires on read/edit-class builtins; without it pi rejected every native `read` card at validation.
650
+ - Peer-review round 2 (engine): parked bridge calls suspend the stdout idle timer (a >5-minute permission prompt no longer kills the turn); turn lifetimes are serialized (a second `run()` can no longer orphan an open turn); the cumulative-text guard points the right direction; a settled turn fails round-trips parked against it.
651
+ - Peer-review round 2 (cleanup): backup selection prefers an exact version match over newest-by-mtime (stacked multi-version backups made legitimate restores refuse); `WrapperReplay` entries are single-use (no unbounded growth, no enumerable stale outputs); `rt`-kind round-trips are removed on turn death; the one-time leftover-patch notice is headless-safe (`ctx.hasUI` gate with stderr fallback) and set after surfacing, not before.
652
+
653
+ ### Removed
654
+
655
+ - `pi.invokeTool` patch: `src/patcher.ts`, the load-time consent prompt, `/agy patch` subcommands, `docs/PI-INVOKETOOL-PATCH.md`, and the `invokeToolPatchDeclined` config flag.
656
+
657
+
658
+ ## [1.2.6] - 2026-08-28
659
+
660
+ ### Changed
661
+
662
+ - **Offline fallback Flash bumped to Gemini 3.7.** `FALLBACK_MODELS`
663
+ (served only when `agy models` fails or is unauthenticated at load) now
664
+ offers `gemini-3.7-flash` instead of `gemini-3.6-flash`. Live discovery
665
+ always overrides the fallback, so this only affects the picker when agy
666
+ is missing or broken.
667
+
668
+ ## [1.2.5] - 2026-08-24
669
+
670
+ ### Fixed
671
+
672
+ - **Patch updated for pi 0.84.3's bundled runtime.** Two upstream changes
673
+ broke the `pi.invokeTool` patch. First, the `core/extensions/loader.js`
674
+ facade was refactored (`runtime.assertActive()` became a local
675
+ `assertActive()` guard), so the sixth site no longer matched and the
676
+ two-phase apply aborted (fail-closed, no files written). Second, pi's `bin`
677
+ now launches `dist/bundle/cli.js`, a bundled runtime with its own embedded
678
+ core, so patching `dist/core/` could never affect a running pi. The patcher
679
+ now also replaces `dist/bundle/cli.js` with a shim that loads the modular
680
+ `dist/cli.js`, making the six sites live again; `findPiRoot` understands
681
+ the `dist/bundle` argv layout. Tradeoff: pi starts via the modular runtime
682
+ (the bundle's faster startup is forfeited while the patch is applied).
683
+ After upgrading, re-apply via `/agy patch apply` and fully restart pi.
684
+ - **Patcher hardening (peer-reviewed).** Atomic writes now preserve the
685
+ destination's permission bits. Without this, every apply stripped the
686
+ execute bit from `dist/bundle/cli.js` (pi's bin target) and broke the `pi`
687
+ command. The entry redirect is never written after any write error, so a
688
+ failed run can no longer silently switch pi to the modular runtime. Backups
689
+ copy forward from the previous same-version backup, so a repair run after a
690
+ partial patch keeps backups complete and restore still reverts the entry
691
+ redirect; an already-redirected entry with no surviving original now warns
692
+ loudly instead of failing silently later.
693
+
694
+ ## [1.2.4] - 2026-08-13
695
+
696
+ ### Added
697
+
698
+ - **Model, thinking tier, and mode shown next to the tool name.** The
699
+ `AskAntigravity` tool now renders
700
+ `AskAntigravity [model=gemini-3.6-flash, thinking=high]` with a prompt
701
+ preview while a delegation runs, plus a tidy result row
702
+ (`✓ AskAntigravity 12.3s`) with an expandable body. Built on pi's
703
+ `renderCall`/`renderResult` hooks; the values shown are the resolved
704
+ config defaults (model alias + tier), not just the args the caller passed.
705
+ `AgyDetails` gained a `thinking` field.
706
+ - **Opt-in full-context delegation (`includeContext`).** New boolean param
707
+ (default `false`, isolated one-shot unchanged). When `true`, the current pi
708
+ conversation is exported as resolved markdown to
709
+ `~/.pi/extensions-data/estebanforge/pi-antigravity-bridge/` and the prompt
710
+ tells agy to read it first. The run passes `--add-dir` for that folder so
711
+ the sandbox can read it; the temp file is removed after the run.
712
+
713
+ ## [1.2.3] - 2026-08-10
714
+
715
+ ### Fixed
716
+
717
+ - **Model parsing now handles agy's real two-column output.** `agy models`
718
+ prints `<slug> <display label>` per line, and `--model` accepts only the
719
+ slug. `entriesFromRaw` (provider path) applied its slug regex to the whole
720
+ line, so every real line was rejected and the provider always fell back to
721
+ the hardcoded catalog; it now splits column 1 and requires a hyphen, which
722
+ also drops banner words split out of column 1. The AskAntigravity resolver
723
+ swallowed slug + label into `--model`, which agy rejected; it now returns a
724
+ `{model, effort?}` shape that sends Gemini-family bases' base slug to
725
+ `--model` and their tier to `--effort`, while fixed-thinking families
726
+ (Claude, GPT-OSS) keep the exact slug with no `--effort` (agy rejects it for
727
+ them). Matches the provider's collapse + clamping path. Tests rewritten to
728
+ the verified live `agy models` fixture.
729
+
730
+ ## [1.1.2] - 2026-08-10
731
+
732
+ ### Fixed
733
+
734
+ - **MCP bridge startup messages no longer pin above the input.** pi's TUI
735
+ captures extension stderr and pins it above the editor for the whole session,
736
+ which left the `[antigravity-bridge mcp] bridge-config-written` and
737
+ `listening` lines stuck on screen. The lifecycle logger now routes through
738
+ `ctx.ui.notify` (an ephemeral toast that fades); headless print/json modes
739
+ fall back to stderr. Error and diagnostic events use the same channel, so
740
+ they no longer pin either.
741
+
742
+ ## [1.1.1] - 2026-08-06
743
+
744
+ ### Changed
745
+ - **Dependencies updated.** Raised the `pi-coding-agent`, `pi-ai`, `pi-tui` dev pins to `^0.84.0`. Audited against the pi v0.84.0 breaking changes; the custom `streamSimple` provider still matches pi-ai 0.84.0's stream-event contract, and `tsc --noEmit` passes.
746
+
747
+ ## [1.1.0] - 2026-07-31
748
+
749
+ ### Added
750
+
751
+ - **Full agy catalog in the picker, grouped like agy's own.** Gemini models
752
+ collapse to base entries (gemini-3.6-flash, gemini-3.1-pro) with a
753
+ thinking-effort toggle; Claude (Sonnet/Opus) and GPT-OSS appear as fixed
754
+ entries with no toggle, since their thinking cannot be changed. The earlier
755
+ Gemini-only filter is gone. Google's Antigravity subscription bills all of
756
+ these through agy, so routing Claude here uses the agy quota you already pay
757
+ for (if you also run pi-claude-bridge you will simply see two Claude
758
+ entries).
759
+ - **Reasoning-effort bridging (`agy --effort`).** For an effort-driven Gemini
760
+ base, pi's thinking-effort toggle drives agy's `--effort` (agy 1.1.5+), and
761
+ the toggle only offers the tiers that base actually accepts (Flash:
762
+ low/medium/high; Pro: low/high), so it can never request a tier agy rejects.
763
+ The level is clamped to the base's supported tiers and always passed (a base
764
+ slug is invalid on its own). Fixed models never receive `--effort` (agy
765
+ rejects it for them). Behavior verified by local experiments against agy
766
+ 1.1.9.
767
+
768
+ ### Changed
769
+
770
+ - **Breaking: Gemini model ids changed shape.** Effort is no longer part of the
771
+ model id (`antigravity/gemini-3-6-flash-medium` →
772
+ `antigravity/gemini-3-6-flash`); it is now chosen via pi's thinking-effort
773
+ toggle. A persisted default model, a `--model antigravity/...` flag, or a
774
+ scoped-model pattern set before upgrading will need re-selection.
775
+ Claude/GPT-OSS ids keep their qualified shape (e.g.
776
+ `antigravity/claude-sonnet-4-6`).
777
+ - **Requires agy >= 1.1.5.** Base slugs and the `--effort` flag landed there;
778
+ older agy rejects every Gemini entry with a flag error.
779
+
780
+ ### Fixed
781
+
782
+ - **AskAntigravity model resolution** now parses agy's stable-slug catalog
783
+ (`gemini-3.6-flash-high`, `claude-sonnet-4-6`) instead of the legacy human
784
+ names. The `sonnet`/`opus`/`gpt-oss` aliases and the `/agy thinking` tier had
785
+ silently resolved to invalid `--model` values since agy switched to slugs;
786
+ they now resolve to exact valid slugs.
787
+
788
+ ## [1.0.0] - 2026-07-29
789
+
790
+ First release. A streaming Gemini model provider for pi, built on Google's
791
+ `agy` CLI, plus an MCP tool bridge that lets agy use pi's installed tools
792
+ (memory, codegraph, Slack, Asana, web, peer delegation, etc.) instead of its
793
+ own. Registers `antigravity/*` models in pi's `/model` picker and streams
794
+ responses by polling the SQLite database agy writes and decoding its
795
+ protobuf step payloads. No generated protobuf code, no native SQLite
796
+ dependency.
797
+
798
+ ### Added
799
+
800
+ #### Streaming provider
801
+
802
+ - **Gemini provider for pi.** `antigravity/gemini-*` models appear in pi's
803
+ `/model` picker, discovered live from `agy models` (with a fallback catalog
804
+ when discovery fails). Picking one routes each turn through the provider.
805
+ - **Real streaming.** A concurrent poll loop (250ms, `PRAGMA data_version`
806
+ coalescing) reads agy's conversation DB while agy is still running, so text
807
+ and tool activity arrive during the turn, not replayed at exit. Three
808
+ trailing polls catch the final flush; abort skips them for prompt cancel.
809
+ - **Hand-rolled protobuf decoder** for agy's `step_payload` blobs: agent text
810
+ at field 20.1, tool calls at field 5.4 (name@2/9, input@3), title at 30.4.
811
+ Field numbers verified against real agy 1.1.7 databases and cross-checked
812
+ against the shindgew/agy-acp and shubzkothekar/antigravity-acp decoders.
813
+ Unknown fields are skipped per protobuf wire rules.
814
+ - **Multi-turn conversations.** A pi session is bound to an agy conversation
815
+ id (persisted at `~/.pi/agent/antigravity-bridge/sessions.json`) and
816
+ resumed via `--conversation <id>`. agy keeps its own history; only the latest
817
+ user message is sent each turn. Atomic, dirty-key-merged writes survive
818
+ concurrent pi processes.
819
+ - **`/agy` slash command** with status, an interactive picker (mode,
820
+ permissions), and direct subcommands. Settings persist to
821
+ `~/.pi/agent/antigravity-bridge/config.json`.
822
+ - **Configurable execution mode** (`accept-edits` default, or `plan`) and
823
+ permissions, overridable by `AGY_MODE` /
824
+ `AGY_SKIP_PERMISSIONS` env vars.
825
+ - **`--dangerously-skip-permissions` passed by default.** Technically
826
+ required: `accept-edits` auto-approves file edits but not shell commands,
827
+ so a `run_command` would otherwise hang on an unanswerable `y/n` prompt in
828
+ non-interactive `-p` mode (upstream google-antigravity/antigravity-cli#318).
829
+ Consistent with pi's own no-confirmation-gate design.
830
+ - **Conversation-id discovery** by snapshot/diff of agy's conversations dir
831
+ (agy `-p` never prints the id). Refuses to bind on ambiguity.
832
+ - **Tool-activity visibility.** agy's closed tool loop surfaces in pi's
833
+ thinking panel as `[agy tool: <name>]`. agy edits/commands land on disk;
834
+ pi's tools never fire (architectural wall, documented).
835
+ - **Cross-turn context continuity.** agy keeps its own history, but it now
836
+ also receives pi-side context it wasn't spawned for (compaction summaries,
837
+ turns from other providers or pi's own tools) as a brief digest with the
838
+ prompt each turn, so multi-turn work and provider switches stay coherent.
839
+ - **Edit diffs in the thinking stream.** When agy writes a file, pi's thinking
840
+ panel shows a line-numbered diff of the change as it lands. Works across
841
+ nested repos, submodules, and multi-repo workspaces; degrades cleanly for
842
+ binary, off-repo, or unchanged files.
843
+ - **Tests.** Unit tests for the protobuf decoder; a
844
+ deterministic fake-agy test that asserts events stream during the run and
845
+ that abort returns promptly (guards the "provider did not actually stream"
846
+ regression class).
847
+
848
+ #### MCP tool bridge (agy -> pi tools)
849
+
850
+ - **`AskAntigravity` tool** is now provided by this extension (ported from
851
+ `pi-ask-antigravity` v1.1.0). The bridge ships BOTH the streaming
852
+ antigravity provider AND the one-shot delegation tool - the same combined
853
+ shape as `pi-claude-bridge`. Model aliases (flash/pro/gemini, tier/version
854
+ qualifiers), one-shot vs continued-conversation modes, and the `mode`/
855
+ `digest` params are all preserved.
856
+ - **Cross-extension clash avoidance.** When both this bridge and
857
+ `pi-ask-antigravity` are installed, the bridge wins and
858
+ `pi-ask-antigravity` silently registers nothing (it detects the bridge via
859
+ package resolution, order-independent). The `AskAntigravity` tool is never
860
+ duplicated.
861
+ - **`/agy` gains `model` and `thinking` subcommands + picker rows** for the
862
+ tool's defaults (alias flash/pro/gemini; tier low/medium/high). Persisted
863
+ alongside the provider settings in `config.json`.
864
+ - **MCP tool bridge.** agy runs as pi's Gemini provider; the bridge exposes
865
+ pi's installed tools to agy over a Streamable HTTP MCP server so agy can
866
+ call them (memory, codegraph, Slack, Asana, web, peer delegation, etc.)
867
+ instead of doing the work itself. Per-pid config directory at
868
+ `~/.pi/agent/antigravity-bridge/agy-mcp-<pid>/`, written into agy's
869
+ `--add-dir` path so agy reads `.agents/mcp_config.json` from there.
870
+ Global agy config is never touched.
871
+ - **Capability gate.** The bridge checks for `pi.invokeTool` at startup and
872
+ silently no-ops the MCP server if the patch is absent (clean pi reinstall
873
+ drops the patch; bridge still runs as a provider).
874
+ - **Tool filtering.** Builtin tools that agy already has natively (read,
875
+ write, edit, bash, ls, grep, find) are filtered out so agy does not double
876
+ up on its own equivalents; `AskAntigravity` is filtered to avoid recursion.
877
+ Every other registered tool is exposed.
878
+ - **Security.** Shared-secret `x-bridge-token` header, request body size cap,
879
+ per-call `AbortController` so agy can cancel in-flight tool calls, full
880
+ request handler `try/catch`, rawHeaders rewrite for Hono's protocol clamp.
881
+
882
+ #### Documentation
883
+
884
+ - `docs/ARCHITECTURE.md` - bridge design and per-pid config layout.
885
+ - `docs/DEVELOPMENT.md` - how to run tests, rebuild, and iterate.
886
+ - `docs/PI-INVOKETOOL-PATCH.md` - the local patch to pi's dist that the
887
+ bridge depends on.
888
+ - `docs/PI-BRIDGE-GAPS.md` - capability gaps, as actionable tasks (G1-G10).
889
+ G1 (conversation history) and G8 (edit diffs) are closed via no-patch,
890
+ provider/decode-side work; the rest (streaming progress, UI primitives,
891
+ MCP-server double-exposure, etc.) remain open, triaged by effort and payoff.
892
+
893
+ ### Fixed
894
+
895
+ - **Protocol-version clamp for the MCP bridge.** agy negotiates a protocol
896
+ version newer than the SDK this bridge ships: `@modelcontextprotocol/sdk`
897
+ 1.29.0 tops out at `2025-11-25`, but agy sends `2026-07-28`. `initialize` is
898
+ exempt from the transport's header check and the SDK downgrades its body
899
+ version itself, yet every follow-up (`tools/list`, `tools/call`,
900
+ `notifications/initialized`) is validated against the `MCP-Protocol-Version`
901
+ header and rejected with `400 Bad Request: Unsupported protocol version`,
902
+ surfaced as a `transport-error` on every turn. The bridge now rewrites any
903
+ unsupported header value to `LATEST_PROTOCOL_VERSION` before the transport
904
+ sees it. The server is stateless (a fresh transport per request), so it
905
+ cannot track the negotiated version across requests; clamping to LATEST is
906
+ the correct downgrade. Hono's Node->Web conversion reads `req.rawHeaders`,
907
+ not the parsed `req.headers` object, so the value is rewritten in the raw
908
+ array (and mirrored on `req.headers` for other readers).