@estebanforge/pi-antigravity-bridge 1.3.0 → 1.3.2

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,25 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.3.2] - 2026-09-01
6
+
7
+ ### Removed
8
+
9
+ - 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.
10
+ Reported by @imatimba in #1. Thanks for the exact repro and the root-cause analysis; the report drove this removal.
11
+
12
+ ### Changed
13
+
14
+ - `scripts/test-provider.ts` wires the stream-json driver explicitly (it exercised the legacy path implicitly before).
15
+ - `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`.
16
+
17
+ ## [1.3.1] - 2026-08-31
18
+
19
+ ### Changed
20
+
21
+ - 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.
22
+
23
+
5
24
  ## [1.3.0] - 2026-08-31
6
25
 
7
26
  ### 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. A single persistent `agy` process runs in your workspace; pi feeds it each turn, parses its stream-json events, and streams the agent text back into pi token by token. Token usage is live.
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.
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
 
@@ -36,7 +36,7 @@ The bridge starts a localhost MCP server inside pi's process. `tools/list` retur
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)
39
+ ### Native cards, wrapper replay, and skills
40
40
 
41
41
  Read-only agy steps (view_file, list_dir, grep_search, find_by_name) re-run as
42
42
  real pi builtins (`read`, `ls`, `grep`, `find`) when those builtins are active,
@@ -48,35 +48,12 @@ anything with side effects.
48
48
 
49
49
  When the bridge is on, agy also gets one `activate_skill` tool whose enum is
50
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.
51
+ answers it directly, no pi round-trip. `/agy doctor` prints driver counters,
52
+ bridge port, and the last lifecycle events without spending tokens.
54
53
 
55
54
  ## Install
56
55
 
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).
56
+ > **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
57
 
81
58
  Install with pi's package manager:
82
59
 
@@ -86,8 +63,6 @@ pi install npm:@estebanforge/pi-antigravity-bridge
86
63
 
87
64
  Requires the **`agy` CLI** installed and authenticated. If you don't have it, follow Google's [official install guide](https://antigravity.google/docs/cli/install) for your platform, then run `agy` once to complete Google OAuth. The extension resolves `agy` on `$PATH`, or via the `AGY_BIN` environment variable.
88
65
 
89
- Also requires Node 22.5 or newer (uses the built-in `node:sqlite`).
90
-
91
66
  ## Usage
92
67
 
93
68
  Pick a model and talk to pi as usual:
@@ -108,17 +83,16 @@ Model ids are slugified from the `agy models` output (`Gemini 3.6 Flash (Medium)
108
83
 
109
84
  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.
110
85
 
111
- ### Engine and bridge surface
86
+ ### Bridge surface
112
87
 
113
- `config.json` selects the turn engine and the bridge surface:
88
+ `config.json` selects the bridge surface:
114
89
 
115
90
  | Key | Values | Default |
116
91
  | --- | --- | --- |
117
- | `engine` | `stream-json` (persistent agy process, toolUse round-trips, live usage) or `legacy-sqlite` (spawn `agy -p`, poll its SQLite) | `stream-json` |
118
92
  | `bridgeTools` | `none` (bridge off), `mcp` (pi-mcp-adapter tools), `all` (every non-builtin tool, incl. other `Ask*` delegations) | `mcp` |
119
93
  | `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` |
120
94
 
121
- 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.
95
+ Env overrides: `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`.
122
96
 
123
97
  ### The /agy command
124
98
 
@@ -127,12 +101,14 @@ Env overrides: `AGY_ENGINE`, `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`. The `legacy-sqlit
127
101
  ```
128
102
  /agy status, or open the mode/permissions/model/thinking picker (TUI)
129
103
  /agy status print current mode, permissions, model + session counts
104
+ /agy doctor bridge state, driver counters, bridge port, last lifecycle events
130
105
  /agy mode plan review-only: agy plans but writes nothing
131
106
  /agy mode accept-edits agy applies edits directly (default)
132
107
  /agy permissions on|off auto-approve / prompt for tool calls (see warning)
133
108
  /agy model flash|pro|gemini default model alias for the AskAntigravity tool
134
109
  /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
110
+ /agy digest on|off inject pi-side context into agy prompts (default off; see table above)
111
+ /agy patch-cleanup restore the original pi files if an older version patched them
136
112
  /agy clear drop all session bindings (force fresh conversations)
137
113
  ```
138
114
 
@@ -140,7 +116,7 @@ Env overrides: `AGY_ENGINE`, `AGY_BRIDGE_TOOLS`, `AGY_DIGEST`. The `legacy-sqlit
140
116
 
141
117
  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
118
 
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.
119
+ 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
120
 
145
121
  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
122
 
@@ -162,13 +138,13 @@ For isolation when running any agent that executes commands without a confirmati
162
138
 
163
139
  ## Development
164
140
 
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).
141
+ 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
142
 
167
143
  ## Terms of Service notice
168
144
 
169
145
  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
146
 
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.
147
+ 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. 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
148
 
173
149
  Google's reported enforcement to date (the February 2026 suspensions) targeted token-reuse tools, not spawning the official CLI.
174
150
 
@@ -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,46 +2,59 @@
2
2
 
3
3
  How the provider works internally. For build/test/debug workflow see [DEVELOPMENT.md](./DEVELOPMENT.md).
4
4
 
5
+ ## Turn engine
6
+
7
+ The provider runs one turn engine: a 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.
8
+
9
+ Shared infrastructure: session binding (`sessions.json`), runtime config, the `AskAntigravity` tool, the MCP tool bridge surface, and the G1 context digest (off by default - see below).
10
+
5
11
  ## Module map
6
12
 
7
13
  ```
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)
14
+ extensions/index.ts pi extension entry: provider registration, model discovery, /agy command, lifecycle notices
15
+ src/provider.ts streamSimple: pi Context -> agy turn -> pi event stream; owns the G9 round-trip store and the G1 digest
16
+ src/driver.ts stream-json driver: persistent agy process, turn serialization, conversation binding, idle/abort timers
17
+ src/stream-events.ts agy NDJSON event parser (init / step_update / result) + usage mapping onto pi's Usage
18
+ src/native-tools.ts maps agy read-only tool steps to real pi builtins (read/ls/grep/find) for native re-execution
19
+ src/skills.ts activate_skill bridge: exposes the pi Agent Skills catalog to agy, answered by the bridge directly
20
+ src/patch-cleanup.ts detects a leftover invokeTool patch from pre-1.3.0 installs; /agy patch-cleanup restores the backup
21
+ src/discovery.ts conversation-id binding for the AskAntigravity one-shot tool (agy -p never prints its conversation id)
22
+ src/models.ts agy models -> pi Model projection (full catalog, per-model effort)
23
+ src/sessions.ts atomic JSON store: pi session -> agy conversation + watermark
24
+ src/config.ts persisted runtime config (bridgeTools, digest, mode, permissions, model/thinking defaults)
17
25
  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
26
+ src/mcp-server.ts MCP tool bridge server: ferries tools/list + tools/call; calls park in the provider round-trip
19
27
  src/diff-render.ts render agy's file edits as git diffs in pi's thinking stream
20
28
  ```
21
29
 
22
- No generated protobuf code, no native SQLite dependency.
30
+ No generated protobuf code, no SQLite dependency.
23
31
 
24
- ## The decode pipeline
32
+ ## Stream-json engine
25
33
 
26
- 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.
34
+ ### Process and events
27
35
 
28
- ### Step types
36
+ 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`:
29
37
 
30
- | step_type | meaning |
38
+ | event | meaning |
31
39
  | --- | --- |
32
- | 15 | agent text (payload field 20 -> field 1) |
33
- | 14 | thinking |
34
- | 23 | title update (payload field 30 -> field 4) |
35
- | 5, 7, 8, 9, 17, 21, 33, 101, 132, 138 | tool run (payload field 5 -> field 4 -> name@2/9, input@3) |
40
+ | `init` | conversation binding (`conversation_id`); the driver remembers it and resumes later turns via `--conversation <id>` |
41
+ | `step_update` | `user_input` / `checkpoint` / `agent_response` / `tool` steps; agent text arrives as `text_delta` on live agy (1.1.13+), with `usage` attached |
42
+ | `result` | terminal; live builds report status `SUCCESS` (older builds `OK` - both accepted) |
43
+
44
+ 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).
45
+
46
+ Usage maps onto pi's `Usage` (input/output/thinking/cache-read tokens); cost stays zero because agy runs on subscription quota.
47
+
48
+ ### No-patch tool round-trip (G9)
36
49
 
37
- Status 3 = complete; anything else = in-flight.
50
+ 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.
38
51
 
39
- ## Polling
52
+ 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.
40
53
 
41
- 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.
54
+ ### Context digest (G1)
42
55
 
43
- ## Conversation id discovery
56
+ 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.
44
57
 
45
- 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).
58
+ ### Removed: the legacy-sqlite engine (1.3.2)
46
59
 
47
- When **more than one** new file appears (a concurrent agy or subagent started in parallel), the runner passes its spawned pid to `newConversationId`, which scans the process tree's open file descriptors (`/proc/<pid>/fd` on Linux) to find the single candidate `.db` our own agy is writing to. This is the authoritative disambiguator: mtime cannot separate two *active* concurrent runs, and the user-message payload (`step_type 98`) is undocumented and deeply nested, so content-matching would risk a silent misbind. When the pid is unavailable, the platform is not Linux, the process has already exited (the AskAntigravity tool binds post-exit), or the scan itself is ambiguous, discovery fails safe to null rather than guessing.
60
+ The pre-1.3.0 engine (spawn `agy -p`, poll the SQLite conversation DB, decode protobuf step payloads) was removed in 1.3.2. agy 1.1.18 changed the step-row storage to a two-phase write (a metadata-only placeholder row that grows in place), which the polling decoder read once as an empty placeholder and never re-read: turns completed with the full reply in the database and zero text streamed to pi (issue #1, reported by @imatimba). The engine reverse-engineered an undocumented storage format, so every agy storage change risked repeating that silent failure. The stream-json engine shares none of that code path and is unaffected by storage-format changes. `AGY_ENGINE` and the `engine` config key are gone; a stale value in an existing `config.json` is ignored.
@@ -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
 
@@ -14,22 +14,9 @@ The integration scripts below spawn a real `agy` process and need a logged-in ac
14
14
 
15
15
  ## Standalone scripts
16
16
 
17
- These exercise the pipeline without pi. Useful for isolating where a bug lives (decoder? poller? provider? pi loader?).
17
+ These exercise the pipeline without pi. Useful for isolating where a bug lives (driver? provider? pi loader?).
18
18
 
19
19
  ```bash
20
- # Decode any conversation DB and print agent text + tool calls.
21
- # Accepts a UUID (resolved against ~/.gemini/antigravity-cli/conversations/)
22
- # or an absolute path. Fastest way to check the protobuf decoder against
23
- # real data. No agy spawn, no network.
24
- npm run decode-db -- <uuid-or-path>
25
-
26
- # Spawn agy and stream decoded events to stdout with timestamps. Proves the
27
- # concurrent poll loop actually streams (events arrive during the run, not
28
- # only at exit). Use this to reproduce a hang or a missing-event bug.
29
- npm run run-agy -- "Say hello"
30
- npm run run-agy -- --model "Gemini 3.6 Flash (Medium)" --mode plan "Review src/protobuf.ts"
31
- npm run run-agy -- --conversation <uuid> "follow up" # resume a turn
32
-
33
20
  # Drive the provider's streamSimple directly (no pi TUI) and assert the
34
21
  # full event lifecycle: start -> text_start -> text_delta -> text_end ->
35
22
  # done. The closest thing to a pi turn without pi.
@@ -42,23 +29,30 @@ npx tsx scripts/test-extension.ts
42
29
  # Load the extension through pi's REAL loader and confirm the antigravity/*
43
30
  # models register. This is the in-pi smoke test.
44
31
  npm run smoke:pi
32
+
33
+ # Live smoke for the stream-json engine. OPT-IN: spends a little Antigravity
34
+ # quota. Proves the persistent process: init binds a conversation, text deltas
35
+ # arrive, the result settles, and a second turn reuses the process.
36
+ AGY_LIVE=1 node --experimental-strip-types scripts/smoke-stream-json.mjs
45
37
  ```
46
38
 
47
39
  ## Debugging a hang or "stuck" turn
48
40
 
49
41
  Most "stuck" reports trace to one of:
50
42
 
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.
43
+ 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. 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.
44
+ 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.
45
+ 3. **Conversation id never bound.** On `stream-json` the `init` event carries the id, so a missing binding means the turn never produced a result event. `/agy doctor` prints the last lifecycle events.
54
46
  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
47
 
56
48
  ## Regression tests worth knowing
57
49
 
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.
50
+ - `tests/stream-roundtrip.test.ts` - the stream-json engine pieces: NDJSON parser, native re-exec mapping, and the no-patch toolUse round-trip store.
51
+ - `tests/provider-streaming.test.ts` - drives streamSimple with an injected fake driver (no agy) and asserts how pi's reasoning level maps onto the agy `--effort` tier (forward, clamp, omit).
52
+ - `tests/provider-digest.test.ts` - the G1 context digest builder: injects pi-side context without replaying agy's own history.
53
+ - `tests/patch-cleanup.test.ts` - legacy-patch detection and restore, real fs via tmpdirs, no mocks.
54
+ - `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
55
 
62
56
  ## Module map
63
57
 
64
- See [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and the decode-pipeline / polling internals.
58
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and the engine internals (stream-json events, no-patch round-trip).
@@ -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.