@estebanforge/pi-antigravity-bridge 1.2.6 → 1.3.1

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.3.1] - 2026-08-31
6
+
7
+ ### Changed
8
+
9
+ - 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.
10
+
11
+
12
+ ## [1.3.0] - 2026-08-31
13
+
14
+ ### Added
15
+
16
+ - 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.
17
+ - 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`.
18
+ - Native re-execution of agy read-only tools as real pi builtins (native cards, live output).
19
+ - Display-only `antigravity` wrapper tool: mutating agy steps land as real toolCall/toolResult pairs via recorded-output replay.
20
+ - Skills bridge: `activate_skill` tool exposing the pi Agent Skills catalog to agy, answered by the bridge directly.
21
+ - `/agy doctor`: engine, bridge, driver counters, and lifecycle tail, zero tokens.
22
+ - 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.
23
+
24
+ ### Changed
25
+
26
+ - The MCP tool bridge no longer patches pi. The `pi.invokeTool` round-trip is replaced by the provider-owned park/emit/resolve flow above.
27
+
28
+ ### Fixed
29
+
30
+ - 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.
31
+ - 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.
32
+ - 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.
33
+ - 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.
34
+
35
+ ### Removed
36
+
37
+ - `pi.invokeTool` patch: `src/patcher.ts`, the load-time consent prompt, `/agy patch` subcommands, `docs/PI-INVOKETOOL-PATCH.md`, and the `invokeToolPatchDeclined` config flag.
38
+
39
+
5
40
  ## [1.2.6] - 2026-08-28
6
41
 
7
42
  ### Changed
package/README.md CHANGED
@@ -8,27 +8,27 @@ If you also have [`@estebanforge/pi-ask-antigravity`](https://github.com/Esteban
8
8
 
9
9
  ## What it does
10
10
 
11
- You pick a Gemini model in pi's `/model` picker. pi routes each turn through this provider. The provider spawns `agy -p` in your workspace, polls the SQLite database agy writes as it works, decodes the protobuf step payloads, and streams the agent text back into pi token by token.
11
+ You pick a Gemini model in pi's `/model` picker. pi routes each turn through this provider. The default `stream-json` engine runs one persistent agy process in your workspace, feeds it each turn, parses its stream-json events, and streams the agent text back into pi token by token. Token usage is live. A `legacy-sqlite` fallback engine (spawn `agy -p`, poll its SQLite conversation DB, decode its protobuf steps) still exists; see [Engine and bridge surface](#engine-and-bridge-surface).
12
12
 
13
13
  Multi-turn works. The provider binds a pi session to an agy conversation id (persisted under `~/.pi/agent/antigravity-bridge/sessions.json`) and resumes it on the next turn via `--conversation <id>`. agy keeps its own history, so only the latest user message is sent each turn.
14
14
 
15
15
  ## What it cannot do
16
16
 
17
- agy runs its own closed tool loop (`read_file`, `write_file`, `edit_file`, `run_command`) against `--add-dir`. By default pi's own `read`/`write`/`edit`/`bash` do not fire for agy's file and shell work, and that is fine: agy has capable native equivalents. What used to be a hard wall for everything else is now bridgeable; see [MCP tool bridge](#mcp-tool-bridge-agy-uses-pis-tools) below.
17
+ agy runs its own closed tool loop (`read_file`, `write_file`, `edit_file`, `run_command`) against `--add-dir`. Its read-only steps (`view_file`, `list_dir`, `grep_search`, `find_by_name`) re-run as real pi builtins (`read`, `ls`, `grep`, `find`) so their cards render natively; mutating steps never execute in pi - they replay through a display-only `antigravity` wrapper tool. What used to be a hard wall for pi's other tools is bridgeable; see [MCP tool bridge](#mcp-tool-bridge-agy-uses-pis-tools) below.
18
18
 
19
19
  Residual limits (with or without the bridge):
20
20
 
21
21
  - agy's own edits still land directly on disk; pi's inline diff review does not engage for them.
22
22
  - agy commands run without per-action approval, same as every other tool in pi. See [Permissions](#permissions) below.
23
- - No token usage or cost accounting. agy does not expose token counts, so usage reports as zero.
23
+ - No cost accounting: cost stays zero because agy runs on your subscription quota. Token usage is live on the `stream-json` engine; the `legacy-sqlite` fallback reports usage as zero.
24
24
 
25
25
  ## MCP tool bridge (agy uses pi's tools)
26
26
 
27
27
  While agy is the active model it normally cannot see pi's universe of extensions: agentmemory, codegraph, web search, slack/asana, the `Ask*` delegations, and any other installed pi tool. This extension optionally bridges that gap.
28
28
 
29
- When the capability is present the bridge starts a localhost MCP server inside pi's process. `tools/list` returns pi's registered tools (built-in file/shell tools and `AskAntigravity` are filtered out), and `tools/call` executes them via `pi.invokeTool()`. agy discovers the server through a per-invocation config: the bridge writes `.agents/mcp_config.json` into a bridge-controlled dir (`~/.pi/agent/antigravity-bridge/agy-mcp-<pid>/`) and the provider passes that dir as an extra `--add-dir` when it spawns agy. The user's global agy config (`~/.gemini/config/mcp_config.json`) is never touched, so standalone agy outside pi is unaffected.
29
+ The bridge starts a localhost MCP server inside pi's process. `tools/list` returns pi's registered tools (built-in file/shell tools and `AskAntigravity` are filtered out), and a `tools/call` routes into pi's own tool loop via the round-trip described below. agy discovers the server through a per-invocation config: the bridge writes `.agents/mcp_config.json` into a bridge-controlled dir (`~/.pi/agent/antigravity-bridge/agy-mcp-<pid>/`) and the provider passes that dir as an extra `--add-dir` when it spawns agy. The user's global agy config (`~/.gemini/config/mcp_config.json`) is never touched, so standalone agy outside pi is unaffected.
30
30
 
31
- **Capability requirement.** `pi.invokeTool()` is not (yet) part of upstream pi. On first load with the patch missing, the extension **asks you once** whether to apply it (see [Install](#install)); once applied it activates after a full `pi` restart. Without it the bridge detects the missing capability at load time, skips the MCP server, and everything else works unchanged. See [docs/PI-INVOKETOOL-PATCH.md](docs/PI-INVOKETOOL-PATCH.md) for what the patch is, and `/agy patch status|apply|restore` to inspect, force, or undo it.
31
+ **No patch required.** Bridge calls park in the provider's round-trip store; the provider ends the pi assistant message with a `toolUse` stop reason for the real pi tool, pi executes it in its own loop (native cards, permissions, hooks), and the toolResult completes the parked MCP response on the next stream call. This is the same mechanism tianzuo/pi-antigravity uses; upstream pi APIs only.
32
32
 
33
33
  **Recursion safety.** Only the provider's agy receives the extra `--add-dir`. The `AskAntigravity` tool spawns its own agy with just the workspace, so that inner agy starts plain (no pi tools) and cannot re-enter. `AskAntigravity` is also filtered from the exposed tool list. Standalone agy is unaffected because nothing is written to its global config.
34
34
 
@@ -36,31 +36,25 @@ When the capability is present the bridge starts a localhost MCP server inside p
36
36
 
37
37
  **Security.** The MCP server binds to `127.0.0.1` only and requires a per-session shared-secret header (`x-bridge-token`) that agy sends from the bridge config; browsers cannot set custom headers on a simple cross-origin POST, so this blocks web CSRF against the loopback server. Request bodies are size-capped. This is intended for single-user developer machines: any local process running as the same user can read the token from the per-pid config and call the exposed tools, so do not run it on a shared host where you do not trust other same-user processes.
38
38
 
39
+ ### Native cards, wrapper replay, and skills (stream-json engine)
40
+
41
+ Read-only agy steps (view_file, list_dir, grep_search, find_by_name) re-run as
42
+ real pi builtins (`read`, `ls`, `grep`, `find`) when those builtins are active,
43
+ so their cards render with pi's own renderers. Mutating and agy-specialty steps
44
+ render through a display-only `antigravity` wrapper tool: its `execute()`
45
+ replays the output agy already recorded, so the transcript gets proper
46
+ toolCall/toolResult pairs without any double execution. Neither path re-runs
47
+ anything with side effects.
48
+
49
+ When the bridge is on, agy also gets one `activate_skill` tool whose enum is
50
+ your pi Agent Skills catalog; calling it returns the SKILL.md body. The bridge
51
+ answers it directly, no pi round-trip. `/agy doctor` prints engine state,
52
+ driver counters, bridge port, and the last lifecycle events without spending
53
+ tokens.
54
+
39
55
  ## Install
40
56
 
41
- > ⚠️ **Heads-up: this extension patches your `pi` install.** When it loads and
42
- > the running pi lacks `pi.invokeTool()`, it **asks you once** whether to edit
43
- > files inside your globally-installed `@earendil-works/pi-coding-agent/dist/`
44
- > (adding one method) to enable the MCP tool bridge. This is, for now, the only
45
- > way agy can use Pi internal tooling.
46
- >
47
- > - **Yes** → applies the patch (reversible via `/agy patch restore`) and tells
48
- > you to restart pi. The bridge starts on the next launch.
49
- >
50
- > - **No** → it remembers your choice and stays silent; it won't ask again until
51
- > you run `/agy patch apply`. The provider and AskAntigravity tool keep working;
52
- > only the MCP tool bridge stays off.
53
- >
54
- > - The apply is **idempotent & safe** (only what's missing; aborts cleanly if a
55
- > pi update moved the code), **backed up** (under
56
- > `~/.pi/agent/antigravity-bridge/pi-patch-backup/`), and **self-healing** (a
57
- > `pi` reinstall/update wipes `dist/`; re-applied on the next start).
58
- >
59
- > The patch only takes effect after a **full `pi` restart** (quit + relaunch) —
60
- > `/reload` is **not** enough, because pi caches its compiled core for the
61
- > process.
62
- >
63
- > Details for the patch can be found in [docs/PI-INVOKETOOL-PATCH.md](docs/PI-INVOKETOOL-PATCH.md).
57
+ > **No patch required.** The bridge runs on pi's public APIs only; the extension never edits your pi install. If an older version of this extension patched your pi (adding `pi.invokeTool()`), the leftover is inert and a pi update removes it. The extension detects it once and offers `/agy patch-cleanup` to restore the original files from the backup immediately.
64
58
 
65
59
  Install with pi's package manager:
66
60
 
@@ -92,6 +86,18 @@ Model ids are slugified from the `agy models` output (`Gemini 3.6 Flash (Medium)
92
86
 
93
87
  If `agy models` fails at load (binary missing, auth not done, network stall), a fallback catalog still populates the picker so you get a clear runtime error instead of an empty list.
94
88
 
89
+ ### Engine and bridge surface
90
+
91
+ `config.json` selects the turn engine and the bridge surface:
92
+
93
+ | Key | Values | Default |
94
+ | --- | --- | --- |
95
+ | `engine` | `stream-json` (persistent agy process, toolUse round-trips, live usage) or `legacy-sqlite` (spawn `agy -p`, poll its SQLite) | `stream-json` |
96
+ | `bridgeTools` | `none` (bridge off), `mcp` (pi-mcp-adapter tools), `all` (every non-builtin tool, incl. other `Ask*` delegations) | `mcp` |
97
+ | `digest` | `off` (stable prompts; agy's prompt cache hits) or `on` (inject a delta of pi-side context - compaction summaries, other-provider turns - into each agy prompt; the delta changes every turn, so agy re-bills the full context). Enable for mixed-provider sessions where agy must see pi-side context | `off` |
98
+
99
+ Env overrides: `AGY_ENGINE`, `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`. The `legacy-sqlite` engine is kept as a fallback and is scheduled for removal once the stream-json engine has burned in.
100
+
95
101
  ### The /agy command
96
102
 
97
103
  `/agy` configures the provider at runtime. Settings persist to `~/.pi/agent/antigravity-bridge/config.json` and take effect on the next turn.
@@ -99,12 +105,14 @@ If `agy models` fails at load (binary missing, auth not done, network stall), a
99
105
  ```
100
106
  /agy status, or open the mode/permissions/model/thinking picker (TUI)
101
107
  /agy status print current mode, permissions, model + session counts
108
+ /agy doctor engine state, driver counters, bridge port, last lifecycle events
102
109
  /agy mode plan review-only: agy plans but writes nothing
103
110
  /agy mode accept-edits agy applies edits directly (default)
104
111
  /agy permissions on|off auto-approve / prompt for tool calls (see warning)
105
112
  /agy model flash|pro|gemini default model alias for the AskAntigravity tool
106
113
  /agy thinking low|medium|high default thinking tier for the AskAntigravity tool
107
- /agy patch status|apply|restore inspect / force / undo the pi.invokeTool patch
114
+ /agy digest on|off inject pi-side context into agy prompts (default off; see engine table)
115
+ /agy patch-cleanup restore the original pi files if an older version patched them
108
116
  /agy clear drop all session bindings (force fresh conversations)
109
117
  ```
110
118
 
@@ -112,7 +120,7 @@ If `agy models` fails at load (binary missing, auth not done, network stall), a
112
120
 
113
121
  pi itself has no built-in approval gate. Unlike codex, claude, or agy running interactively, pi does not prompt you to confirm each tool action before it runs. That is the host environment this extension lives in.
114
122
 
115
- Because agy in `-p` (print) mode cannot answer an interactive `y/n` prompt, this extension passes `--dangerously-skip-permissions` by default. It is technically necessary: `accept-edits` auto-approves file edits but not shell commands, so a `run_command` would otherwise hang forever waiting for a prompt nothing can answer (upstream [google-antigravity/antigravity-cli#318](https://github.com/google-antigravity/antigravity-cli/issues/318)). The net effect is that agy executes commands the same way pi already executes your other tools: without per-action review.
123
+ Because agy runs non-interactively under this provider (nothing can answer a `y/n` prompt), this extension passes `--dangerously-skip-permissions` by default. It is technically necessary: `accept-edits` auto-approves file edits but not shell commands, so a `run_command` would otherwise hang forever waiting for a prompt nothing can answer (upstream [google-antigravity/antigravity-cli#318](https://github.com/google-antigravity/antigravity-cli/issues/318)). The net effect is that agy executes commands the same way pi already executes your other tools: without per-action review.
116
124
 
117
125
  If you want agy to execute nothing, use `/agy mode plan`. Do not combine `--sandbox` with skip-permissions ([#36](https://github.com/google-antigravity/antigravity-cli/issues/36)).
118
126
 
@@ -134,13 +142,13 @@ For isolation when running any agent that executes commands without a confirmati
134
142
 
135
143
  ## Development
136
144
 
137
- Build, test, and debug instructions live in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). For the internal architecture (decode pipeline, polling, conversation discovery) see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
145
+ Build, test, and debug instructions live in [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md). For the internal architecture (engines, bridge round-trips, conversation discovery) see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
138
146
 
139
147
  ## Terms of Service notice
140
148
 
141
149
  Google's [Antigravity ToS](https://antigravity.google/terms) (Section 6) prohibits accessing the service "in connection with products not provided by us", and names as its example using tools like Hermes/OpenClaw with Antigravity OAuth. That targets reusing your credentials in a non-Google harness that calls Google's backend directly.
142
150
 
143
- This extension does not do that. It spawns the official, unmodified `agy` binary as a subprocess; `agy` performs its own OAuth and makes its own calls to Google. This code never sees, extracts, or reuses your token, and never contacts Antigravity's backend. It only reads the local SQLite file `agy` writes. From Google's server-side view there is no signal that distinguishes "agy launched by pi" from "agy launched by a terminal, an IDE task runner, or cron": same signed binary, same authenticated calls.
151
+ This extension does not do that. It spawns the official, unmodified `agy` binary as a subprocess; `agy` performs its own OAuth and makes its own calls to Google. This code never sees, extracts, or reuses your token, and never contacts Antigravity's backend. It only reads what `agy` itself produces locally: its stream-json output on the default engine, its SQLite conversation DB on the fallback engine. From Google's server-side view there is no signal that distinguishes "agy launched by pi" from "agy launched by a terminal, an IDE task runner, or cron": same signed binary, same authenticated calls.
144
152
 
145
153
  Google's reported enforcement to date (the February 2026 suspensions) targeted token-reuse tools, not spawning the official CLI.
146
154
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  Date: 2026-08-21. Sources: the official VSIX `Google.google-antigravity_1.0.0` (marketplace.visualstudio.com/items?itemName=Google.google-antigravity, unpacked at `~/Downloads/Google.google-antigravity_1.0.0`), Zed's external-agents registry cache (`~/Library/Application Support/Zed/external_agents/registry/registry.json`), the ACP release zip from `dl.google.com`, and the binaries installed on this machine. I verified everything below by direct inspection or live execution unless marked otherwise. Section 8 has the reproduction commands.
4
4
 
5
+ > **2026-08-31 (1.3.0) update:** the roadmap sections below predate 1.3.0 and assume the `pi.invokeTool` patch (`src/patcher.ts`, `docs/PI-INVOKETOOL-PATCH.md`) stays. 1.3.0 removed the patch entirely: the MCP tool bridge now runs no-patch via the provider's toolUse round-trip, and `AGY_ENGINE=stream-json` (persistent process) is the default turn engine. Treat every patch-dependent step in sections 9.x as needing rework against the round-trip path; the reverse-engineered protocol facts (sections 1-8) are unaffected.
6
+
5
7
  ## 0. Executive summary
6
8
 
7
9
  Google ships two official mechanisms for driving Antigravity from an editor, plus one hidden one:
@@ -2,27 +2,70 @@
2
2
 
3
3
  How the provider works internally. For build/test/debug workflow see [DEVELOPMENT.md](./DEVELOPMENT.md).
4
4
 
5
+ ## Engines
6
+
7
+ The provider runs one of two turn engines (`config.engine`, default `stream-json`):
8
+
9
+ - **`stream-json` (default):** one long-lived `agy --input-format stream-json --output-format stream-json` process per provider. Turns are fed over stdin; agy emits NDJSON events on stdout; the driver parses them and streams text into pi token by token. Conversation binding comes from the `init` event, tool steps arrive as typed events (no protobuf decoding), and token usage is live.
10
+ - **`legacy-sqlite` (fallback):** the pre-1.3.0 engine - spawn `agy -p`, poll its SQLite conversation DB on a 250 ms interval, decode the protobuf step payloads. Kept as a fallback and scheduled for removal once `stream-json` has burned in (`AGY_ENGINE=legacy-sqlite` selects it).
11
+
12
+ Shared by both engines: session binding (`sessions.json`), runtime config, the `AskAntigravity` tool, the MCP tool bridge surface, and the G1 context digest (off by default - see below).
13
+
5
14
  ## Module map
6
15
 
7
16
  ```
8
- extensions/index.ts registerProvider + model discovery + /agy command (entry)
9
- src/provider.ts streamSimple: pi Context -> agy turn -> pi event stream
10
- src/runner.ts spawn agy -p, concurrent poll loop, abort/timeout, emit events
11
- src/poller.ts read-only node:sqlite handle over one conversation DB
12
- src/protobuf.ts hand-rolled varint walker + extractors (field 20.1 = text)
13
- src/discovery.ts snapshot/diff to bind the conversation id agy never prints
14
- src/models.ts agy models -> pi Model projection
15
- src/sessions.ts atomic JSON store: pi session -> agy conversation + last step
16
- src/config.ts persisted runtime config (mode, permissions, model/thinking defaults)
17
+ extensions/index.ts pi extension entry: provider registration, model discovery, /agy command, lifecycle notices
18
+ src/provider.ts streamSimple: pi Context -> agy turn -> pi event stream; owns the G9 round-trip store and the G1 digest
19
+ src/driver.ts stream-json driver: persistent agy process, turn serialization, conversation binding, idle/abort timers
20
+ src/stream-events.ts agy NDJSON event parser (init / step_update / result) + usage mapping onto pi's Usage
21
+ src/native-tools.ts maps agy read-only tool steps to real pi builtins (read/ls/grep/find) for native re-execution
22
+ src/skills.ts activate_skill bridge: exposes the pi Agent Skills catalog to agy, answered by the bridge directly
23
+ src/patch-cleanup.ts detects a leftover invokeTool patch from pre-1.3.0 installs; /agy patch-cleanup restores the backup
24
+ src/runner.ts legacy engine: spawn agy -p, concurrent poll loop, abort/timeout, emit events
25
+ src/poller.ts legacy engine: read-only node:sqlite handle over one conversation DB
26
+ src/protobuf.ts legacy engine: hand-rolled varint walker + extractors (field 20.1 = text)
27
+ src/discovery.ts legacy engine: snapshot/diff + pid fd-scan to bind the conversation id agy -p never prints
28
+ src/models.ts agy models -> pi Model projection (full catalog, per-model effort)
29
+ src/sessions.ts atomic JSON store: pi session -> agy conversation + watermark
30
+ src/config.ts persisted runtime config (engine, bridgeTools, digest, mode, permissions, model/thinking defaults)
17
31
  src/ask-tool.ts the AskAntigravity one-shot delegation tool (model/thinking defaults)
18
- src/mcp-server.ts MCP tool bridge: exposes pi's tools to agy over Streamable HTTP
19
- src/patcher.ts Auto-applies the pi.invokeTool local patch to enable the MCP tool bridge
32
+ src/mcp-server.ts MCP tool bridge server: ferries tools/list + tools/call; calls park in the provider round-trip
20
33
  src/diff-render.ts render agy's file edits as git diffs in pi's thinking stream
21
34
  ```
22
35
 
23
- No generated protobuf code, no native SQLite dependency.
36
+ No generated protobuf code, no native SQLite dependency (`node:sqlite` covers the legacy engine's reads).
37
+
38
+ ## Stream-json engine (default)
39
+
40
+ ### Process and events
41
+
42
+ The driver spawns `agy --input-format stream-json --output-format stream-json` once per provider and keeps it alive across turns (`/agy doctor` shows the reuse counter). A turn writes the prompt to stdin and reads NDJSON events until the terminal `result`:
43
+
44
+ | event | meaning |
45
+ | --- | --- |
46
+ | `init` | conversation binding (`conversation_id`); the driver remembers it and resumes later turns via `--conversation <id>` |
47
+ | `step_update` | `user_input` / `checkpoint` / `agent_response` / `tool` steps; agent text arrives as `text_delta` on live agy (1.1.13+), with `usage` attached |
48
+ | `result` | terminal; live builds report status `SUCCESS` (older builds `OK` - both accepted) |
49
+
50
+ Unknown event kinds parse as `{kind:"unknown"}` so a future agy release degrades instead of crashing the reader loop. Shapes were captured from live output and cross-checked against tianzuo/pi-antigravity `lib/events.ts` (MIT).
51
+
52
+ Usage maps onto pi's `Usage` (input/output/thinking/cache-read tokens); cost stays zero because agy runs on subscription quota.
53
+
54
+ ### No-patch tool round-trip (G9)
55
+
56
+ The MCP bridge server executes no tools itself. A `tools/call` parks in the provider's round-trip store; the provider ends the current pi assistant message with a `toolUse` stop reason for the real pi tool; pi executes it in its own loop (native cards, permissions, hooks); the `toolResult` completes the parked MCP response on the next stream call. No pi patch, no privileged API.
57
+
58
+ Display follows the same split: agy read-only steps (`view_file`, `list_dir`, `grep_search`, `find_by_name`) re-run as real pi builtins via `native-tools.ts`, so their cards render with pi's own renderers. Mutating and agy-specialty steps replay through a display-only `antigravity` wrapper tool - recorded output only, nothing re-executes. The skills bridge exposes one `activate_skill` tool whose enum is the pi Agent Skills catalog; the bridge answers it directly, no round-trip.
59
+
60
+ ### Context digest (G1)
61
+
62
+ By default the prompt agy receives is only the latest user message: agy keeps its own history. When `config.digest` is on (`AGY_DIGEST`, `/agy digest on`), the provider prepends a DELTA digest of pi-side context agy was not spawned for - compaction summaries, other-provider turns, pi-tool results. Off by default because the digest changes every turn and defeats agy's server-side prompt cache (~25-30k tokens re-billed per turn). Enable it for mixed-provider sessions where agy must see pi-side context; pure antigravity sessions gain nothing, and bridge round-trips deliver tool results through the bridge, not the digest.
63
+
64
+ ## Legacy-sqlite engine internals
65
+
66
+ The sections below describe the `legacy-sqlite` fallback only (`AGY_ENGINE=legacy-sqlite`). The stream-json engine shares none of this code path.
24
67
 
25
- ## The decode pipeline
68
+ ### The decode pipeline
26
69
 
27
70
  agy writes each step to a SQLite row with a protobuf blob in `step_payload`. The text we want lives at field 20, submessage field 1. Tool calls live at field 5, submessage field 4, with the name at field 2 or 9 and the raw input JSON at field 3. These field numbers are reverse-engineered facts (cross-checked against the shindgew/agy-acp and shubzkothekar/antigravity-acp decoders, then verified against real databases on agy 1.1.7). They are load-bearing. Unknown fields are skipped per protobuf wire rules, so a future agy that adds fields will not break decoding.
28
71
 
@@ -37,11 +80,11 @@ agy writes each step to a SQLite row with a protobuf blob in `step_payload`. The
37
80
 
38
81
  Status 3 = complete; anything else = in-flight.
39
82
 
40
- ## Polling
83
+ ### Polling
41
84
 
42
85
  The runner spawns `agy -p`, then polls its conversation DB on a 250ms interval concurrent with the running process (this is what makes the provider actually stream, not replay at exit). Each tick issues a single `PRAGMA data_version` check; while agy is thinking and has not committed, that check is false and no row SELECT runs at all (neither the new-row read nor the in-place re-read of the step agy is currently extending). Only when a commit lands do both reads fire in one pass. Three trailing polls at 100ms after agy exits catch the last flush; on abort these are skipped so cancellation is prompt.
43
86
 
44
- ## Conversation id discovery
87
+ ### Conversation id discovery
45
88
 
46
89
  agy `-p` does not print the conversation id. On a fresh run the runner snapshots the `*.db` stems in the conversations dir before spawn, then diffs after. Exactly one new file = ours; zero new = refuse to bind (surfaced as an error rather than a guess).
47
90
 
@@ -6,7 +6,7 @@ How to build, test, and debug this extension outside pi.
6
6
 
7
7
  ```bash
8
8
  npm install
9
- npm test # unit tests (protobuf decoder, runner streaming/abort, mcp bridge)
9
+ npm test # unit tests via vitest (no agy spawn, no network)
10
10
  npm run build # tsc --noEmit type check
11
11
  ```
12
12
 
@@ -42,23 +42,32 @@ npx tsx scripts/test-extension.ts
42
42
  # Load the extension through pi's REAL loader and confirm the antigravity/*
43
43
  # models register. This is the in-pi smoke test.
44
44
  npm run smoke:pi
45
+
46
+ # Live smoke for the stream-json engine. OPT-IN: spends a little Antigravity
47
+ # quota. Proves the persistent process: init binds a conversation, text deltas
48
+ # arrive, the result settles, and a second turn reuses the process.
49
+ AGY_LIVE=1 node --experimental-strip-types scripts/smoke-stream-json.mjs
45
50
  ```
46
51
 
47
52
  ## Debugging a hang or "stuck" turn
48
53
 
49
54
  Most "stuck" reports trace to one of:
50
55
 
51
- 1. **agy blocked on a permission prompt.** `accept-edits` auto-approves file edits but NOT shell commands. Any `run_command` prompts `y/n`, which hangs forever in non-interactive `-p` mode. The provider passes `--dangerously-skip-permissions` by default to avoid this. If you turned it off (`/agy permissions off`), that is why. See the README Permissions section.
52
- 2. **agy never started.** Check `AGY_BIN` is on PATH (or set explicitly). The runner swallows spawn ENOENT into the result's stderr, surfaced by the provider as an error event.
53
- 3. **Conversation id never bound.** The snapshot/diff discovery refuses to bind if more than one new `.db` appears (ambiguous). `scripts/decode-db.ts` against the suspected DB confirms agy wrote steps.
56
+ 1. **agy blocked on a permission prompt.** `accept-edits` auto-approves file edits but NOT shell commands. Any `run_command` prompts `y/n`, which hangs forever in non-interactive mode (both engines). The provider passes `--dangerously-skip-permissions` by default to avoid this. If you turned it off (`/agy permissions off`), that is why. See the README Permissions section.
57
+ 2. **agy never started.** Check `AGY_BIN` is on PATH (or set explicitly). The spawner swallows spawn ENOENT into the result's stderr, surfaced by the provider as an error event.
58
+ 3. **Conversation id never bound.** On `stream-json` the `init` event carries the id. On `legacy-sqlite` the snapshot/diff discovery refuses to bind if more than one new `.db` appears (ambiguous). `npm run decode-db` against the suspected DB confirms agy wrote steps.
54
59
  4. **Print-mode environmental hang.** `pi -p` can hang with zero output in some containers (upstream [google-antigravity/antigravity-cli#318](https://github.com/google-antigravity/antigravity-cli/issues/318)). It affects built-in providers too, not this extension. Validate the turn with `scripts/test-provider.ts` instead.
55
60
 
56
61
  ## Regression tests worth knowing
57
62
 
58
- - `tests/protobuf.test.ts` - pure decoder math (varint, field walking, nested submessages).
59
- - `tests/runner-streaming.test.ts` - a fake agy writes rows on a delay; asserts events arrive DURING the run (not all at exit) and that abort returns promptly. This is the test that guards the "provider did not actually stream" class of bug.
60
- - `tests/mcp-server.test.ts` - the MCP tool bridge end-to-end against a real (port 0) server: capability gate, per-pid config lifecycle, shared-secret token gate, 1 MB body cap, protocol-version clamp, and tool-call dispatch through `pi.invokeTool`. Guards the security and transport-correctness of the bridge.
63
+ - `tests/stream-roundtrip.test.ts` - the stream-json engine pieces: NDJSON parser, native re-exec mapping, and the no-patch toolUse round-trip store.
64
+ - `tests/provider-streaming.test.ts` - drives streamSimple with an injected fake runner (no agy) and asserts the exact event sequence: text/thinking close-on-switch, tool labels through the thinking block, empty-turn fallback.
65
+ - `tests/provider-digest.test.ts` - the G1 context digest builder: injects pi-side context without replaying agy's own history.
66
+ - `tests/patch-cleanup.test.ts` - legacy-patch detection and restore, real fs via tmpdirs, no mocks.
67
+ - `tests/runner-streaming.test.ts` - (legacy engine) a fake agy writes rows on a delay; asserts events arrive DURING the run (not all at exit) and that abort returns promptly. Guards the "provider did not actually stream" class of bug.
68
+ - `tests/protobuf.test.ts` - (legacy engine) pure decoder math (varint, field walking, nested submessages).
69
+ - `tests/mcp-server.test.ts` - the MCP tool bridge end-to-end against a real (port 0) server: capability gate, per-pid config lifecycle, shared-secret token gate, 1 MB body cap, protocol-version clamp. The provider owns the tool catalog and the round-trip; the server only ferries list/call.
61
70
 
62
71
  ## Module map
63
72
 
64
- See [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and the decode-pipeline / polling internals.
73
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and the engine internals (stream-json events, no-patch round-trip, legacy decode/polling).
@@ -2,71 +2,48 @@
2
2
 
3
3
  Status of the MCP tool bridge between agy (Antigravity CLI, used as pi's Gemini
4
4
  provider) and pi's extension/builtin tools. This doc tracks **open gaps only**.
5
- Shipped work lives in `CHANGELOG.md` (most recently: a conversation-history
6
- delta digest, and git-sourced edit diffs). Ideas that were weighed and rejected
7
- are listed at the end under "Discarded ideas".
5
+ Shipped work lives in `CHANGELOG.md` (most recently, 1.3.0: the stream-json
6
+ engine and the no-patch toolUse round-trip that replaced `pi.invokeTool`).
7
+ Ideas that were weighed and rejected are listed at the end under "Discarded
8
+ ideas".
8
9
 
9
10
  ## What the bridge already does
10
11
 
11
- agy -> bridge MCP server -> `pi.invokeTool(name, args)` -> pi's tool registry
12
- -> result returned through MCP -> agy model context. The chain was verified
13
- end-to-end with `memory_search` and `ask_user_question`. The bridge exposes
14
- pi's extension tools (builtins are filtered out since agy has native
15
- equivalents; `AskAntigravity` is filtered to avoid recursion).
12
+ agy -> bridge MCP server `tools/call` -> the call parks in the provider's
13
+ round-trip store -> the provider ends the pi assistant message with a
14
+ `toolUse` stop reason for the real pi tool -> pi executes it in its own loop
15
+ (native cards, permissions, hooks) -> the `toolResult` completes the parked
16
+ MCP response on the next stream call. No pi patch. Verified end-to-end with
17
+ `memory_search` and `ask_user_question`. The bridge exposes pi's extension
18
+ tools (builtins are filtered out since agy has native equivalents;
19
+ `AskAntigravity` is filtered to avoid recursion).
16
20
 
17
21
  What this means in practice: agy can read/write files, use memory, navigate
18
22
  code with codegraph, search the web, post to Slack, create Asana tasks, spawn
19
23
  subagents, prompt the user with `ask_user_question`, and delegate to peer
20
24
  reviewers (Claude, Codex, Antigravity), all by going through pi's installed
21
- tooling instead of its own. Tools run in pi's process with pi's own credentials,
22
- so a secret never crosses the bridge. agy's file edits surface as git-sourced
23
- diffs in pi's thinking stream, and each turn agy receives a delta digest of
24
- pi-side context (compaction summaries, other-provider turns) it was not spawned
25
- for.
25
+ tooling instead of its own. Tools run in pi's process with pi's own
26
+ credentials, so a secret never crosses the bridge, and a long call renders in
27
+ pi's native UI while it runs. agy's file edits surface as git-sourced diffs in
28
+ pi's thinking stream. A delta digest of pi-side context (compaction summaries,
29
+ other-provider turns) is available but OFF by default (`/agy digest on`): the
30
+ digest changes every turn and defeats agy's server-side prompt cache.
26
31
 
27
32
  ## Open gaps
28
33
 
29
- Three gaps remain. Every one either needs a pi dist patch (the maintenance cost
30
- the shipped gaps deliberately avoided) or a verify-before-build step. Ordered by
31
- former G-number.
34
+ Two gaps remain. Both now sit on the no-patch round-trip path, so closing them
35
+ means provider- or bridge-side work only; there is no pi dist patch to extend
36
+ anymore. Ordered by impact.
32
37
 
33
- **Numbering note:** the G1/G2/G3 labels below are this living doc's renumbered
34
- open set, NOT the historical G1-G10 in `CHANGELOG.md`. In that historical list
35
- G1 = conversation-history digest and G8 = edit diffs; both are now closed
36
- (shipped in 1.0.0) and so dropped from here, leaving the three renumbered below.
38
+ **Numbering note:** the G1/G2 labels below are this living doc's renumbered
39
+ open set, NOT the historical G-numbers. In the historical list (`CHANGELOG.md`
40
+ and the comments in `src/provider.ts`) G1 = conversation-history digest
41
+ (shipped 1.0.0, now opt-in via `config.digest`) and G9 = the no-patch toolUse
42
+ round-trip (shipped 1.3.0).
37
43
 
38
44
  ---
39
45
 
40
- ### G1. Stream progress for long tool calls [HIGH IMPACT]
41
-
42
- **Status:** Open
43
- **Objective:** Stop blocking on long tools (`web_research`, `librarian`,
44
- `codegraph_explore` on big repos). Push partial output back to agy so pi's UI
45
- shows live progress instead of a frozen spinner.
46
-
47
- **Why:** Today the bridge blocks until the tool returns. The user has no signal
48
- that work is happening. This is partly a pi-side gap (most long tools do not
49
- emit progress) and partly a bridge-transport gap.
50
-
51
- **Scope:**
52
- - `docs/PI-INVOKETOOL-PATCH.md`: expose pi's progress bus.
53
- - `src/mcp-server.ts`: switch from blocking `invokeTool` to a streaming RPC
54
- using MCP `notifications/progress` (or a `pi_tool_progress` SSE channel).
55
- - pi-side: audit long tools and add progress emission where missing.
56
-
57
- **Acceptance criteria:**
58
- - [ ] A tool that runs >1s emits at least one progress notification.
59
- - [ ] pi's TUI spinner updates during the call, not only on completion.
60
- - [ ] Result content is identical to the blocking path (no data loss).
61
- - [ ] Fallback: if a tool does not emit progress, behavior matches today.
62
-
63
- **Effort:** Large. Touches pi's progress bus and the MCP transport.
64
-
65
- **Blocks:** None. **Blocked by:** None.
66
-
67
- ---
68
-
69
- ### G2. Expose pi's UI primitives [MEDIUM-HIGH IMPACT]
46
+ ### G1. Expose pi's UI primitives [MEDIUM-HIGH IMPACT]
70
47
 
71
48
  **Status:** Open
72
49
  **Objective:** Let agy drive pi's native UI: confirm dialogs, toasts,
@@ -80,7 +57,9 @@ notification toast (for "task started" / "save ok"), native file picker
80
57
  for agy edits, that path is structurally closed (see G8 in `CHANGELOG.md`).
81
58
 
82
59
  **Scope:**
83
- - `docs/PI-INVOKETOOL-PATCH.md`: expose `AgentSession.ui` helpers.
60
+ - pi-side: confirm a public API surface for these primitives. The old plan of
61
+ patching `AgentSession.ui` into pi's dist is dead; the bridge no longer
62
+ patches pi.
84
63
  - `src/mcp-server.ts`: wrappers for `pi_confirm`, `pi_notify`,
85
64
  `pi_select_file`, `pi_select_directory`, `pi_set_status`.
86
65
 
@@ -91,29 +70,30 @@ for agy edits, that path is structurally closed (see G8 in `CHANGELOG.md`).
91
70
  - [ ] `pi_set_status(text)` updates the footer; clears on empty string.
92
71
  - [ ] Tests cover each primitive with a mocked `ui` seam.
93
72
 
94
- **Effort:** Medium-large. Each primitive is a small pi patch plus a wrapper.
73
+ **Effort:** Medium-large, gated on pi exposing the primitives without a patch.
95
74
 
96
- **Blocks:** None. **Blocked by:** None.
75
+ **Blocks:** None. **Blocked by:** pi-side API availability.
97
76
 
98
77
  ---
99
78
 
100
- ### G3. Lifecycle event subscription [MEDIUM IMPACT]
79
+ ### G2. Lifecycle event subscription [MEDIUM IMPACT]
101
80
 
102
81
  **Status:** Open
103
82
  **Objective:** Let a long-lived agy session observe pi events: `turn_start`,
104
83
  `turn_end`, `tool_call`, `tool_result`, `compaction`.
105
84
 
106
85
  **Why:** Today the bridge handles only `session_start` and `session_shutdown`.
107
- agy is fire-and-forget per turn. Event subscription would enable a class of
108
- "observer" tooling.
86
+ Event subscription would enable a class of "observer" tooling.
109
87
 
110
- **Caveat:** agy is request-response per turn (`-p`), not event-reactive. Before
111
- building, confirm there is a real consumer that can act on an async event
112
- stream; otherwise this risks the same "no consumer" failure that sank
113
- file-watching (see Discarded ideas).
88
+ **Caveat:** agy is still request-response per turn, even though the
89
+ stream-json engine keeps one process alive across turns. Before building,
90
+ confirm there is a real consumer that can act on an async event stream;
91
+ otherwise this risks the same "no consumer" failure that sank file-watching
92
+ (see Discarded ideas).
114
93
 
115
94
  **Scope:**
116
- - `docs/PI-INVOKETOOL-PATCH.md`: add an event-emitter seam on `AgentSession`.
95
+ - Provider-side event tap (no pi patch): relay pi event callbacks into the
96
+ bridge.
117
97
  - `src/mcp-server.ts`: `pi_subscribe(event)` returns a stream id; an SSE
118
98
  channel pushes events.
119
99
 
@@ -124,12 +104,20 @@ file-watching (see Discarded ideas).
124
104
  - [ ] At least three event types supported at close.
125
105
  - [ ] No perf regression on the event hot path.
126
106
 
127
- **Effort:** Large. Non-trivial pi-side patching.
107
+ **Effort:** Large.
128
108
 
129
109
  **Blocks:** None. **Blocked by:** Confirm a real event-driven consumer exists.
130
110
 
131
111
  ---
132
112
 
113
+ ## Closed by 1.3.0 (moved out of the open set)
114
+
115
+ - **Stream progress for long tool calls** (the former top open gap): closed by
116
+ the no-patch round-trip. Bridged tools no longer block inside the MCP
117
+ server; they execute as real pi tools in pi's own loop, so pi's native
118
+ card/spinner UX shows progress while the call runs, and the result content
119
+ is identical to the old blocking path.
120
+
133
121
  ## Discarded ideas (not worth it)
134
122
 
135
123
  Weighed and rejected; kept here as a graveyard so they are not re-proposed. Full
@@ -145,10 +133,10 @@ reasoning is in project memory.
145
133
  mid-session appears next turn. The heartbeat would only help a long-lived
146
134
  client that caches the list, and there is none.
147
135
  - **Settings, env, and secrets access** — DECLINED. Tools exposed via
148
- the bridge run in pi's process (`pi.invokeTool` -> `tool.execute`) and
149
- self-authenticate with pi's own credentials, so agy already uses pi's creds
150
- for every tool; a credential never crosses the bridge. A `pi_get_setting`
151
- accessor was predicated on credential reuse that does not apply.
136
+ the bridge run in pi's process and self-authenticate with pi's own
137
+ credentials, so agy already uses pi's creds for every tool; a credential
138
+ never crosses the bridge. A `pi_get_setting` accessor was predicated on
139
+ credential reuse that does not apply.
152
140
  - **Image / binary content blocks** — NOT NEEDED. pi shares the
153
141
  path to any image it produces (e.g. `/tmp/pi-clipboard-<uuid>.png`), and agy
154
142
  reaches and reads those files directly via the bridge's `read` tool, so
@@ -163,24 +151,19 @@ reasoning is in project memory.
163
151
 
164
152
  For each open gap, the default shape:
165
153
 
166
- 1. Identify the pi-side API to expose (or add).
167
- 2. Extend the patch in `docs/PI-INVOKETOOL-PATCH.md` with a read (or write)
168
- accessor.
169
- 3. Add a bridge tool wrapper in `src/mcp-server.ts` that calls the patched API.
170
- 4. Register the tool name with the bridge (it appears in agy's tool catalog on
171
- the next session).
172
- 5. Add a test under `tests/mcp-server.test.ts` that round-trips a real call.
173
- 6. Tick the gap's acceptance checkboxes.
154
+ 1. Identify the pi-side API (must be public; the bridge does not patch pi).
155
+ 2. Add a bridge tool wrapper in `src/mcp-server.ts` that parks a call through
156
+ the provider's round-trip store, or answers bridge-side when no pi tool is
157
+ needed (see `src/skills.ts` for that pattern).
158
+ 3. Register the tool name with the bridge (it appears in agy's tool catalog on
159
+ the next turn).
160
+ 4. Add a test under `tests/mcp-server.test.ts` that round-trips a real call.
161
+ 5. Tick the gap's acceptance checkboxes.
174
162
 
175
- Most need no change to agy, only the bridge (and, for some, pi's dist). Note:
176
- the two shipped gaps were closed without any pi dist patch or new MCP tool, by
177
- working provider/decode-side (`src/provider.ts`, `src/diff-render.ts`); the
178
- shape above is a default, not a requirement.
163
+ Most need no change to agy, only the bridge or the provider.
179
164
 
180
165
  ## Cross-references
181
166
 
182
- - `docs/ARCHITECTURE.md` — bridge design and per-pid config layout.
183
- - `docs/PI-INVOKETOOL-PATCH.md` — the local patch to pi that this whole
184
- feature depends on.
167
+ - `docs/ARCHITECTURE.md` — engine internals, round-trip design, per-pid config layout.
185
168
  - `docs/DEVELOPMENT.md` — how to run tests, rebuild, and iterate.
186
- - `CHANGELOG.md` — shipped work (conversation-history digest, edit diffs).
169
+ - `CHANGELOG.md` — shipped work.