@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 +24 -5
- package/dist/catalog.d.ts +390 -0
- package/dist/catalog.js +166 -0
- package/dist/context.d.ts +9 -5
- package/dist/context.js +81 -23
- package/dist/provider.js +2 -10
- package/dist/response.d.ts +10 -3
- package/dist/response.js +23 -11
- package/dist/stream.js +47 -4
- package/package.json +1 -1
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
22
|
-
- It has no account rotation, quota fallback, model substitution, or
|
|
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 {};
|
package/dist/catalog.js
ADDED
|
@@ -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:
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
39
|
-
|
|
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
|
|
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:
|
|
50
|
+
return { project, model: route.wireModel, request: { contents, ...(systemInstruction ? { systemInstruction } : {}), generationConfig: {
|
|
50
51
|
temperature: typeof temperature === "number" ? temperature : 1,
|
|
51
|
-
maxOutputTokens:
|
|
52
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
|
83
|
-
fail("
|
|
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 >
|
|
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,
|
package/dist/response.d.ts
CHANGED
|
@@ -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
|
|
29
|
+
private content;
|
|
23
30
|
private done;
|
|
24
31
|
private usage;
|
|
25
32
|
push(record: string): ResponseSemantic[];
|
|
26
33
|
finish(): void;
|
|
27
|
-
|
|
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
|
-
|
|
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.
|
|
39
|
-
throw new ResponseSemanticError("Response ended without
|
|
38
|
+
if (!this.content)
|
|
39
|
+
throw new ResponseSemanticError("Response ended without content.");
|
|
40
40
|
}
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
|
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) {
|