@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 +35 -0
- package/README.md +40 -32
- package/docs/ANTIGRAVITY-INTEGRATIONS.md +2 -0
- package/docs/ARCHITECTURE.md +58 -15
- package/docs/DEVELOPMENT.md +17 -8
- package/docs/PI-BRIDGE-GAPS.md +65 -82
- package/extensions/index.ts +199 -139
- package/package.json +2 -2
- package/src/config.ts +55 -5
- package/src/driver.ts +644 -0
- package/src/mcp-server.ts +28 -48
- package/src/native-tools.ts +104 -0
- package/src/{patcher.ts → patch-cleanup.ts} +25 -197
- package/src/provider.ts +447 -24
- package/src/skills.ts +116 -0
- package/src/stream-events.ts +123 -0
- package/docs/PI-INVOKETOOL-PATCH.md +0 -254
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
|
|
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`.
|
|
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
|
|
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
|
-
|
|
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
|
|
|
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
|
-
>
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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:
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -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
|
|
9
|
-
src/provider.ts streamSimple: pi Context -> agy turn -> pi event stream
|
|
10
|
-
src/
|
|
11
|
-
src/
|
|
12
|
-
src/
|
|
13
|
-
src/
|
|
14
|
-
src/
|
|
15
|
-
src/
|
|
16
|
-
src/
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -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 (
|
|
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
|
|
52
|
-
2. **agy never started.** Check `AGY_BIN` is on PATH (or set explicitly). The
|
|
53
|
-
3. **Conversation id never bound.**
|
|
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/
|
|
59
|
-
- `tests/
|
|
60
|
-
- `tests/
|
|
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
|
|
73
|
+
See [ARCHITECTURE.md](./ARCHITECTURE.md) for the module map and the engine internals (stream-json events, no-patch round-trip, legacy decode/polling).
|
package/docs/PI-BRIDGE-GAPS.md
CHANGED
|
@@ -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:
|
|
6
|
-
|
|
7
|
-
are listed at the end under "Discarded
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
22
|
-
so a secret never crosses the bridge
|
|
23
|
-
|
|
24
|
-
pi-side context (compaction summaries,
|
|
25
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
|
34
|
-
open set, NOT the historical
|
|
35
|
-
G1 = conversation-history digest
|
|
36
|
-
(shipped
|
|
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.
|
|
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
|
-
-
|
|
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
|
|
73
|
+
**Effort:** Medium-large, gated on pi exposing the primitives without a patch.
|
|
95
74
|
|
|
96
|
-
**Blocks:** None. **Blocked by:**
|
|
75
|
+
**Blocks:** None. **Blocked by:** pi-side API availability.
|
|
97
76
|
|
|
98
77
|
---
|
|
99
78
|
|
|
100
|
-
###
|
|
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
|
-
|
|
108
|
-
"observer" tooling.
|
|
86
|
+
Event subscription would enable a class of "observer" tooling.
|
|
109
87
|
|
|
110
|
-
**Caveat:** agy is request-response per turn
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
file-watching
|
|
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
|
-
-
|
|
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.
|
|
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
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
|
167
|
-
2.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
the next
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
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` —
|
|
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
|
|
169
|
+
- `CHANGELOG.md` — shipped work.
|