@estebanforge/pi-antigravity-bridge 1.3.0 → 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,13 @@
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
+
5
12
  ## [1.3.0] - 2026-08-31
6
13
 
7
14
  ### Added
package/README.md CHANGED
@@ -8,25 +8,25 @@ 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
- 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
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
 
@@ -54,29 +54,7 @@ tokens.
54
54
 
55
55
  ## Install
56
56
 
57
- > ⚠️ **Heads-up: this extension patches your `pi` install.** When it loads and
58
- > the running pi lacks `pi.invokeTool()`, it **asks you once** whether to edit
59
- > files inside your globally-installed `@earendil-works/pi-coding-agent/dist/`
60
- > (adding one method) to enable the MCP tool bridge. This is, for now, the only
61
- > way agy can use Pi internal tooling.
62
- >
63
- > - **Yes** → applies the patch (reversible via `/agy patch restore`) and tells
64
- > you to restart pi. The bridge starts on the next launch.
65
- >
66
- > - **No** → it remembers your choice and stays silent; it won't ask again until
67
- > you run `/agy patch apply`. The provider and AskAntigravity tool keep working;
68
- > only the MCP tool bridge stays off.
69
- >
70
- > - The apply is **idempotent & safe** (only what's missing; aborts cleanly if a
71
- > pi update moved the code), **backed up** (under
72
- > `~/.pi/agent/antigravity-bridge/pi-patch-backup/`), and **self-healing** (a
73
- > `pi` reinstall/update wipes `dist/`; re-applied on the next start).
74
- >
75
- > The patch only takes effect after a **full `pi` restart** (quit + relaunch) —
76
- > `/reload` is **not** enough, because pi caches its compiled core for the
77
- > process.
78
- >
79
- > 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.
80
58
 
81
59
  Install with pi's package manager:
82
60
 
@@ -127,12 +105,14 @@ Env overrides: `AGY_ENGINE`, `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`. The `legacy-sqlit
127
105
  ```
128
106
  /agy status, or open the mode/permissions/model/thinking picker (TUI)
129
107
  /agy status print current mode, permissions, model + session counts
108
+ /agy doctor engine state, driver counters, bridge port, last lifecycle events
130
109
  /agy mode plan review-only: agy plans but writes nothing
131
110
  /agy mode accept-edits agy applies edits directly (default)
132
111
  /agy permissions on|off auto-approve / prompt for tool calls (see warning)
133
112
  /agy model flash|pro|gemini default model alias for the AskAntigravity tool
134
113
  /agy thinking low|medium|high default thinking tier for the AskAntigravity tool
135
- /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
136
116
  /agy clear drop all session bindings (force fresh conversations)
137
117
  ```
138
118
 
@@ -140,7 +120,7 @@ Env overrides: `AGY_ENGINE`, `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`. The `legacy-sqlit
140
120
 
141
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.
142
122
 
143
- 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.
144
124
 
145
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)).
146
126
 
@@ -162,13 +142,13 @@ For isolation when running any agent that executes commands without a confirmati
162
142
 
163
143
  ## Development
164
144
 
165
- 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).
166
146
 
167
147
  ## Terms of Service notice
168
148
 
169
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.
170
150
 
171
- 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.
172
152
 
173
153
  Google's reported enforcement to date (the February 2026 suspensions) targeted token-reuse tools, not spawning the official CLI.
174
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,26 +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
32
+ src/mcp-server.ts MCP tool bridge server: ferries tools/list + tools/call; calls park in the provider round-trip
19
33
  src/diff-render.ts render agy's file edits as git diffs in pi's thinking stream
20
34
  ```
21
35
 
22
- 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.
23
67
 
24
- ## The decode pipeline
68
+ ### The decode pipeline
25
69
 
26
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.
27
71
 
@@ -36,11 +80,11 @@ agy writes each step to a SQLite row with a protobuf blob in `step_payload`. The
36
80
 
37
81
  Status 3 = complete; anything else = in-flight.
38
82
 
39
- ## Polling
83
+ ### Polling
40
84
 
41
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.
42
86
 
43
- ## Conversation id discovery
87
+ ### Conversation id discovery
44
88
 
45
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).
46
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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@estebanforge/pi-antigravity-bridge",
3
- "version": "1.3.0",
4
- "description": "Streaming Gemini provider for pi, built on the agy CLI. Registers antigravity/* models in pi's /model picker via SQLite polling + protobuf decode of agy's conversation DBs.",
3
+ "version": "1.3.1",
4
+ "description": "Streaming Gemini provider for pi, built on the agy CLI. Registers antigravity/* models in pi's /model picker; drives agy through its stream-json protocol (persistent process, tool round-trips, live usage).",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi-extension",