@benjamolina/pi-antigravity-guard 0.1.0 → 0.3.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Pi Antigravity Guard
2
2
 
3
- `@benjamolina/pi-antigravity-guard` is a Pi extension that registers the text-only `antigravity-guard` provider. It exposes exactly one selectable model, `antigravity-gemini-3.8-flash`, which is sent to Antigravity as `gemini-3.8-flash`.
3
+ `@benjamolina/pi-antigravity-guard` is a static Pi extension that registers the text-only `antigravity-guard` provider. It uses Pi-managed OAuth credentials and the existing Antigravity OAuth endpoint and quota path; it does not provide alternate credential routes, quota pools, fallback, or model substitution.
4
4
 
5
5
  ## Use
6
6
 
@@ -12,13 +12,32 @@ Install the package in Pi, then run:
12
12
 
13
13
  Choose browser login, or choose manual login and paste the complete callback URL. The loopback callback uses fixed port `51121`; browser callback failures, remote shells, and port conflicts can use the manual callback-URL flow.
14
14
 
15
- Start a fresh text-only session with `--no-builtin-tools` and disable any active extension-provided tools. That flag does not disable extension tools by itself. Tools, tool history, images, and thinking content are unsupported and rejected rather than silently changed.
15
+ Start a fresh text-only session with `--no-builtin-tools` and disable any active extension-provided tools. That flag does not disable extension tools by itself. Tools, tool history, and images are unsupported and rejected rather than silently changed.
16
+
17
+ ## Supported static catalog
18
+
19
+ The provider registers only these evidence-admitted public IDs, in this order. `off` is available for every row even though it is omitted from Pi's level map; omitted reasoning also selects that row's `off` route. Unsupported levels are not advertised and are rejected before transport.
20
+
21
+ | Public ID | Exposed Pi levels | Context / output |
22
+ |---|---|---:|
23
+ | `antigravity-gemini-3.8-flash` | off, low, medium, high | 1,048,576 / 65,536 |
24
+ | `antigravity-gemini-3.7-flash` | off, low, medium, high | 1,048,576 / 65,536 |
25
+ | `antigravity-gemini-3.6-flash` | off, low, medium, high | 1,048,576 / 65,536 |
26
+ | `antigravity-gemini-3.1-pro` | off, low, high | 1,048,576 / 65,535 |
27
+ | `antigravity-claude-sonnet-4.6` | off, high | 250,000 / 64,000 |
28
+ | `antigravity-claude-opus-4.6-thinking` | off, high | 250,000 / 64,000 |
29
+ | `antigravity-gpt-oss-120b` | off, medium | 131,072 / 32,768 |
30
+
31
+ Gemini 3.8 remains compatible with the released mapping: `antigravity-gemini-3.8-flash` sends `gemini-3.8-flash-tiered`; `off` sends native `thinkingLevel: "low"` with hidden thoughts, and `low`, `medium`, and `high` send their matching native level with visible thoughts.
32
+
33
+ `antigravity-gemini-3.5-flash` is not registered or advertised as supported because its recorded HTTP-200 response lacks the strict terminal metadata required for admission.
16
34
 
17
35
  ## Limits and safety
18
36
 
19
- - This provider stores credentials through Pi's OAuth lifecycle and never reads or changes OpenCode account files; each is independent.
37
+ An explicit `maxTokens` must be a positive integer no greater than the row's output limit. For a route with a positive finite thinking budget, it must also be greater than that budget. When `maxTokens` is omitted, the provider uses 4,096 unless a larger finite thinking budget needs a 1,024-token answer reserve, always bounded by the row's output limit. Custom `thinkingBudgets` are unsupported and rejected.
38
+
20
39
  - Costs are reported as zero because subscription usage is unpriced, not because access is free.
21
- - The documented public-to-wire model mapping is not proof that the model is live, available, or entitled. No authorized live OAuth or generation check has been run.
22
- - It has no account rotation, quota fallback, model substitution, or tool support.
40
+ - The catalog is static: it performs no startup or runtime model discovery, catalog synchronization, or dynamic registration.
41
+ - It has no account rotation, quota fallback, model substitution, tool support, or image support.
23
42
 
24
43
  To remove it, disable or uninstall `@benjamolina/pi-antigravity-guard` and reload Pi. Removal does not delete Pi-managed credentials or revoke tokens.
@@ -0,0 +1,390 @@
1
+ export type StandardReasoningLevel = "off" | "minimal" | "low" | "medium" | "high";
2
+ type NativeThinkingLevel = "low" | "medium" | "high";
3
+ type NativeRoute = {
4
+ readonly wireModel: string;
5
+ readonly thinking: {
6
+ readonly kind: "native-level";
7
+ readonly thinkingLevel: NativeThinkingLevel;
8
+ readonly includeThoughts: boolean;
9
+ };
10
+ };
11
+ type IntegerBudgetRoute = {
12
+ readonly wireModel: string;
13
+ readonly thinking: {
14
+ readonly kind: "budget";
15
+ readonly budget: number;
16
+ readonly includeThoughts: boolean;
17
+ } | {
18
+ readonly kind: "omit";
19
+ };
20
+ };
21
+ export type GenerationRoute = NativeRoute | IntegerBudgetRoute;
22
+ export interface CatalogEntry {
23
+ readonly publicId: string;
24
+ readonly descriptor: {
25
+ readonly name: string;
26
+ readonly thinkingLevelMap: Readonly<Record<"minimal" | "low" | "medium" | "high", string | null>>;
27
+ readonly contextWindow: number;
28
+ readonly maxTokens: number;
29
+ };
30
+ readonly routes: Readonly<Partial<Record<StandardReasoningLevel, GenerationRoute>>>;
31
+ readonly replay: {
32
+ readonly kind: "same-public-model" | "strip";
33
+ };
34
+ readonly response: {
35
+ readonly kind: "gemini-envelope";
36
+ readonly family: "gemini" | "claude" | "gpt-oss";
37
+ };
38
+ }
39
+ declare const CATALOG: readonly [{
40
+ readonly publicId: "antigravity-gemini-3.8-flash";
41
+ readonly descriptor: {
42
+ readonly name: "Gemini 3.8 Flash (Antigravity, text only)";
43
+ readonly thinkingLevelMap: {
44
+ readonly minimal: null;
45
+ readonly low: "low";
46
+ readonly medium: "medium";
47
+ readonly high: "high";
48
+ };
49
+ readonly contextWindow: 1048576;
50
+ readonly maxTokens: 65536;
51
+ };
52
+ readonly routes: {
53
+ readonly off: {
54
+ readonly wireModel: "gemini-3.8-flash-tiered";
55
+ readonly thinking: {
56
+ readonly kind: "native-level";
57
+ readonly thinkingLevel: "low";
58
+ readonly includeThoughts: false;
59
+ };
60
+ };
61
+ readonly low: {
62
+ readonly wireModel: "gemini-3.8-flash-tiered";
63
+ readonly thinking: {
64
+ readonly kind: "native-level";
65
+ readonly thinkingLevel: "low";
66
+ readonly includeThoughts: true;
67
+ };
68
+ };
69
+ readonly medium: {
70
+ readonly wireModel: "gemini-3.8-flash-tiered";
71
+ readonly thinking: {
72
+ readonly kind: "native-level";
73
+ readonly thinkingLevel: "medium";
74
+ readonly includeThoughts: true;
75
+ };
76
+ };
77
+ readonly high: {
78
+ readonly wireModel: "gemini-3.8-flash-tiered";
79
+ readonly thinking: {
80
+ readonly kind: "native-level";
81
+ readonly thinkingLevel: "high";
82
+ readonly includeThoughts: true;
83
+ };
84
+ };
85
+ };
86
+ readonly replay: {
87
+ readonly kind: "same-public-model";
88
+ };
89
+ readonly response: {
90
+ readonly kind: "gemini-envelope";
91
+ readonly family: "gemini";
92
+ };
93
+ }, {
94
+ readonly publicId: "antigravity-gemini-3.7-flash";
95
+ readonly descriptor: {
96
+ readonly name: "Gemini 3.7 Flash (Antigravity, text only)";
97
+ readonly thinkingLevelMap: {
98
+ readonly minimal: null;
99
+ readonly low: "low";
100
+ readonly medium: "medium";
101
+ readonly high: "high";
102
+ };
103
+ readonly contextWindow: 1048576;
104
+ readonly maxTokens: 65536;
105
+ };
106
+ readonly routes: {
107
+ readonly off: {
108
+ readonly wireModel: "gemini-3.7-flash-low";
109
+ readonly thinking: {
110
+ readonly kind: "budget";
111
+ readonly budget: 0;
112
+ readonly includeThoughts: false;
113
+ };
114
+ };
115
+ readonly low: {
116
+ readonly wireModel: "gemini-3.7-flash-low";
117
+ readonly thinking: {
118
+ readonly kind: "budget";
119
+ readonly budget: 1000;
120
+ readonly includeThoughts: true;
121
+ };
122
+ };
123
+ readonly medium: {
124
+ readonly wireModel: "gemini-3.7-flash-medium";
125
+ readonly thinking: {
126
+ readonly kind: "budget";
127
+ readonly budget: 4000;
128
+ readonly includeThoughts: true;
129
+ };
130
+ };
131
+ readonly high: {
132
+ readonly wireModel: "gemini-3.7-flash-high";
133
+ readonly thinking: {
134
+ readonly kind: "budget";
135
+ readonly budget: -1;
136
+ readonly includeThoughts: true;
137
+ };
138
+ };
139
+ };
140
+ readonly replay: {
141
+ readonly kind: "strip";
142
+ };
143
+ readonly response: {
144
+ readonly kind: "gemini-envelope";
145
+ readonly family: "gemini";
146
+ };
147
+ }, {
148
+ readonly publicId: "antigravity-gemini-3.6-flash";
149
+ readonly descriptor: {
150
+ readonly name: "Gemini 3.6 Flash (Antigravity, text only)";
151
+ readonly thinkingLevelMap: {
152
+ readonly minimal: null;
153
+ readonly low: "low";
154
+ readonly medium: "medium";
155
+ readonly high: "high";
156
+ };
157
+ readonly contextWindow: 1048576;
158
+ readonly maxTokens: 65536;
159
+ };
160
+ readonly routes: {
161
+ readonly off: {
162
+ readonly wireModel: "gemini-3.6-flash-low";
163
+ readonly thinking: {
164
+ readonly kind: "omit";
165
+ };
166
+ };
167
+ readonly low: {
168
+ readonly wireModel: "gemini-3.6-flash-low";
169
+ readonly thinking: {
170
+ readonly kind: "budget";
171
+ readonly budget: 1000;
172
+ readonly includeThoughts: true;
173
+ };
174
+ };
175
+ readonly medium: {
176
+ readonly wireModel: "gemini-3.6-flash-medium";
177
+ readonly thinking: {
178
+ readonly kind: "budget";
179
+ readonly budget: 4000;
180
+ readonly includeThoughts: true;
181
+ };
182
+ };
183
+ readonly high: {
184
+ readonly wireModel: "gemini-3.6-flash-high";
185
+ readonly thinking: {
186
+ readonly kind: "budget";
187
+ readonly budget: -1;
188
+ readonly includeThoughts: true;
189
+ };
190
+ };
191
+ };
192
+ readonly replay: {
193
+ readonly kind: "strip";
194
+ };
195
+ readonly response: {
196
+ readonly kind: "gemini-envelope";
197
+ readonly family: "gemini";
198
+ };
199
+ }, {
200
+ readonly publicId: "antigravity-gemini-3.1-pro";
201
+ readonly descriptor: {
202
+ readonly name: "Gemini 3.1 Pro (Antigravity, text only)";
203
+ readonly thinkingLevelMap: {
204
+ readonly minimal: null;
205
+ readonly low: "low";
206
+ readonly medium: null;
207
+ readonly high: "high";
208
+ };
209
+ readonly contextWindow: 1048576;
210
+ readonly maxTokens: 65535;
211
+ };
212
+ readonly routes: {
213
+ readonly off: {
214
+ readonly wireModel: "gemini-3.1-pro-low";
215
+ readonly thinking: {
216
+ readonly kind: "omit";
217
+ };
218
+ };
219
+ readonly low: {
220
+ readonly wireModel: "gemini-3.1-pro-low";
221
+ readonly thinking: {
222
+ readonly kind: "budget";
223
+ readonly budget: 1001;
224
+ readonly includeThoughts: true;
225
+ };
226
+ };
227
+ readonly high: {
228
+ readonly wireModel: "gemini-pro-agent";
229
+ readonly thinking: {
230
+ readonly kind: "budget";
231
+ readonly budget: 10001;
232
+ readonly includeThoughts: true;
233
+ };
234
+ };
235
+ };
236
+ readonly replay: {
237
+ readonly kind: "strip";
238
+ };
239
+ readonly response: {
240
+ readonly kind: "gemini-envelope";
241
+ readonly family: "gemini";
242
+ };
243
+ }, {
244
+ readonly publicId: "antigravity-claude-sonnet-4.6";
245
+ readonly descriptor: {
246
+ readonly name: "Claude Sonnet 4.6 (Antigravity, text only)";
247
+ readonly thinkingLevelMap: {
248
+ readonly minimal: null;
249
+ readonly low: null;
250
+ readonly medium: null;
251
+ readonly high: "high";
252
+ };
253
+ readonly contextWindow: 250000;
254
+ readonly maxTokens: 64000;
255
+ };
256
+ readonly routes: {
257
+ readonly off: {
258
+ readonly wireModel: "claude-sonnet-4-6";
259
+ readonly thinking: {
260
+ readonly kind: "budget";
261
+ readonly budget: 0;
262
+ readonly includeThoughts: false;
263
+ };
264
+ };
265
+ readonly high: {
266
+ readonly wireModel: "claude-sonnet-4-6";
267
+ readonly thinking: {
268
+ readonly kind: "budget";
269
+ readonly budget: 1024;
270
+ readonly includeThoughts: true;
271
+ };
272
+ };
273
+ };
274
+ readonly replay: {
275
+ readonly kind: "strip";
276
+ };
277
+ readonly response: {
278
+ readonly kind: "gemini-envelope";
279
+ readonly family: "claude";
280
+ };
281
+ }, {
282
+ readonly publicId: "antigravity-claude-opus-4.6-thinking";
283
+ readonly descriptor: {
284
+ readonly name: "Claude Opus 4.6 Thinking (Antigravity, text only)";
285
+ readonly thinkingLevelMap: {
286
+ readonly minimal: null;
287
+ readonly low: null;
288
+ readonly medium: null;
289
+ readonly high: "high";
290
+ };
291
+ readonly contextWindow: 250000;
292
+ readonly maxTokens: 64000;
293
+ };
294
+ readonly routes: {
295
+ readonly off: {
296
+ readonly wireModel: "claude-opus-4-6-thinking";
297
+ readonly thinking: {
298
+ readonly kind: "budget";
299
+ readonly budget: 0;
300
+ readonly includeThoughts: false;
301
+ };
302
+ };
303
+ readonly high: {
304
+ readonly wireModel: "claude-opus-4-6-thinking";
305
+ readonly thinking: {
306
+ readonly kind: "budget";
307
+ readonly budget: 1024;
308
+ readonly includeThoughts: true;
309
+ };
310
+ };
311
+ };
312
+ readonly replay: {
313
+ readonly kind: "strip";
314
+ };
315
+ readonly response: {
316
+ readonly kind: "gemini-envelope";
317
+ readonly family: "claude";
318
+ };
319
+ }, {
320
+ readonly publicId: "antigravity-gpt-oss-120b";
321
+ readonly descriptor: {
322
+ readonly name: "GPT-OSS 120B (Antigravity, text only)";
323
+ readonly thinkingLevelMap: {
324
+ readonly minimal: null;
325
+ readonly low: null;
326
+ readonly medium: "medium";
327
+ readonly high: null;
328
+ };
329
+ readonly contextWindow: 131072;
330
+ readonly maxTokens: 32768;
331
+ };
332
+ readonly routes: {
333
+ readonly off: {
334
+ readonly wireModel: "gpt-oss-120b-medium";
335
+ readonly thinking: {
336
+ readonly kind: "omit";
337
+ };
338
+ };
339
+ readonly medium: {
340
+ readonly wireModel: "gpt-oss-120b-medium";
341
+ readonly thinking: {
342
+ readonly kind: "budget";
343
+ readonly budget: 8192;
344
+ readonly includeThoughts: true;
345
+ };
346
+ };
347
+ };
348
+ readonly replay: {
349
+ readonly kind: "strip";
350
+ };
351
+ readonly response: {
352
+ readonly kind: "gemini-envelope";
353
+ readonly family: "gpt-oss";
354
+ };
355
+ }];
356
+ type Catalog = typeof CATALOG;
357
+ export type AntigravityPublicModelId = Catalog[number]["publicId"];
358
+ export type AntigravityWireModelId = Catalog[number]["routes"][keyof Catalog[number]["routes"]]["wireModel"];
359
+ export declare function defineCatalog<T extends readonly {
360
+ readonly publicId: string;
361
+ readonly descriptor?: CatalogEntry["descriptor"];
362
+ readonly routes: Readonly<Record<string, {
363
+ readonly wireModel: string;
364
+ readonly thinking?: GenerationRoute["thinking"];
365
+ }>>;
366
+ }[]>(entries: T): T;
367
+ export declare function listCatalogEntries(): readonly CatalogEntry[];
368
+ export declare function getCatalogEntry(publicId: unknown): CatalogEntry | undefined;
369
+ export declare function resolveGenerationRoute(entry: CatalogEntry, reasoning: unknown): GenerationRoute;
370
+ export declare function toPiModelDescriptor(entry: CatalogEntry): {
371
+ id: string;
372
+ name: string;
373
+ reasoning: boolean;
374
+ thinkingLevelMap: {
375
+ high: string | null;
376
+ low: string | null;
377
+ medium: string | null;
378
+ minimal: string | null;
379
+ };
380
+ input: "text"[];
381
+ cost: {
382
+ input: number;
383
+ output: number;
384
+ cacheRead: number;
385
+ cacheWrite: number;
386
+ };
387
+ contextWindow: number;
388
+ maxTokens: number;
389
+ };
390
+ export {};
@@ -0,0 +1,166 @@
1
+ const ANSWER_RESERVE = 1024;
2
+ const CATALOG = freeze(defineCatalog([{
3
+ publicId: "antigravity-gemini-3.8-flash",
4
+ descriptor: {
5
+ name: "Gemini 3.8 Flash (Antigravity, text only)",
6
+ thinkingLevelMap: { minimal: null, low: "low", medium: "medium", high: "high" },
7
+ contextWindow: 1_048_576,
8
+ maxTokens: 65_536,
9
+ },
10
+ routes: {
11
+ off: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "low", includeThoughts: false } },
12
+ low: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "low", includeThoughts: true } },
13
+ medium: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "medium", includeThoughts: true } },
14
+ high: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "high", includeThoughts: true } },
15
+ },
16
+ replay: { kind: "same-public-model" },
17
+ response: { kind: "gemini-envelope", family: "gemini" },
18
+ }, {
19
+ publicId: "antigravity-gemini-3.7-flash",
20
+ descriptor: {
21
+ name: "Gemini 3.7 Flash (Antigravity, text only)",
22
+ thinkingLevelMap: { minimal: null, low: "low", medium: "medium", high: "high" },
23
+ contextWindow: 1_048_576,
24
+ maxTokens: 65_536,
25
+ },
26
+ routes: {
27
+ off: { wireModel: "gemini-3.7-flash-low", thinking: { kind: "budget", budget: 0, includeThoughts: false } },
28
+ low: { wireModel: "gemini-3.7-flash-low", thinking: { kind: "budget", budget: 1000, includeThoughts: true } },
29
+ medium: { wireModel: "gemini-3.7-flash-medium", thinking: { kind: "budget", budget: 4000, includeThoughts: true } },
30
+ high: { wireModel: "gemini-3.7-flash-high", thinking: { kind: "budget", budget: -1, includeThoughts: true } },
31
+ },
32
+ replay: { kind: "strip" },
33
+ response: { kind: "gemini-envelope", family: "gemini" },
34
+ }, {
35
+ publicId: "antigravity-gemini-3.6-flash",
36
+ descriptor: {
37
+ name: "Gemini 3.6 Flash (Antigravity, text only)",
38
+ thinkingLevelMap: { minimal: null, low: "low", medium: "medium", high: "high" },
39
+ contextWindow: 1_048_576,
40
+ maxTokens: 65_536,
41
+ },
42
+ routes: {
43
+ off: { wireModel: "gemini-3.6-flash-low", thinking: { kind: "omit" } },
44
+ low: { wireModel: "gemini-3.6-flash-low", thinking: { kind: "budget", budget: 1000, includeThoughts: true } },
45
+ medium: { wireModel: "gemini-3.6-flash-medium", thinking: { kind: "budget", budget: 4000, includeThoughts: true } },
46
+ high: { wireModel: "gemini-3.6-flash-high", thinking: { kind: "budget", budget: -1, includeThoughts: true } },
47
+ },
48
+ replay: { kind: "strip" },
49
+ response: { kind: "gemini-envelope", family: "gemini" },
50
+ }, {
51
+ publicId: "antigravity-gemini-3.1-pro",
52
+ descriptor: {
53
+ name: "Gemini 3.1 Pro (Antigravity, text only)",
54
+ thinkingLevelMap: { minimal: null, low: "low", medium: null, high: "high" },
55
+ contextWindow: 1_048_576,
56
+ maxTokens: 65_535,
57
+ },
58
+ routes: {
59
+ off: { wireModel: "gemini-3.1-pro-low", thinking: { kind: "omit" } },
60
+ low: { wireModel: "gemini-3.1-pro-low", thinking: { kind: "budget", budget: 1001, includeThoughts: true } },
61
+ high: { wireModel: "gemini-pro-agent", thinking: { kind: "budget", budget: 10001, includeThoughts: true } },
62
+ },
63
+ replay: { kind: "strip" },
64
+ response: { kind: "gemini-envelope", family: "gemini" },
65
+ }, {
66
+ publicId: "antigravity-claude-sonnet-4.6",
67
+ descriptor: {
68
+ name: "Claude Sonnet 4.6 (Antigravity, text only)",
69
+ thinkingLevelMap: { minimal: null, low: null, medium: null, high: "high" },
70
+ contextWindow: 250_000,
71
+ maxTokens: 64_000,
72
+ },
73
+ routes: {
74
+ off: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 0, includeThoughts: false } },
75
+ high: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 1024, includeThoughts: true } },
76
+ },
77
+ replay: { kind: "strip" },
78
+ response: { kind: "gemini-envelope", family: "claude" },
79
+ }, {
80
+ publicId: "antigravity-claude-opus-4.6-thinking",
81
+ descriptor: {
82
+ name: "Claude Opus 4.6 Thinking (Antigravity, text only)",
83
+ thinkingLevelMap: { minimal: null, low: null, medium: null, high: "high" },
84
+ contextWindow: 250_000,
85
+ maxTokens: 64_000,
86
+ },
87
+ routes: {
88
+ off: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 0, includeThoughts: false } },
89
+ high: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 1024, includeThoughts: true } },
90
+ },
91
+ replay: { kind: "strip" },
92
+ response: { kind: "gemini-envelope", family: "claude" },
93
+ }, {
94
+ publicId: "antigravity-gpt-oss-120b",
95
+ descriptor: {
96
+ name: "GPT-OSS 120B (Antigravity, text only)",
97
+ thinkingLevelMap: { minimal: null, low: null, medium: "medium", high: null },
98
+ contextWindow: 131_072,
99
+ maxTokens: 32_768,
100
+ },
101
+ routes: {
102
+ off: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "omit" } },
103
+ medium: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "budget", budget: 8192, includeThoughts: true } },
104
+ },
105
+ replay: { kind: "strip" },
106
+ response: { kind: "gemini-envelope", family: "gpt-oss" },
107
+ }]));
108
+ const lookup = new Map(CATALOG.map((entry) => [entry.publicId, entry]));
109
+ function freeze(value) {
110
+ if (typeof value === "object" && value !== null) {
111
+ Object.freeze(value);
112
+ for (const child of Object.values(value))
113
+ freeze(child);
114
+ }
115
+ return value;
116
+ }
117
+ export function defineCatalog(entries) {
118
+ const identities = new Set();
119
+ for (const entry of entries) {
120
+ if (identities.has(entry.publicId))
121
+ throw new Error(`Duplicate public model ID: ${entry.publicId}`);
122
+ identities.add(entry.publicId);
123
+ if (!Object.keys(entry.routes).length)
124
+ throw new Error(`Model has no routes: ${entry.publicId}`);
125
+ if (!entry.descriptor)
126
+ continue;
127
+ for (const [level, route] of Object.entries(entry.routes)) {
128
+ if (!route.thinking)
129
+ throw new Error(`Route has no thinking policy: ${entry.publicId}/${level}`);
130
+ if (level !== "off" && !Object.hasOwn(entry.descriptor.thinkingLevelMap, level)) {
131
+ throw new Error(`Route is outside descriptor map: ${entry.publicId}/${level}`);
132
+ }
133
+ if (level !== "off" && entry.descriptor.thinkingLevelMap[level] === null) {
134
+ throw new Error(`Route is hidden by descriptor: ${entry.publicId}/${level}`);
135
+ }
136
+ if (route.thinking.kind === "budget" && route.thinking.budget > 0 && Number.isFinite(route.thinking.budget) && route.thinking.budget + ANSWER_RESERVE > entry.descriptor.maxTokens) {
137
+ throw new Error(`Finite thinking budget does not leave answer reserve: ${entry.publicId}/${level}`);
138
+ }
139
+ }
140
+ }
141
+ return entries;
142
+ }
143
+ export function listCatalogEntries() {
144
+ return CATALOG;
145
+ }
146
+ export function getCatalogEntry(publicId) {
147
+ return typeof publicId === "string" ? lookup.get(publicId) : undefined;
148
+ }
149
+ export function resolveGenerationRoute(entry, reasoning) {
150
+ const level = reasoning === undefined || reasoning === "off" ? "off" : reasoning;
151
+ if (typeof level !== "string" || !entry.routes[level])
152
+ throw new Error("Unsupported reasoning level");
153
+ return entry.routes[level];
154
+ }
155
+ export function toPiModelDescriptor(entry) {
156
+ return {
157
+ id: entry.publicId,
158
+ name: entry.descriptor.name,
159
+ reasoning: true,
160
+ thinkingLevelMap: { ...entry.descriptor.thinkingLevelMap },
161
+ input: ["text"],
162
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
163
+ contextWindow: entry.descriptor.contextWindow,
164
+ maxTokens: entry.descriptor.maxTokens,
165
+ };
166
+ }
package/dist/context.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai";
2
- declare const WIRE_MODEL = "gemini-3.8-flash-tiered";
3
2
  interface Part {
4
3
  text: string;
4
+ thought?: true;
5
+ thoughtSignature?: string;
5
6
  }
6
7
  interface Content {
7
8
  role: "user" | "model";
@@ -9,7 +10,7 @@ interface Content {
9
10
  }
10
11
  export interface GenerationRequest {
11
12
  project: string;
12
- model: typeof WIRE_MODEL;
13
+ model: string;
13
14
  request: {
14
15
  contents: Content[];
15
16
  systemInstruction?: {
@@ -18,9 +19,12 @@ export interface GenerationRequest {
18
19
  generationConfig: {
19
20
  temperature: number;
20
21
  maxOutputTokens: number;
21
- thinkingConfig: {
22
- thinkingLevel: "low";
23
- includeThoughts: false;
22
+ thinkingConfig?: {
23
+ thinkingLevel: "low" | "medium" | "high";
24
+ includeThoughts: boolean;
25
+ } | {
26
+ thinkingBudget: number;
27
+ includeThoughts: boolean;
24
28
  };
25
29
  };
26
30
  };
package/dist/context.js CHANGED
@@ -1,8 +1,8 @@
1
+ import { getCatalogEntry, resolveGenerationRoute } from "./catalog.js";
1
2
  const MAX_TEXT_BYTES = 8 * 1024 * 1024;
2
- const MAX_OUTPUT_TOKENS = 65_536;
3
- const PUBLIC_MODEL = "antigravity-gemini-3.8-flash";
4
- const WIRE_MODEL = "gemini-3.8-flash-tiered";
3
+ const PROVIDER = "antigravity-guard";
5
4
  const RESERVED_HEADERS = new Set(["authorization", "host", "content-type", "content-length"]);
5
+ const THINKING_LEVELS = ["low", "medium", "high"];
6
6
  export class ContextSerializationError extends Error {
7
7
  }
8
8
  export function serializeTextContext(input) {
@@ -14,16 +14,16 @@ export function serializeTextContext(input) {
14
14
  const options = field(input, "options");
15
15
  const project = field(input, "project", true);
16
16
  const requestId = field(input, "requestId", true);
17
- if (!isRecord(context) || !isRecord(model) || field(model, "id", true) !== PUBLIC_MODEL || typeof project !== "string" || typeof requestId !== "string")
17
+ const entry = isRecord(model) ? getCatalogEntry(field(model, "id", true)) : undefined;
18
+ if (!isRecord(context) || !entry || typeof project !== "string" || typeof requestId !== "string")
18
19
  fail("Invalid text context.");
20
+ const route = resolveGenerationRoute(entry, option(options, "reasoning"));
19
21
  const systemPrompt = field(context, "systemPrompt");
20
22
  const tools = field(context, "tools");
21
23
  if (systemPrompt !== undefined && typeof systemPrompt !== "string")
22
24
  fail("Text-only context is required.");
23
- if (tools !== undefined) {
24
- if (isDenseArray(tools).length)
25
- fail("Tools are not supported by this text-only provider.");
26
- }
25
+ if (tools !== undefined && isDenseArray(tools).length)
26
+ fail("Tools are not supported by this text-only provider.");
27
27
  validateOptions(options);
28
28
  let textBytes = byteLength(systemPrompt ?? "");
29
29
  const contents = [];
@@ -35,21 +35,25 @@ export function serializeTextContext(input) {
35
35
  fail("Tool history is not supported by this text-only provider.");
36
36
  if (role !== "user" && role !== "assistant")
37
37
  fail("Unsupported context role for this text-only provider.");
38
- const text = textParts(field(message, "content", true));
39
- textBytes += text.reduce((total, part) => total + byteLength(part.text), 0);
38
+ const assistant = role === "assistant";
39
+ const parts = messageParts(field(message, "content", true), assistant, assistant && entry.replay.kind === "same-public-model" && isSameProviderAndModel(message, entry.publicId));
40
+ textBytes += parts.reduce((total, part) => total + byteLength(part.text), 0);
40
41
  if (textBytes > MAX_TEXT_BYTES)
41
42
  fail("Text context is too large.");
42
- contents.push({ role: role === "assistant" ? "model" : "user", parts: text });
43
+ contents.push({ role: role === "assistant" ? "model" : "user", parts });
43
44
  }
44
45
  if (!contents.length)
45
46
  fail("A text conversation is required.");
46
47
  const systemInstruction = systemPrompt === undefined ? undefined : { parts: [{ text: systemPrompt }] };
47
48
  const temperature = option(options, "temperature");
48
49
  const maxTokens = option(options, "maxTokens");
49
- return { project, model: WIRE_MODEL, request: { contents, ...(systemInstruction ? { systemInstruction } : {}), generationConfig: {
50
+ return { project, model: route.wireModel, request: { contents, ...(systemInstruction ? { systemInstruction } : {}), generationConfig: {
50
51
  temperature: typeof temperature === "number" ? temperature : 1,
51
- maxOutputTokens: typeof maxTokens === "number" ? maxTokens : 4096,
52
- thinkingConfig: { thinkingLevel: "low", includeThoughts: false },
52
+ maxOutputTokens: resolveOutputTokens(maxTokens, entry.descriptor.maxTokens, route.thinking),
53
+ ...(() => {
54
+ const thinkingConfig = serializeThinkingConfig(route.thinking);
55
+ return thinkingConfig ? { thinkingConfig } : {};
56
+ })(),
53
57
  } }, requestType: "agent", userAgent: "antigravity", requestId };
54
58
  }
55
59
  catch (error) {
@@ -58,36 +62,87 @@ export function serializeTextContext(input) {
58
62
  fail("Invalid text context.");
59
63
  }
60
64
  }
61
- function textParts(content) {
65
+ function messageParts(content, assistant, sameProviderAndModel) {
62
66
  if (typeof content === "string")
63
67
  return content ? [{ text: content }] : fail("A text conversation is required.");
64
68
  const parts = isDenseArray(content);
65
69
  if (!parts.length)
66
70
  fail("A text conversation is required.");
67
71
  return parts.map((part) => {
68
- if (!isRecord(part) || field(part, "type", true) !== "text")
69
- fail("Only text context is supported by this provider.");
70
- const text = field(part, "text", true);
71
- if (typeof text !== "string" || !text)
72
+ if (!isRecord(part))
72
73
  fail("Only text context is supported by this provider.");
73
- return { text };
74
+ const type = field(part, "type", true);
75
+ if (type === "text") {
76
+ const text = field(part, "text", true);
77
+ if (typeof text !== "string")
78
+ fail("Only text context is supported by this provider.");
79
+ const thoughtSignature = sameProviderAndModel ? validThoughtSignature(field(part, "textSignature")) : undefined;
80
+ if (!text && !thoughtSignature)
81
+ fail("Only text context is supported by this provider.");
82
+ return { text, ...(thoughtSignature ? { thoughtSignature } : {}) };
83
+ }
84
+ if (type === "thinking" && assistant && sameProviderAndModel) {
85
+ const text = field(part, "thinking", true);
86
+ if (typeof text !== "string")
87
+ fail("Only text context is supported by this provider.");
88
+ const thoughtSignature = validThoughtSignature(field(part, "thinkingSignature"));
89
+ if (!text && !thoughtSignature)
90
+ fail("Only text context is supported by this provider.");
91
+ return { thought: true, text, ...(thoughtSignature ? { thoughtSignature } : {}) };
92
+ }
93
+ if (type === "thinking" && assistant) {
94
+ const text = field(part, "thinking", true);
95
+ if (typeof text !== "string" || !text)
96
+ fail("Only text context is supported by this provider.");
97
+ return { text };
98
+ }
99
+ fail("Only text context is supported by this provider.");
74
100
  });
75
101
  }
102
+ function isSameProviderAndModel(message, publicId) {
103
+ return field(message, "provider") === PROVIDER && field(message, "model") === publicId;
104
+ }
105
+ function serializeThinkingConfig(thinking) {
106
+ if (thinking.kind === "native-level")
107
+ return { thinkingLevel: thinking.thinkingLevel, includeThoughts: thinking.includeThoughts };
108
+ if (thinking.kind === "budget")
109
+ return { thinkingBudget: thinking.budget, includeThoughts: thinking.includeThoughts };
110
+ return undefined;
111
+ }
112
+ function resolveOutputTokens(maxTokens, maxAllowed, thinking) {
113
+ if (typeof maxTokens === "number") {
114
+ if (maxTokens > maxAllowed)
115
+ fail(`maxTokens must be a positive integer no greater than ${maxAllowed}.`);
116
+ if (thinking.kind === "budget" && thinking.budget > 0 && maxTokens <= thinking.budget)
117
+ fail("maxTokens must exceed the selected thinking budget.");
118
+ return maxTokens;
119
+ }
120
+ return thinking.kind === "budget" && thinking.budget > 0 ? Math.max(4096, thinking.budget + 1024) : 4096;
121
+ }
122
+ function validThoughtSignature(value) {
123
+ if (typeof value !== "string" || !value || value.length % 4 !== 0)
124
+ return undefined;
125
+ return /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined;
126
+ }
76
127
  function validateOptions(options) {
77
128
  if (options === undefined)
78
129
  return;
79
130
  if (!isRecord(options))
80
131
  fail("Invalid generation options.");
81
132
  const reasoning = option(options, "reasoning"), budgets = option(options, "thinkingBudgets"), deferred = option(options, "deferred"), toolChoice = option(options, "toolChoice"), sampling = option(options, "samplingParams"), temperature = option(options, "temperature"), maxTokens = option(options, "maxTokens"), headers = option(options, "headers");
82
- if (reasoning !== undefined || budgets !== undefined || deferred)
83
- fail("Reasoning and deferred requests are not supported.");
133
+ if (reasoning !== undefined && reasoning !== "off" && !isThinkingLevel(reasoning) && reasoning !== "minimal")
134
+ fail("Unsupported reasoning level.");
135
+ if (budgets !== undefined)
136
+ fail("Thinking budgets are not supported.");
137
+ if (deferred)
138
+ fail("Deferred requests are not supported.");
84
139
  if (toolChoice !== undefined && toolChoice !== "none")
85
140
  fail("Tools are not supported by this text-only provider.");
86
141
  if (sampling !== undefined)
87
142
  fail("Custom generation options are not supported.");
88
143
  if (temperature !== undefined && (typeof temperature !== "number" || !Number.isFinite(temperature) || temperature < 0 || temperature > 2))
89
144
  fail("Temperature must be finite and between 0 and 2.");
90
- if (maxTokens !== undefined && (typeof maxTokens !== "number" || !Number.isInteger(maxTokens) || maxTokens <= 0 || maxTokens > MAX_OUTPUT_TOKENS))
145
+ if (maxTokens !== undefined && (typeof maxTokens !== "number" || !Number.isInteger(maxTokens) || maxTokens <= 0 || maxTokens > 65_536))
91
146
  fail("maxTokens must be a positive integer no greater than 65536.");
92
147
  if (headers !== undefined && !isRecord(headers))
93
148
  fail("Invalid generation options.");
@@ -95,6 +150,9 @@ function validateOptions(options) {
95
150
  if (RESERVED_HEADERS.has(name.toLowerCase()))
96
151
  fail("Custom headers cannot replace protected request headers.");
97
152
  }
153
+ function isThinkingLevel(value) {
154
+ return typeof value === "string" && THINKING_LEVELS.includes(value);
155
+ }
98
156
  function byteLength(value) {
99
157
  return new TextEncoder().encode(value).byteLength;
100
158
  }
package/dist/provider.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { ANTIGRAVITY_ENDPOINTS } from "@benjamolina/antigravity-guard-core";
2
+ import { listCatalogEntries, toPiModelDescriptor } from "./catalog.js";
2
3
  import { createPiOAuthLifecycle } from "./oauth.js";
3
4
  import { createPiLifecycleStream, executeStreamTransport } from "./stream.js";
4
5
  const PROVIDER = "antigravity-guard";
5
6
  const API = "antigravity-guard-sse";
6
- const MODEL = "antigravity-gemini-3.8-flash";
7
7
  const INVALID_CREDENTIALS = "Antigravity credentials are invalid. Run /login antigravity-guard.";
8
8
  export function registerAntigravityProvider(pi) {
9
9
  const oauth = createPiOAuthLifecycle({ fetch: globalThis.fetch, now: Date.now });
@@ -11,15 +11,7 @@ export function registerAntigravityProvider(pi) {
11
11
  name: "Antigravity Guard",
12
12
  baseUrl: ANTIGRAVITY_ENDPOINTS.daily,
13
13
  api: API,
14
- models: [{
15
- id: MODEL,
16
- name: "Gemini 3.8 Flash (Antigravity, text only)",
17
- reasoning: false,
18
- input: ["text"],
19
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
20
- contextWindow: 1_048_576,
21
- maxTokens: 65_536,
22
- }],
14
+ models: listCatalogEntries().map(toPiModelDescriptor),
23
15
  oauth: {
24
16
  name: "Antigravity Guard",
25
17
  isSubscription: true,
@@ -1,7 +1,13 @@
1
- export type ResponseSemantic = TextSemantic | FinishSemantic | UsageSemantic;
1
+ export type ResponseSemantic = TextSemantic | ThinkingSemantic | FinishSemantic | UsageSemantic;
2
2
  export interface TextSemantic {
3
3
  type: "text";
4
4
  text: string;
5
+ signature?: string;
6
+ }
7
+ export interface ThinkingSemantic {
8
+ type: "thinking";
9
+ thinking: string;
10
+ signature?: string;
5
11
  }
6
12
  export interface FinishSemantic {
7
13
  type: "finish";
@@ -13,18 +19,19 @@ export interface UsageSemantic {
13
19
  output: number;
14
20
  cacheRead: number;
15
21
  cacheWrite: 0;
22
+ reasoning: number;
16
23
  total: number;
17
24
  }
18
25
  export declare class ResponseSemanticError extends Error {
19
26
  }
20
27
  export declare class ResponseSemantics {
21
28
  private finished;
22
- private text;
29
+ private content;
23
30
  private done;
24
31
  private usage;
25
32
  push(record: string): ResponseSemantic[];
26
33
  finish(): void;
27
- addText(text: string): void;
34
+ addContent(text: string): void;
28
35
  addFinish(): void;
29
36
  private updateUsage;
30
37
  }
package/dist/response.js CHANGED
@@ -2,9 +2,9 @@ export class ResponseSemanticError extends Error {
2
2
  }
3
3
  export class ResponseSemantics {
4
4
  finished = false;
5
- text = false;
5
+ content = false;
6
6
  done = false;
7
- usage = { prompt: 0, cacheRead: 0, output: 0 };
7
+ usage = { prompt: 0, cacheRead: 0, output: 0, reasoning: 0 };
8
8
  push(record) {
9
9
  if (this.done)
10
10
  throw new ResponseSemanticError("Response data arrived after [DONE].");
@@ -35,20 +35,22 @@ export class ResponseSemantics {
35
35
  finish() {
36
36
  if (!this.finished)
37
37
  throw new ResponseSemanticError("Response ended without a finish reason.");
38
- if (!this.text)
39
- throw new ResponseSemanticError("Response ended without text.");
38
+ if (!this.content)
39
+ throw new ResponseSemanticError("Response ended without content.");
40
40
  }
41
- addText(text) { this.text = this.text || text.length > 0; }
41
+ addContent(text) { this.content = this.content || text.length > 0; }
42
42
  addFinish() { this.finished = true; }
43
43
  updateUsage(value) {
44
44
  const prompt = count(value, "promptTokenCount", this.usage.prompt);
45
45
  const cacheRead = count(value, "cachedContentTokenCount", this.usage.cacheRead);
46
- const output = count(value, "candidatesTokenCount", this.usage.output) + count(value, "thoughtsTokenCount", 0);
46
+ const candidates = count(value, "candidatesTokenCount", this.usage.output - this.usage.reasoning);
47
+ const reasoning = count(value, "thoughtsTokenCount", this.usage.reasoning);
48
+ const output = candidates + reasoning;
47
49
  const reported = field(value, "totalTokenCount");
48
50
  if (cacheRead > prompt || (reported !== undefined && reported !== prompt + output))
49
51
  throw new ResponseSemanticError("Antigravity returned invalid usage.");
50
- this.usage = { prompt, cacheRead, output };
51
- return { type: "usage", input: prompt - cacheRead, output, cacheRead, cacheWrite: 0, total: prompt + output };
52
+ this.usage = { prompt, cacheRead, output, reasoning };
53
+ return { type: "usage", input: prompt - cacheRead, output, cacheRead, cacheWrite: 0, reasoning, total: prompt + output };
52
54
  }
53
55
  }
54
56
  function candidate(value, events, semantics) {
@@ -67,10 +69,15 @@ function candidate(value, events, semantics) {
67
69
  throw new ResponseSemanticError("Antigravity returned invalid content.");
68
70
  for (const part of parts) {
69
71
  const text = field(part, "text");
70
- if (typeof text !== "string" || Object.keys(part).some((name) => name !== "text" && name !== "thoughtSignature"))
72
+ const thought = field(part, "thought");
73
+ if (typeof text !== "string" || (thought !== undefined && typeof thought !== "boolean") || Object.keys(part).some((name) => name !== "text" && name !== "thought" && name !== "thoughtSignature"))
71
74
  throw new ResponseSemanticError("Antigravity returned unsupported content.");
72
- semantics.addText(text);
73
- events.push({ type: "text", text });
75
+ const signature = validThoughtSignature(field(part, "thoughtSignature"));
76
+ semantics.addContent(text);
77
+ if (thought === true)
78
+ events.push({ type: "thinking", thinking: text, ...(signature ? { signature } : {}) });
79
+ else
80
+ events.push({ type: "text", text, ...(signature ? { signature } : {}) });
74
81
  }
75
82
  }
76
83
  const finishReason = field(candidate, "finishReason");
@@ -81,6 +88,11 @@ function candidate(value, events, semantics) {
81
88
  events.push({ type: "finish", reason: finishReason === "STOP" ? "stop" : "length" });
82
89
  }
83
90
  }
91
+ function validThoughtSignature(value) {
92
+ if (typeof value !== "string" || !value || value.length % 4 !== 0)
93
+ return undefined;
94
+ return /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined;
95
+ }
84
96
  function validateMetadata(value) {
85
97
  for (const name of ["responseId", "modelVersion"]) {
86
98
  const metadata = field(value, name);
package/dist/stream.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { calculateCost, createAssistantMessageEventStream } from "@earendil-works/pi-ai";
2
2
  import { ANTIGRAVITY_ENDPOINTS } from "@benjamolina/antigravity-guard-core";
3
+ import { getCatalogEntry } from "./catalog.js";
3
4
  import { serializeTextContext } from "./context.js";
4
5
  import { ResponseSemanticError, ResponseSemantics } from "./response.js";
5
6
  import { SseFrameError, SseFramer } from "./sse.js";
@@ -9,7 +10,6 @@ const MAX_ERROR_BYTES = 64 * 1024;
9
10
  const ANTIGRAVITY_USER_AGENT = "antigravity/cli/1.1.23 (aidev_client; os_type=linux; arch=amd64; cl=974125021; auth_method=consumer)";
10
11
  const ENDPOINT = `${ANTIGRAVITY_ENDPOINTS.daily}/v1internal:streamGenerateContent?alt=sse`;
11
12
  const API = "antigravity-guard-sse";
12
- const MODEL = "antigravity-gemini-3.8-flash";
13
13
  const PROTECTED_HEADERS = new Set(["authorization", "host", "content-type", "content-length"]);
14
14
  const LOCAL_ERRORS = new WeakSet();
15
15
  export class StreamTransportError extends Error {
@@ -37,6 +37,17 @@ export function createPiLifecycleStream(input) {
37
37
  };
38
38
  let complete = false;
39
39
  let textStarted = false;
40
+ let currentBlock;
41
+ const closeBlock = () => {
42
+ if (!currentBlock)
43
+ return;
44
+ const contentIndex = output.content.length - 1;
45
+ if (currentBlock.type === "text")
46
+ stream.push({ type: "text_end", contentIndex, content: currentBlock.text, partial: output });
47
+ else
48
+ stream.push({ type: "thinking_end", contentIndex, content: currentBlock.thinking, partial: output });
49
+ currentBlock = undefined;
50
+ };
40
51
  let removeAbort = () => { };
41
52
  const finalize = (reason, errorMessage) => {
42
53
  if (complete)
@@ -47,8 +58,7 @@ export function createPiLifecycleStream(input) {
47
58
  controller.abort();
48
59
  output.stopReason = reason;
49
60
  if (reason === "stop" || reason === "length") {
50
- if (textStarted)
51
- stream.push({ type: "text_end", contentIndex: 0, content: output.content[0]?.type === "text" ? output.content[0].text : "", partial: output });
61
+ closeBlock();
52
62
  stream.push({ type: "done", reason, message: output });
53
63
  }
54
64
  else {
@@ -60,6 +70,35 @@ export function createPiLifecycleStream(input) {
60
70
  const onSemantic = (semantic) => {
61
71
  if (complete)
62
72
  return;
73
+ if (isContentSemantic(semantic)) {
74
+ if (!currentBlock || currentBlock.type !== semantic.type) {
75
+ closeBlock();
76
+ if (semantic.type === "text") {
77
+ currentBlock = { type: "text", text: "" };
78
+ output.content.push(currentBlock);
79
+ stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
80
+ }
81
+ else {
82
+ currentBlock = { type: "thinking", thinking: "" };
83
+ output.content.push(currentBlock);
84
+ stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
85
+ }
86
+ }
87
+ const contentIndex = output.content.length - 1;
88
+ if (semantic.type === "text" && currentBlock.type === "text") {
89
+ currentBlock.text += semantic.text;
90
+ if (semantic.signature)
91
+ currentBlock.textSignature = semantic.signature;
92
+ stream.push({ type: "text_delta", contentIndex, delta: semantic.text, partial: output });
93
+ }
94
+ else if (semantic.type === "thinking" && currentBlock.type === "thinking") {
95
+ currentBlock.thinking += semantic.thinking;
96
+ if (semantic.signature)
97
+ currentBlock.thinkingSignature = semantic.signature;
98
+ stream.push({ type: "thinking_delta", contentIndex, delta: semantic.thinking, partial: output });
99
+ }
100
+ return;
101
+ }
63
102
  if (semantic.type === "text" && semantic.text) {
64
103
  if (!textStarted) {
65
104
  textStarted = true;
@@ -76,6 +115,7 @@ export function createPiLifecycleStream(input) {
76
115
  output.usage.output = semantic.output;
77
116
  output.usage.cacheRead = semantic.cacheRead;
78
117
  output.usage.cacheWrite = semantic.cacheWrite;
118
+ output.usage.reasoning = semantic.reasoning;
79
119
  output.usage.totalTokens = semantic.input + semantic.output + semantic.cacheRead + semantic.cacheWrite;
80
120
  output.usage.cost = calculateCost(input.model, output.usage);
81
121
  }
@@ -95,6 +135,9 @@ export function createPiLifecycleStream(input) {
95
135
  finalize(signal.aborted ? "aborted" : "error"); }, (error) => finalize(signal.aborted || input.signal?.aborted ? "aborted" : "error", isLocalStreamError(error) ? error.message : undefined));
96
136
  return stream;
97
137
  }
138
+ function isContentSemantic(semantic) {
139
+ return semantic.type === "text" || semantic.type === "thinking";
140
+ }
98
141
  export async function executeStreamTransport(input) {
99
142
  validateInput(input);
100
143
  const signal = totalSignal(input);
@@ -126,7 +169,7 @@ export async function executeStreamTransport(input) {
126
169
  }
127
170
  }
128
171
  function validateInput(input) {
129
- if (input.model.id !== MODEL || input.model.api !== API)
172
+ if (!getCatalogEntry(input.model.id) || input.model.api !== API)
130
173
  throw streamError("response", "The selected Antigravity model or API is unsupported.");
131
174
  }
132
175
  async function payloadHook(input, payload, signal) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjamolina/pi-antigravity-guard",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Pi adapter for Antigravity Guard",
5
5
  "type": "module",
6
6
  "license": "MIT",