@estebanforge/pi-antigravity-bridge 1.6.2 → 1.6.4

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.
@@ -1,462 +0,0 @@
1
- # ACP Protocol Reference — `antigravity-acp` (verified live)
2
-
3
- Everything below was verified LIVE on 2026-09-03 against the official Google
4
- ACP server, build `agy_acp_server_20260818_01_RC01` (registry id
5
- `antigravity-acp`, v1.0.0), linux-x86_64, on a headless Fedora box. Raw
6
- traffic: `probe-logs/acp-traffic-run5.jsonl` (600 clean JSONL lines,
7
- gitignored). Gate verdicts: [ACP-ADOPTION-PLAN.md](./ACP-ADOPTION-PLAN.md)
8
- section 8.1. Raw SQLite conversation store: `probe-logs/acp-server-conversations/`.
9
-
10
- Transport: newline-delimited JSON-RPC 2.0 over the server's stdio. The server
11
- is fully silent until it receives a request. Cold start to `initialize`
12
- response measured ~5-6 s across three runs (1.5 GB binary). Startup flags:
13
- only `--debug` and `--notices` exist. No model/effort/conversation flags:
14
- everything moves through the protocol or `settings.json`.
15
-
16
- Data provenance: the captures are SERVER STDOUT only. Requests quoted here
17
- are author-attested — we composed and sent those exact bytes (source: our
18
- stdin writes, preserved in `probe-logs/MANIFEST.md` references and the pi
19
- session) and correlate them with the success/error responses on the capture.
20
- No request echo exists on the wire. Gate F's accepted `mcpServers` shape is
21
- verified this way: those exact bytes were sent and `session/new` returned a
22
- session.
23
-
24
- ## Method index (verified against this build)
25
-
26
- | Method | Status on RC01 |
27
- | --- | --- |
28
- | `initialize` | works |
29
- | `authenticate` | works (param is `methodId`) |
30
- | `session/new` | works |
31
- | `session/set_config_option` | works (param is `configId`) |
32
- | `session/prompt` | works |
33
- | `session/cancel` | **-32601 Method not found. NOT IMPLEMENTED on RC01** |
34
- | `session/load` | works (run 6: `{sessionId, cwd, mcpServers}`; full-text history replay) |
35
- | `session/close` | works (run 6; per-session, connection survives) |
36
- | `session/set_mode` | works (run 6; `modeId`; redundant with `set_config_option("mode")`) |
37
- | `session/request_permission` | observed live (run 6, mode `default` tool probe; agent-to-client REQUEST, once-only options) |
38
- | `session/list`, `session/resume`, `session/fork`, `session/delete` | advertised; not yet probed |
39
- | `fs/read_text_file`, `fs/write_text_file`, `terminal/*` | not observed (our client capabilities were `fs:false`, `terminal:false`) |
40
-
41
- ## initialize
42
-
43
- Request:
44
-
45
- ```json
46
- {"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{"fs":{"readTextFile":false,"writeTextFile":false},"terminal":false}}}
47
- ```
48
-
49
- Result (verbatim, capability names):
50
-
51
- ```json
52
- {"protocolVersion":1,
53
- "agentCapabilities":{
54
- "loadSession":true,
55
- "promptCapabilities":{"image":true,"audio":true,"embeddedContext":true},
56
- "mcpCapabilities":{"http":true,"sse":true},
57
- "sessionCapabilities":{"list":{},"resume":{}},
58
- "auth":{"logout":{}}},
59
- "authMethods":[
60
- {"description":"Log in with your Google account","id":"oauth-personal","name":"Log in with Google"},
61
- {"description":"Log in with your Gemini Enterprise account","id":"oauth-business","name":"Log in with Gemini Enterprise"},
62
- {"description":"Use an API key with Gemini Developer API","id":"gemini-api-key","name":"Gemini API key"},
63
- {"description":"Use Gemini Enterprise Agent Platform (formerly Vertex AI) with Application Default Credentials or an API key","id":"agent-platform","name":"Gemini Enterprise Agent Platform"}],
64
- "agentInfo":{"name":"antigravity-acp","title":"Google Antigravity","version":"agy_acp_server_20260818_01_RC01"}}
65
- ```
66
-
67
- Types: `promptCapabilities.image|audio|embeddedContext: bool`;
68
- `mcpCapabilities.http|sse: bool`; `sessionCapabilities.list|resume: object`;
69
- `auth.logout: object`; `authMethods[].id` is the string passed to
70
- `authenticate`.
71
-
72
- ## authenticate
73
-
74
- Request (param name is `methodId`, NOT `method` — `method` yields -32602 with
75
- `errors:[{"loc":["methodId"],"msg":"field required","type":"value_error.missing"}]`):
76
-
77
- ```json
78
- {"jsonrpc":"2.0","id":1,"method":"authenticate","params":{"methodId":"oauth-personal"}}
79
- ```
80
-
81
- Success result: `{}` (empty object). Failures (verified live):
82
-
83
- ```json
84
- {"jsonrpc":"2.0","id":2,"error":{"code":-32000,"message":"Onboarding failed: The authentication flow did not complete successfully.","data":{"reason":"onboarding_failed"}}}
85
- {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Onboarding failed: Timed out waiting for the authentication flow to complete.","data":{"reason":"onboarding_failed"}}}
86
- ```
87
-
88
- Unauthenticated `session/new` (before any auth) returns:
89
-
90
- ```json
91
- {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Authentication required","data":{"message":"No authentication method selected. Either call the `authenticate` method (supports oauth-personal, gemini-api-key, agent-platform), or set `auth.type` in settings.json (/home/<u>/.gemini/antigravity-acp/settings.json) to one of: oauth-personal, gemini-api-key (requires GEMINI_API_KEY env var), oauth-business (Gemini Enterprise; requires gcp.project/location), agent-platform (formerly 'vertex-ai', still accepted; requires GOOGLE_API_KEY, or a project and location from GOOGLE_CLOUD_PROJECT/GOOGLE_CLOUD_LOCATION or gcp.project/gcp.location with Application Default Credentials)."}}}
92
- ```
93
-
94
- ### settings.json shapes (official Zed docs + error message)
95
-
96
- ```jsonc
97
- // ~/.gemini/antigravity-acp/settings.json
98
- {"auth": {"type": "oauth-personal"}}
99
- {"auth": {"type": "gemini-api-key"}} // + GEMINI_API_KEY env
100
- {"auth": {"type": "oauth-business"}, "gcp": {"project": "<ID>", "location": "<REGION>"}}
101
- {"auth": {"type": "agent-platform"}, "gcp": {"project": "<ID>", "location": "<REGION>"}}
102
- ```
103
-
104
- settings.json is read at server STARTUP: writing it while a server process
105
- runs has no effect on that process (verified: the error persisted until
106
- restart).
107
-
108
- ### oauth-personal flow (headless, verified end-to-end)
109
-
110
- On `authenticate` the server: starts a loopback HTTP listener on
111
- `127.0.0.1:<random port>` (observed 44603, 34653, 59563, 35293 — random per
112
- run), composes a Google OAuth URL, and tries to open a browser. HEADLESS: it
113
- never prints the URL on stdout or stderr (`--debug` stderr stays empty) and
114
- execs nothing (strace-verified). The URL exists only in process memory.
115
- Capture trick (works): set `BROWSER` to a script that appends its argument to
116
- a file, and/or prepend a fake `xdg-open` to `PATH` (see
117
- `probe-logs/capurl.sh`). Remote login: `ssh -N -L <port>:127.0.0.1:<port>
118
- <host>` on the user's machine, then open the URL there; Google's redirect
119
- lands on the server's loopback through the tunnel. NEVER curl the loopback
120
- yourself: a `GET /` without the code param is treated as a failed callback
121
- and aborts the flow ("Authentication failed" page).
122
-
123
- Captured URL anatomy (run 5):
124
-
125
- ```
126
- https://accounts.google.com/o/oauth2/v2/auth
127
- ?response_type=code
128
- &client_id=1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com
129
- &redirect_uri=http%3A%2F%2F127.0.0.1%3A35293%2F
130
- &scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform
131
- +https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email
132
- +https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faicode
133
- &state=<random>
134
- &access_type=offline
135
- &prompt=consent
136
- &code_challenge=<S256>
137
- &code_challenge_method=S256
138
- ```
139
-
140
- Timing: the onboarding window is minutes-scale (one flow timed out at ~8.5
141
- minutes; a second completed within ~1.5 minutes). Tokens persist at
142
- `~/.gemini/antigravity-acp/acp_token.json` (0600; 510 bytes; credential —
143
- never copy). A second server run after successful auth has NOT yet been
144
- tested (token-reuse across restart is a remaining probe).
145
-
146
- ## session/new
147
-
148
- Request:
149
-
150
- ```json
151
- {"jsonrpc":"2.0","id":3,"method":"session/new","params":{"cwd":"/abs/cwd","mcpServers":[]}}
152
- ```
153
-
154
- Result (verbatim, this is also the model catalog + config schema):
155
-
156
- ```json
157
- {"sessionId":"4d5a6c89-0be5-4d98-ac4d-dede75c780fe",
158
- "modes":{"currentModeId":"default","availableModes":[
159
- {"id":"default","name":"Default","description":"Default permission prompt flow"},
160
- {"id":"auto_edit","name":"Auto Edit","description":"Auto-approve file edit tools"},
161
- {"id":"yolo","name":"YOLO","description":"Auto-approve all tools"}]},
162
- "models":{"availableModels":[
163
- {"modelId":"gemini-3.8-flash-high","name":"Gemini 3.8 Flash (High)","description":"gemini-3.8-flash-high"},
164
- {"modelId":"gemini-3.8-flash-medium","name":"Gemini 3.8 Flash (Medium)","description":"gemini-3.8-flash-medium"},
165
- {"modelId":"gemini-3.8-flash-low","name":"Gemini 3.8 Flash (Low)","description":"gemini-3.8-flash-low"},
166
- {"modelId":"gemini-3.7-flash-high","name":"Gemini 3.7 Flash (High)","description":"gemini-3.7-flash-high"},
167
- {"modelId":"gemini-3.7-flash-medium","name":"Gemini 3.7 Flash (Medium)","description":"gemini-3.7-flash-medium"},
168
- {"modelId":"gemini-3.7-flash-low","name":"Gemini 3.7 Flash (Low)","description":"gemini-3.7-flash-low"},
169
- {"modelId":"gemini-3.6-flash-high","name":"Gemini 3.6 Flash (High)","description":"gemini-3.6-flash-high"},
170
- {"modelId":"gemini-3.6-flash-medium","name":"Gemini 3.6 Flash (Medium)","description":"gemini-3.6-flash-medium"},
171
- {"modelId":"gemini-3.6-flash-low","name":"Gemini 3.6 Flash (Low)","description":"gemini-3.6-flash-low"},
172
- {"modelId":"gemini-pro-agent","name":"Gemini 3.1 Pro (High)","description":"gemini-pro-agent"},
173
- {"modelId":"gemini-3.1-pro-low","name":"Gemini 3.1 Pro (Low)","description":"gemini-3.1-pro-low"}],
174
- "currentModelId":"gemini-3.7-flash-high"},
175
- "configOptions":[
176
- {"currentValue":"gemini-3.7-flash-high","id":"model","name":"Model","category":"model","type":"select","options":[{"value":"gemini-3.8-flash-high","name":"Gemini 3.8 Flash (High)","description":"..."}, "...all 11..."]},
177
- {"currentValue":"default","id":"mode","name":"Session Mode","category":"mode","type":"select","options":[{"value":"default","name":"Default","description":"Default permission prompt flow"},{"value":"auto_edit","name":"Auto Edit","description":"Auto-approve file edit tools"},{"value":"yolo","name":"YOLO","description":"Auto-approve all tools"}]}]}
178
- ```
179
-
180
- Key facts:
181
-
182
- - `sessionId` is a UUID, chosen by the server.
183
- - The FULL model catalog ships in `session/new` (the `agy models` CLI is not
184
- needed for the ACP engine). Default model: `gemini-3.7-flash-high`.
185
- - Model ids are FULL slugs with the effort tier baked in
186
- (`gemini-3.8-flash-low` = Flash, low effort). One `--model`-style split is
187
- NOT needed here. `gemini-pro-agent` is the 3.1 Pro High slug (differs from
188
- the CLI's `gemini-3.1-pro-high` print).
189
- - Modes map onto our config knobs: `default` = permission prompt flow,
190
- `auto_edit` ≈ `--mode accept-edits` for edit tools, `yolo` ≈
191
- `--dangerously-skip-permissions`.
192
- - `mode` and `model` are both `configOptions` (`type: select`), settable via
193
- `session/set_config_option`.
194
- - `availableCommands` arrive right after via `session/update`
195
- (`available_commands_update`): observed commands `plan` ("generates an
196
- implementation plan artifact and awaits user approval") and `logout`.
197
-
198
- ## session/set_config_option
199
-
200
- Request (param is `configId`, NOT `configOptionId` — wrong name gives -32602
201
- `errors:[{"loc":["configId"],"msg":"field required"}]`):
202
-
203
- ```json
204
- {"jsonrpc":"2.0","id":5,"method":"session/set_config_option","params":{"sessionId":"<uuid>","configId":"model","value":"gemini-3.8-flash-low"}}
205
- ```
206
-
207
- Result: the full `configOptions` array with `currentValue` updated
208
- (`"currentValue":"gemini-3.8-flash-low"` observed). LIVE VERIFIED: model and
209
- effort switch per session with no process recycle. Mode switch
210
- (`configId:"mode"`) is expected to work identically (same option shape) but
211
- was not explicitly exercised.
212
-
213
- ## session/prompt
214
-
215
- Request (`prompt` is an array of typed content blocks):
216
-
217
- ```json
218
- {"jsonrpc":"2.0","id":6,"method":"session/prompt","params":{"sessionId":"<uuid>","prompt":[{"type":"text","text":"Reply with exactly: HELLO ACP. Do not use any tools."}]}}
219
- ```
220
-
221
- Streaming: `session/update` NOTIFICATIONS (no id), then the `session/prompt`
222
- response. Discriminator is `update.sessionUpdate`.
223
-
224
- `agent_message_chunk` (verbatim):
225
-
226
- ```json
227
- {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"<uuid>","update":{"content":{"text":"HELLO ACP.","type":"text"},"sessionUpdate":"agent_message_chunk"}}}
228
- ```
229
-
230
- Terminal response:
231
-
232
- ```json
233
- {"jsonrpc":"2.0","id":6,"result":{"stopReason":"end_turn"}}
234
- ```
235
-
236
- Observed stop reasons so far: `end_turn` only. Schema enumerates
237
- `max_tokens`, `max_turn_requests`, `refusal`, `cancelled` (cancelled
238
- unreachable on RC01 — no cancel method).
239
-
240
- `available_commands_update` (verbatim):
241
-
242
- ```json
243
- {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"<uuid>","update":{"availableCommands":[{"name":"plan","description":"Plan carefully before executing a task (generates an implementation plan artifact and awaits user approval)."},{"name":"logout","description":"Log out and clear stored credentials."}],"sessionUpdate":"available_commands_update"}}}
244
- ```
245
-
246
- ### Streaming behavior (300-line and 3000-line stress writes)
247
-
248
- - Chunks are pure DELTAS. Verified by content: numbers split mid-token across
249
- chunk boundaries (`"...\n39\n40\n4"` then `"1\n42\n43..."`), and no chunk
250
- ever contained a full prefix of the accumulated text. The stream-json
251
- cumulative-resend failure mode was NOT observed over ACP (defensive guard
252
- stays cheap insurance).
253
- - Throughput: 300 numbers (~600 tokens) completed within one 25 s poll;
254
- 3000 numbers streamed continuously at roughly 100 numbers per 1.5 s.
255
- - Ordering: notifications strictly before the prompt response; no
256
- interleaving with other responses observed.
257
-
258
- ### Usage
259
-
260
- NO usage/token fields appeared anywhere in `initialize`, `session/new`,
261
- `set_config_option`, `agent_message_chunk`, prompt results, or tool flows
262
- (run 6). Gate B FINAL: ABSENT on RC01. Consequence in the plan (updated
263
- 2026-09-07): Gate B is INFORMATIONAL - zero-usage is documented and accepted
264
- on ACP, never a blocker. Both engines are permanently maintained peers; no
265
- deletion is planned.
266
-
267
- ## session/cancel
268
-
269
- **NOT IMPLEMENTED on RC01** (despite appearing in the ACP schema source and
270
- our plan):
271
-
272
- ```json
273
- {"jsonrpc":"2.0","id":12,"method":"session/cancel","params":{"sessionId":"<uuid>"}}
274
- {"jsonrpc":"2.0","id":12,"error":{"code":-32601,"message":"Method not found","data":{"method":"session/cancel"}}}
275
- ```
276
-
277
- An in-flight `session/prompt` CANNOT be stopped over the protocol on this
278
- build (verified: a 3000-number stream ran to completion after cancel
279
- returned -32601). Abort fallback candidate: kill the connection and
280
- `session/load` on the next server (probe pending).
281
-
282
- ## Error shapes (verified)
283
-
284
- ```jsonc
285
- // -32602 Invalid params (wrong/missing param name) — includes field location
286
- {"code":-32602,"message":"Invalid params","data":{"errors":[{"loc":["methodId"],"msg":"field required","type":"value_error.missing"}]}}
287
- // -32000 server-state errors — message + data{message|reason}
288
- {"code":-32000,"message":"Authentication required","data":{"message":"..."}}
289
- {"code":-32000,"message":"Onboarding failed: ...","data":{"reason":"onboarding_failed"}}
290
- // -32601 method not implemented
291
- {"code":-32601,"message":"Method not found","data":{"method":"session/cancel"}}
292
- ```
293
-
294
- Note the schema-mismatch pattern: two live params differ from the obvious
295
- names (`methodId` not `method`; `configId` not `configOptionId`). Expect
296
- thirds; code against captured examples, and treat -32602 `loc` as the
297
- correction oracle.
298
-
299
- ## Server-side persistence (on-disk formats)
300
-
301
- `~/.gemini/antigravity-acp/` layout:
302
-
303
- | Path | Purpose |
304
- | --- | --- |
305
- | `settings.json` | auth config (world-readable 0644); read at startup only |
306
- | `acp_token.json` | OAuth tokens (0600). CREDENTIAL. Never copy or print. |
307
- | `conversations/<uuid>.db` | SQLite 3 conversation DB (WAL mode; 163 KB main + 4 MB WAL for our probe session) |
308
- | `conversations/<uuid>.db-wal`, `.db-shm` | WAL pair |
309
- | `conversations/<uuid>.meta` | 46-byte JSON: `{"cwd":"<session cwd>"}` |
310
-
311
- Session ids match conversation DB file stems (our `sessionId`
312
- `4d5a6c89-0be5-4d98-ac4d-dede75c780fe` → `conversations/4d5a6c89-....db`).
313
-
314
- Snapshot DDL (`probe-logs/acp-server-conversations/schema.sql`; 7 tables):
315
-
316
- ```sql
317
- CREATE TABLE `trajectory_meta` (`trajectory_id` text,`cascade_id` text,`trajectory_type` integer,`source` integer,PRIMARY KEY (`trajectory_id`));
318
- CREATE TABLE `steps` (`idx` integer,`step_type` integer NOT NULL DEFAULT 0,`status` integer NOT NULL DEFAULT 0,`has_subtrajectory` numeric NOT NULL DEFAULT false,`metadata` blob,`error_details` blob,`permissions` blob,`task_details` blob,`render_info` blob,`step_payload` blob,`step_format` integer NOT NULL DEFAULT 0,PRIMARY KEY (`idx`));
319
- CREATE TABLE `gen_metadata` (`idx` integer,`data` blob,`size` integer NOT NULL DEFAULT 0,PRIMARY KEY (`idx`));
320
- CREATE TABLE `executor_metadata` (`idx` integer,`data` blob,PRIMARY KEY (`idx`));
321
- CREATE TABLE `parent_references` (`idx` integer,`data` blob,PRIMARY KEY (`idx`));
322
- CREATE TABLE `trajectory_metadata_blob` (`id` text DEFAULT "main",`data` blob,PRIMARY KEY (`id`));
323
- CREATE TABLE `battle_mode_infos` (`idx` integer,`data` blob,PRIMARY KEY (`idx`));
324
- ```
325
-
326
- Two facts matter for us:
327
-
328
- 1. `steps.step_payload` is an OPAQUE BLOB — the same two-phase-write SQLite
329
- blob family that silently broke the pre-1.3.2 legacy engine (issue #1).
330
- Never poll these DBs; that decision is re-validated by this schema.
331
- 2. WAL quirk: a read-only open of the live `.db` shows an EMPTY
332
- `sqlite_master` (schema lives in the WAL until checkpoint); copying
333
- `db+wal+shm` yields a copy that also reads empty (stale `-shm` suppresses
334
- replay). To snapshot: `VACUUM INTO` from an opened connection (that is how
335
- `probe-logs/acp-server-conversations/probe-session-4d5a6c89.db` was made).
336
-
337
- ## Client capability posture (phase 1 decision, verified harmless)
338
-
339
- `initialize` sent `fs:false`, `terminal:false`. Effects observed: `session/new`
340
- and pure-text prompts work; the agent self-executes its own tools and asks
341
- via `session/request_permission` (verified in run 6, tool probe). No `fs/*`
342
- or `terminal/*` delegation occurred with capabilities off.
343
-
344
- ## Timing summary (Gate E evidence)
345
-
346
- | Event | Observation |
347
- | --- | --- |
348
- | Spawn → initialize response | ~5-6 s (three runs, consistent; 1.5 GB binary, page cache warm) |
349
- | session/new response | < 2 s |
350
- | set_config_option response | < 1 s |
351
- | Prompt first chunk | ~1-2 s (Flash, low effort) |
352
- | OAuth onboarding window | minutes-scale; one timeout observed at ~8.5 min |
353
- | Bridge `tools/call` HTTP request | agy's MCP client abandons the request at ~180s (observed twice at exactly 180.000s on 2026-09-05: AskClaude at 225.7s, then a follow-up exec_command). Mitigations: the bridge early-acks any call still running after ~20s with a poll handle (`bridge_poll_result`, `call-tool-escalated` log event) so the request never reaches the deadline; escalated parks re-arm at 30 min for human-gated latency; a result that outlives polling is re-delivered as a new same-conversation prompt (late delivery, `late-result` log event). `mcp-server` logs `progress-token` when a request carries `_meta.progressToken`: if agy ever sends one, progress notifications become a testable zero-UX fix |
354
- | Steady RSS | ~327 MB (5 min mixed load; VSZ ~5.3 GB is TCMalloc reservation) |
355
-
356
- ## Run 6 findings (2026-09-03, post-restart session; raw traffic
357
- `probe-logs/acp-traffic-run6-restart-load-tools.jsonl`, 28 clean JSONL lines)
358
-
359
- All remaining probes executed. Results:
360
-
361
- 1. **Token persistence: PASS.** New server process, `session/load` with NO
362
- `authenticate` call succeeded. `acp_token.json` + `settings.json` carry
363
- auth across restarts.
364
- 2. **`session/load`: PASS.** Params `{sessionId, cwd, mcpServers}`. On
365
- success the server REPLAYS the whole conversation as `session/update`
366
- notifications: `user_message_chunk` / `agent_message_chunk` PAIRS per
367
- turn, in order. Replay re-sends prior replies as FULL TEXT (one giant
368
- chunk), not the original deltas. Driver rule: on load, incoming chunks
369
- are HISTORY replay, never live generation. Then:
370
- `available_commands_update`.
371
- 3. **Config does NOT persist across restart.** The loaded session came back
372
- `currentModelId: gemini-3.7-flash-high` (default) although it had been
373
- switched to `gemini-3.8-flash-low` before the kill. Model/effort/mode
374
- must be re-applied by the driver after every restart/load.
375
- 4. **`session/request_permission` verified live.** Agent-to-client REQUEST:
376
- `{sessionId, toolCall{...full tool_call object incl. diff content...},
377
- options:[{optionId:"allow",name:"Allow",kind:"allow_once"},
378
- {optionId:"deny",name:"Deny",kind:"reject_once"}]}`. Client answers:
379
- `{"outcome":{"outcome":"selected","optionId":"allow"}}`. Only once-kinds
380
- observed (no allow_always in this payload).
381
- 5. **`tool_call` / `tool_call_update` shapes verified.** Fields:
382
- `toolCallId`, `title` ("Run create_file?"), `kind` (`edit`/`read`),
383
- `status` lifecycle `pending → in_progress → completed|failed`, `content`
384
- with `{type:"diff", path, newText, _meta:{kind:"add"}}` (the edit arrives
385
- AS A DIFF), `locations:[{path}]`, `rawInput` (full args object), optional
386
- `rawOutput` on completion. Gate C PASS: enough content to retire
387
- wrapper-replay rendering later.
388
- 6. **toolCallId formats are inconsistent:** `33eb71a0...` (32-hex),
389
- `<sessionId>:7` (composite), `call_859008` (OpenAI-style). Treat as
390
- opaque strings.
391
- 7. **Approved-id supersede quirk.** After `allow`, the approved
392
- `create_file` call reported `status:"failed",
393
- rawOutput:"Tool call was approved but never executed."` while the actual
394
- edit executed under a different toolCallId (`<sessionId>:7`,
395
- `edit_file`) and succeeded (file created, read back, confirmed).
396
- Never assume approved toolCallId == executed toolCallId; track effects,
397
- not ids.
398
- 8. **Image prompt: PASS end-to-end.** 1x1 red PNG as `{type:"image",
399
- data:<base64>, mimeType:"image/png"}` + text question → answer "Red".
400
- (Community reports said the agy CLI was broken for images; the ACP path
401
- works.)
402
- 9. **Gate F: PASS at shape level.** Accepted entry:
403
- `{"name":"pi-bridge","type":"http","url":"http://...","headers":[]}`.
404
- `headers` is a LIST. Permitted types: `http`, `sse` (same headers
405
- shape), stdio (`command`/`args`/`env`), and an `acp` type (needs `id`).
406
- A DEAD url is accepted at `session/new` (connection is lazy). Phase-1
407
- TODO: point at the real bridge (MCP SDK Streamable HTTP) and confirm the
408
- agent's http client completes `tools/list` + `tools/call` (G9
409
- round-trip end-to-end).
410
- 10. **`session/set_mode` (`modeId`) and `session/close` work** (`{}`
411
- results). `close` is per-session; the connection survives it.
412
- `set_mode` and `set_config_option("mode")` are redundant paths to the
413
- same state.
414
- 11. **Usage: ABSENT on RC01 (Gate B final).** No token fields in any
415
- payload across both runs, including tool flows. Accept zero-usage
416
- (existing documented fallback); re-check on future builds.
417
- 12. **Steady RSS ~327 MB** under 5 minutes of mixed load.
418
-
419
- Probe session closed cleanly (`session/close` then kill). Final gate status:
420
- plan section 8.1.
421
-
422
- ## Phase-2 probe findings (2026-09-03; raw traffic
423
- `probe-logs/acp-phase2-traffic.jsonl`, one session, four labeled prompts,
424
- all `end_turn`)
425
-
426
- 1. **`agent_thought_chunk` is SPARSE on RC01.** A step-by-step math prompt
427
- produced ZERO thought chunks - the reasoning shipped as plain
428
- `agent_message_chunk` text. The only thought chunk in the capture came
429
- from the `/plan` flow. Shape is identical to the message chunk
430
- (`content:{type:"text",text}`), already mapped. Consequence: the
431
- thinking-block pipeline rarely renders on flash-low; do not build UI
432
- assumptions on frequent thought deltas.
433
- 2. **Image prompts: PASS at real resolution.** 64x64 two-tone PNG (top
434
- red / bottom blue) sent as `{type:"image",data,mimeType}` blocks ahead
435
- of the text block answered "Top: Red, Bottom: Blue". Mixed
436
- image+text prompt arrays work; no 1x1-only limitation.
437
- 3. **Tool frames: three mappings the phase-1 driver got wrong.**
438
- - MCP tools wrap args in an envelope: `rawInput:{arguments:{text}}`.
439
- Native `edit_file` carries args directly (`rawInput:{file_path}`).
440
- - MCP tool titles are "<server>_<tool>" ("pi-bridge_bridge_echo"); the
441
- clean name hides in `_meta.mcp.tool`.
442
- - A completed `tool_call_update`'s `rawOutput` is the server's DISPLAY
443
- string, often just the tool title ("Call bridge_echo"), NOT the
444
- result. `content[]` entries wrap their payload
445
- (`{type:"content",content:{type:"text",text}}`); for edits the same
446
- array carries the real diff (`newText`...). The MCP result itself
447
- reaches the model out-of-band: the client answered ECHO:PROBE-9
448
- correctly without the result ever appearing in an update.
449
- 4. **`/plan` command flow (F.7 answered: the server INTERCEPTS leading
450
- slash commands).** "/plan Organize a birthday party" produced: one
451
- `agent_thought_chunk`, a `create_file` tool_call with the full plan
452
- diff in `content[]`, an `edit_file` into the server's brain dir
453
- (`~/.gemini/antigravity-acp/brain/<sessionId>/...`), and a closing
454
- message asking for decisions. It WRITES under the auto permission
455
- policy. **Verdict: ACP has NO review-only mode.** The three modes
456
- (default/auto_edit/yolo) are permission modes only; the CLI's
457
- `--mode plan` has no ACP equivalent. The committed exception stands:
458
- plan delegations keep `agy -p --mode plan`.
459
- 5. **No `plan` sessionUpdate type exists** on RC01. Plan artifacts arrive
460
- as ordinary edit `tool_call`s (rendered as tool cards), not as a
461
- distinct update stream. The phase-2 idea "plan updates as thinking
462
- labels" is observed-absent, not deferred.
@@ -1,50 +0,0 @@
1
- # Antigravity Editor Integrations: Reverse Engineered Internals (historical)
2
-
3
- Date of original research: 2026-08-21 (full 551-line analysis preserved in git
4
- history; this stub summarizes what still matters). Sources: the official VSIX
5
- `Google.google-antigravity_1.0.0`, Zed's external-agents registry cache, the
6
- ACP release zip from `dl.google.com`, and the binaries on this machine — all
7
- verified by direct inspection or live execution.
8
-
9
- > **2026-09-04 status:** the ACP adoption this doc originally planned is
10
- > SHIPPED (phases 0-3 of docs/ACP-ADOPTION-PLAN.md). Everything about the ACP
11
- > server's protocol now lives in docs/ACP-PROTOCOL-REFERENCE.md (far more
12
- > complete than section 3 below); the adoption decision and trade-offs live
13
- > in ACP-ADOPTION-PLAN.md sections 1, 4, and 8.1 (superseding sections 9-10
14
- > here). The VSIX internals below are retained as ecosystem reference only —
15
- > this bridge uses Mechanism B and has no plans to touch Mechanism A.
16
-
17
- ## The three mechanisms (executive summary)
18
-
19
- 1. **Mechanism A, VS Code and JetBrains extension**: the extension spawns
20
- `agy --hub` (hidden flag; `AGY_ENABLE_HUB=1`), a local HTTP server serving
21
- the complete Antigravity web UI, embedded in an iframe. The webapp drives
22
- the agent over an internal WebSocket protocol; IDE capabilities flow
23
- through a protobuf RPC bridge over postMessage. Never documented by
24
- Google.
25
- 2. **Mechanism B, Zed and any ACP client**: Google publishes
26
- `agy_acp_server`, a dedicated binary speaking Agent Client Protocol v1,
27
- JSON-RPC over stdio. The sanctioned programmatic surface — this is what
28
- the bridge's ACP engine drives.
29
- 3. **Hidden, `agy agentapi`**: an undocumented subcommand that proxies
30
- conversation control into a running Antigravity IDE language server over
31
- HTTP via the `ANTIGRAVITY_LS_ADDRESS` environment variable. Documented
32
- nowhere else; potential future integration surface, unexamined.
33
-
34
- ## Facts worth keeping
35
-
36
- - Two distinct `agy` install locations exist: `~/.local/bin/agy` (the
37
- self-updating CLI, what `agy install` configures) and `~/.gemini/bin/agy`
38
- (where the VS Code extension's auto-installer pins its own copy). They are
39
- independent and can be different versions.
40
- - The VSIX auto-installer resolves binaries from
41
- `https://antigravity-cli-auto-updater-974169037036.us-central1.run.app/manifests/{goos}_{goarch}.json`
42
- (production) or a GCS dogfood bucket, verifies sha256/sha512 when present,
43
- and installs to `~/.gemini/bin/agy`.
44
- - The hub process signals OAuth via the stdout prefix
45
- `ANTIGRAVITY_OPEN_URL:`; the extension opens the URI with
46
- `vscode.env.openExternal`. (The bridge's ACP auth uses the same
47
- loopback-redirect OAuth flow via the BROWSER-capture trick — see
48
- docs/ACP-PROTOCOL-REFERENCE.md.)
49
- - The ACP conversation store is per-session SQLite with opaque
50
- `steps.step_payload` blobs. Never poll these DBs.
@@ -1,33 +0,0 @@
1
- # Approval gate (agy native tools)
2
-
3
- agy is not a plain model: it runs its own agent loop with its own native tools (`run_command`, `create_file`, `edit_file`, ...). Those calls execute inside agy with no pi involvement, so pi's permission extensions never saw them. The approval gate closes that gap: agy native tool calls pass through a pi-side approval in a form the existing permission-extension ecosystem gates with zero changes.
4
-
5
- Mechanics: the extension stages an `.agents/hooks.json` group in the workspace; the Antigravity server/CLI fires a `PreToolUse` hook before each mutating native tool runs. The hook script (generated, per-pid, mode 0600 because it embeds the bridge token) POSTs the call to the bridge and polls for a decision. The bridge parks it, the provider interrupts the pi-side view of the agy turn with a `toolUse` for a SHADOW tool named `bash`/`write`/`edit` (same schema as the real builtin plus internal `__agy*` marker fields), and pi's whole `tool_call` surface applies: any installed permission extension gates the call unchanged, and only if nothing blocks does the fallback policy run. The decision travels back to the hook and agy enforces it; on deny the reason text is what agy's model sees. Marker calls never execute locally (a ticket check denies forged ones); non-marker calls delegate to a factory twin of the real builtin, so normal pi bash/write/edit behavior is unchanged. Read-only agy tools stay ungated. Every decision lands in the daily log with tool names, source, and latency.
6
-
7
- Configuration (`/agy` config keys or environment):
8
-
9
- ```jsonc
10
- {
11
- "approvals": {
12
- "gateMode": "auto", // auto | shadow | dedicated | off
13
- "mode": "ask" // ask | allow | deny (fallback when no extension gates)
14
- }
15
- }
16
- // env: AGY_APPROVALS=shadow AGY_APPROVALS_MODE=ask
17
- ```
18
-
19
- `auto` (default) keeps the gate OFF until one of the known pi permission packages is detected (pi settings `packages` name-match or known config markers). `shadow` forces it on; `off` forces it off. `dedicated` currently stages the same shadow tools (the explicit `antigravity_approve` variant is planned; the config value is accepted today so the schema is stable). The fallback `mode` is consulted only when no extension blocked the call: `ask` shows a pi confirm dialog (headless runs deny, fail-closed), `allow` approves, `deny` blocks. Timeouts deny fail-closed: the staged hook timeout always exceeds the park budget, but a hook that outlives its timeout soft-passes upstream (verified against the ACP server), so the park always answers first.
20
-
21
- If you write your own gate extension, it sees a normal pi tool call:
22
-
23
- ```typescript
24
- export default function (pi) {
25
- pi.on("tool_call", async (event) => {
26
- if (event.input?.__agyGate && event.input.command?.startsWith("rm ")) {
27
- return { block: true, reason: "rm is not allowed through the agy gate" };
28
- }
29
- });
30
- }
31
- ```
32
-
33
- Live end-to-end verification (drive a real mutating agy turn through the gate) is still pending; see docs/TODO.md section 1.