@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.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/extensions/index.ts +17 -8
- package/package.json +5 -21
- package/src/approval-hook.ts +116 -98
- package/src/driver.ts +6 -1
- package/docs/ACP-ADOPTION-PLAN.md +0 -940
- package/docs/ACP-PROTOCOL-REFERENCE.md +0 -462
- package/docs/ANTIGRAVITY-INTEGRATIONS.md +0 -50
- package/docs/APPROVAL-GATE.md +0 -33
- package/docs/ARCHITECTURE.md +0 -116
- package/docs/DEVELOPMENT.md +0 -110
- package/docs/ENGINES.md +0 -46
- package/docs/PI-BRIDGE-GAPS.md +0 -97
- package/docs/TODO.md +0 -21
|
@@ -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.
|
package/docs/APPROVAL-GATE.md
DELETED
|
@@ -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.
|