@benjamolina/pi-antigravity-guard 0.2.0 → 0.4.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 +38 -7
- package/dist/catalog.d.ts +446 -0
- package/dist/catalog.js +188 -0
- package/dist/context.d.ts +19 -10
- package/dist/context.js +114 -47
- package/dist/provider.js +2 -11
- package/dist/response.d.ts +31 -6
- package/dist/response.js +98 -49
- package/dist/stream.d.ts +21 -4
- package/dist/stream.js +84 -15
- package/dist/tool-context.d.ts +14 -0
- package/dist/tool-context.js +162 -0
- package/dist/tool-contract.d.ts +17 -0
- package/dist/tool-contract.js +65 -0
- package/dist/tool-schema.d.ts +7 -0
- package/dist/tool-schema.js +125 -0
- package/package.json +1 -1
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
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 }, tools: disabled("missing-direct-evidence") },
|
|
12
|
+
low: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "low", includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
13
|
+
medium: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "medium", includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
14
|
+
high: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "high", includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
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 }, tools: disabled("missing-direct-evidence") },
|
|
28
|
+
low: { wireModel: "gemini-3.7-flash-low", thinking: { kind: "budget", budget: 1000, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
29
|
+
medium: { wireModel: "gemini-3.7-flash-medium", thinking: { kind: "budget", budget: 4000, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
30
|
+
high: { wireModel: "gemini-3.7-flash-high", thinking: { kind: "budget", budget: -1, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
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" }, tools: disabled("missing-direct-evidence") },
|
|
44
|
+
low: { wireModel: "gemini-3.6-flash-low", thinking: { kind: "budget", budget: 1000, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
45
|
+
medium: { wireModel: "gemini-3.6-flash-medium", thinking: { kind: "budget", budget: 4000, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
46
|
+
high: { wireModel: "gemini-3.6-flash-high", thinking: { kind: "budget", budget: -1, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
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" }, tools: disabled("missing-direct-evidence") },
|
|
60
|
+
low: { wireModel: "gemini-3.1-pro-low", thinking: { kind: "budget", budget: 1001, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
61
|
+
high: { wireModel: "gemini-pro-agent", thinking: { kind: "budget", budget: 10001, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
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 }, tools: disabled("claude-continuity-unproven") },
|
|
75
|
+
high: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: disabled("claude-continuity-unproven") },
|
|
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 }, tools: disabled("claude-continuity-unproven") },
|
|
89
|
+
high: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: disabled("claude-continuity-unproven") },
|
|
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" }, tools: disabled("missing-direct-evidence") },
|
|
103
|
+
medium: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "budget", budget: 8192, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
|
|
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 disabled(reason) {
|
|
110
|
+
return { state: "disabled", contractRevision: 1, reason };
|
|
111
|
+
}
|
|
112
|
+
/** Constructs deterministic capability data for hermetic serializer tests only. */
|
|
113
|
+
export function createEnabledToolCapability(evidence) {
|
|
114
|
+
return freeze({ state: "enabled", contractRevision: 1, fixtureEvidence: evidence, directEvidence: evidence });
|
|
115
|
+
}
|
|
116
|
+
function freeze(value) {
|
|
117
|
+
if (typeof value === "object" && value !== null) {
|
|
118
|
+
Object.freeze(value);
|
|
119
|
+
for (const child of Object.values(value))
|
|
120
|
+
freeze(child);
|
|
121
|
+
}
|
|
122
|
+
return value;
|
|
123
|
+
}
|
|
124
|
+
export function defineCatalog(entries) {
|
|
125
|
+
const identities = new Set();
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (identities.has(entry.publicId))
|
|
128
|
+
throw new Error(`Duplicate public model ID: ${entry.publicId}`);
|
|
129
|
+
identities.add(entry.publicId);
|
|
130
|
+
if (!Object.keys(entry.routes).length)
|
|
131
|
+
throw new Error(`Model has no routes: ${entry.publicId}`);
|
|
132
|
+
if (!entry.descriptor)
|
|
133
|
+
continue;
|
|
134
|
+
for (const [level, route] of Object.entries(entry.routes)) {
|
|
135
|
+
if (!route.thinking)
|
|
136
|
+
throw new Error(`Route has no thinking policy: ${entry.publicId}/${level}`);
|
|
137
|
+
if (level !== "off" && !Object.hasOwn(entry.descriptor.thinkingLevelMap, level)) {
|
|
138
|
+
throw new Error(`Route is outside descriptor map: ${entry.publicId}/${level}`);
|
|
139
|
+
}
|
|
140
|
+
if (level !== "off" && entry.descriptor.thinkingLevelMap[level] === null) {
|
|
141
|
+
throw new Error(`Route is hidden by descriptor: ${entry.publicId}/${level}`);
|
|
142
|
+
}
|
|
143
|
+
if (route.thinking.kind === "budget" && route.thinking.budget > 0 && Number.isFinite(route.thinking.budget) && route.thinking.budget + ANSWER_RESERVE > entry.descriptor.maxTokens) {
|
|
144
|
+
throw new Error(`Finite thinking budget does not leave answer reserve: ${entry.publicId}/${level}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return entries;
|
|
149
|
+
}
|
|
150
|
+
export function listCatalogEntries() {
|
|
151
|
+
return CATALOG;
|
|
152
|
+
}
|
|
153
|
+
export function getCatalogEntry(publicId) {
|
|
154
|
+
return typeof publicId === "string" ? lookup.get(publicId) : undefined;
|
|
155
|
+
}
|
|
156
|
+
export function resolveGenerationRoute(entry, reasoning) {
|
|
157
|
+
return resolveGenerationSelection(entry, reasoning).route;
|
|
158
|
+
}
|
|
159
|
+
export function resolveGenerationSelection(entry, reasoning) {
|
|
160
|
+
const level = reasoning === undefined || reasoning === "off" ? "off" : reasoning;
|
|
161
|
+
if (typeof level !== "string" || !entry.routes[level])
|
|
162
|
+
throw new Error("Unsupported reasoning level");
|
|
163
|
+
const typedLevel = level;
|
|
164
|
+
const configured = entry.routes[typedLevel];
|
|
165
|
+
const { tools, ...route } = configured;
|
|
166
|
+
return { level: typedLevel, route, tools: resolveToolCapability(tools, entry.publicId, typedLevel, route.wireModel) };
|
|
167
|
+
}
|
|
168
|
+
export function resolveToolCapability(capability, publicModelId, reasoning, wireModel) {
|
|
169
|
+
if (capability.state === "disabled")
|
|
170
|
+
return capability;
|
|
171
|
+
const evidence = capability.state === "enabled" ? [capability.fixtureEvidence, capability.directEvidence] : [capability.fixtureEvidence];
|
|
172
|
+
if (capability.contractRevision !== 1 || evidence.some((item) => item.publicModelId !== publicModelId || item.reasoning !== reasoning || item.wireModel !== wireModel)) {
|
|
173
|
+
return freeze(disabled("stale-or-conflicting-evidence"));
|
|
174
|
+
}
|
|
175
|
+
return capability;
|
|
176
|
+
}
|
|
177
|
+
export function toPiModelDescriptor(entry) {
|
|
178
|
+
return {
|
|
179
|
+
id: entry.publicId,
|
|
180
|
+
name: entry.descriptor.name,
|
|
181
|
+
reasoning: true,
|
|
182
|
+
thinkingLevelMap: { ...entry.descriptor.thinkingLevelMap },
|
|
183
|
+
input: ["text"],
|
|
184
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
185
|
+
contextWindow: entry.descriptor.contextWindow,
|
|
186
|
+
maxTokens: entry.descriptor.maxTokens,
|
|
187
|
+
};
|
|
188
|
+
}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai";
|
|
2
|
-
|
|
3
|
-
declare const THINKING_LEVELS: readonly ["low", "medium", "high"];
|
|
4
|
-
type ThinkingLevel = typeof THINKING_LEVELS[number];
|
|
2
|
+
import { type GenerationSelection } from "./catalog.ts";
|
|
5
3
|
interface Part {
|
|
6
4
|
text: string;
|
|
7
5
|
thought?: true;
|
|
@@ -9,24 +7,34 @@ interface Part {
|
|
|
9
7
|
}
|
|
10
8
|
interface Content {
|
|
11
9
|
role: "user" | "model";
|
|
12
|
-
parts:
|
|
13
|
-
}
|
|
14
|
-
interface ThinkingConfig {
|
|
15
|
-
thinkingLevel: ThinkingLevel;
|
|
16
|
-
includeThoughts: boolean;
|
|
10
|
+
parts: object[];
|
|
17
11
|
}
|
|
18
12
|
export interface GenerationRequest {
|
|
19
13
|
project: string;
|
|
20
|
-
model:
|
|
14
|
+
model: string;
|
|
21
15
|
request: {
|
|
22
16
|
contents: Content[];
|
|
23
17
|
systemInstruction?: {
|
|
24
18
|
parts: Part[];
|
|
25
19
|
};
|
|
20
|
+
tools?: {
|
|
21
|
+
functionDeclarations: unknown;
|
|
22
|
+
}[];
|
|
23
|
+
toolConfig?: {
|
|
24
|
+
functionCallingConfig: {
|
|
25
|
+
mode: "AUTO" | "NONE";
|
|
26
|
+
};
|
|
27
|
+
};
|
|
26
28
|
generationConfig: {
|
|
27
29
|
temperature: number;
|
|
28
30
|
maxOutputTokens: number;
|
|
29
|
-
thinkingConfig
|
|
31
|
+
thinkingConfig?: {
|
|
32
|
+
thinkingLevel: "low" | "medium" | "high";
|
|
33
|
+
includeThoughts: boolean;
|
|
34
|
+
} | {
|
|
35
|
+
thinkingBudget: number;
|
|
36
|
+
includeThoughts: boolean;
|
|
37
|
+
};
|
|
30
38
|
};
|
|
31
39
|
};
|
|
32
40
|
requestType: "agent";
|
|
@@ -42,5 +50,6 @@ export interface SerializeTextContextInput {
|
|
|
42
50
|
}
|
|
43
51
|
export declare class ContextSerializationError extends Error {
|
|
44
52
|
}
|
|
53
|
+
export declare function serializeContext(input: SerializeTextContextInput, injectedSelection?: GenerationSelection, onRecovery?: (recoveryCount: number) => void): GenerationRequest;
|
|
45
54
|
export declare function serializeTextContext(input: SerializeTextContextInput): GenerationRequest;
|
|
46
55
|
export {};
|
package/dist/context.js
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
|
+
import { getCatalogEntry, resolveGenerationSelection } from "./catalog.js";
|
|
2
|
+
import { hasToolContext, prepareToolContext, replayToolHistory } from "./tool-context.js";
|
|
1
3
|
const MAX_TEXT_BYTES = 8 * 1024 * 1024;
|
|
2
|
-
const MAX_OUTPUT_TOKENS = 65_536;
|
|
3
4
|
const PROVIDER = "antigravity-guard";
|
|
4
|
-
const PUBLIC_MODEL = "antigravity-gemini-3.8-flash";
|
|
5
|
-
const WIRE_MODEL = "gemini-3.8-flash-tiered";
|
|
6
5
|
const RESERVED_HEADERS = new Set(["authorization", "host", "content-type", "content-length"]);
|
|
7
6
|
const THINKING_LEVELS = ["low", "medium", "high"];
|
|
8
7
|
export class ContextSerializationError extends Error {
|
|
9
8
|
}
|
|
9
|
+
export function serializeContext(input, injectedSelection, onRecovery) {
|
|
10
|
+
const context = input.context;
|
|
11
|
+
if (!hasToolContext(context))
|
|
12
|
+
return serializeTextContext(input);
|
|
13
|
+
const options = input.options;
|
|
14
|
+
const entry = isRecord(input.model) ? getCatalogEntry(field(input.model, "id", true)) : undefined;
|
|
15
|
+
if (!entry || !isRecord(context))
|
|
16
|
+
return serializeTextContext(input);
|
|
17
|
+
const selected = resolveGenerationSelection(entry, isRecord(options) ? option(options, "reasoning") : undefined);
|
|
18
|
+
const selection = injectedSelection ?? selected;
|
|
19
|
+
if (selection.level !== selected.level || selection.route.wireModel !== selected.route.wireModel)
|
|
20
|
+
fail("Invalid tool capability selection.");
|
|
21
|
+
if (selection.tools.state !== "enabled")
|
|
22
|
+
fail(capabilityError(entry.publicId, selection.level, selection.tools));
|
|
23
|
+
const prepared = prepareToolContext(field(context, "tools") ?? [], isRecord(options) ? field(options, "toolChoice") : undefined);
|
|
24
|
+
const { tools: _tools, messages, ...textContext } = context;
|
|
25
|
+
const { toolChoice: _choice, ...textOptions } = isRecord(options) ? options : {};
|
|
26
|
+
const text = serializeTextContext({ ...input, context: { ...textContext, messages: [{ role: "user", content: "placeholder", timestamp: 0 }] }, options: textOptions });
|
|
27
|
+
const contents = replayToolHistory(isDenseArray(messages), (part, message) => messagePart(part, field(message, "role") === "assistant", field(message, "role") === "assistant" && entry.replay.kind === "same-public-model" && isSameProviderAndModel(message, entry.publicId)), onRecovery);
|
|
28
|
+
const { systemInstruction, generationConfig } = text.request;
|
|
29
|
+
return { ...text, request: {
|
|
30
|
+
contents,
|
|
31
|
+
...(systemInstruction ? { systemInstruction } : {}),
|
|
32
|
+
...(prepared ? { tools: [{ functionDeclarations: prepared.declarations }], toolConfig: { functionCallingConfig: { mode: prepared.mode } } } : {}),
|
|
33
|
+
generationConfig,
|
|
34
|
+
} };
|
|
35
|
+
}
|
|
10
36
|
export function serializeTextContext(input) {
|
|
11
37
|
try {
|
|
12
38
|
if (!isRecord(input))
|
|
@@ -16,8 +42,13 @@ export function serializeTextContext(input) {
|
|
|
16
42
|
const options = field(input, "options");
|
|
17
43
|
const project = field(input, "project", true);
|
|
18
44
|
const requestId = field(input, "requestId", true);
|
|
19
|
-
|
|
45
|
+
const entry = isRecord(model) ? getCatalogEntry(field(model, "id", true)) : undefined;
|
|
46
|
+
if (!isRecord(context) || !entry || typeof project !== "string" || typeof requestId !== "string")
|
|
20
47
|
fail("Invalid text context.");
|
|
48
|
+
const selection = resolveGenerationSelection(entry, option(options, "reasoning"));
|
|
49
|
+
const route = selection.route;
|
|
50
|
+
if (isToolBearingContext(context) && selection.tools.state !== "enabled")
|
|
51
|
+
fail(capabilityError(entry.publicId, selection.level, selection.tools));
|
|
21
52
|
const systemPrompt = field(context, "systemPrompt");
|
|
22
53
|
const tools = field(context, "tools");
|
|
23
54
|
if (systemPrompt !== undefined && typeof systemPrompt !== "string")
|
|
@@ -36,7 +67,7 @@ export function serializeTextContext(input) {
|
|
|
36
67
|
if (role !== "user" && role !== "assistant")
|
|
37
68
|
fail("Unsupported context role for this text-only provider.");
|
|
38
69
|
const assistant = role === "assistant";
|
|
39
|
-
const parts = messageParts(field(message, "content", true), assistant, assistant && isSameProviderAndModel(message));
|
|
70
|
+
const parts = messageParts(field(message, "content", true), assistant, assistant && entry.replay.kind === "same-public-model" && isSameProviderAndModel(message, entry.publicId));
|
|
40
71
|
textBytes += parts.reduce((total, part) => total + byteLength(part.text), 0);
|
|
41
72
|
if (textBytes > MAX_TEXT_BYTES)
|
|
42
73
|
fail("Text context is too large.");
|
|
@@ -47,10 +78,13 @@ export function serializeTextContext(input) {
|
|
|
47
78
|
const systemInstruction = systemPrompt === undefined ? undefined : { parts: [{ text: systemPrompt }] };
|
|
48
79
|
const temperature = option(options, "temperature");
|
|
49
80
|
const maxTokens = option(options, "maxTokens");
|
|
50
|
-
return { project, model:
|
|
81
|
+
return { project, model: route.wireModel, request: { contents, ...(systemInstruction ? { systemInstruction } : {}), generationConfig: {
|
|
51
82
|
temperature: typeof temperature === "number" ? temperature : 1,
|
|
52
|
-
maxOutputTokens:
|
|
53
|
-
|
|
83
|
+
maxOutputTokens: resolveOutputTokens(maxTokens, entry.descriptor.maxTokens, route.thinking),
|
|
84
|
+
...(() => {
|
|
85
|
+
const thinkingConfig = serializeThinkingConfig(route.thinking);
|
|
86
|
+
return thinkingConfig ? { thinkingConfig } : {};
|
|
87
|
+
})(),
|
|
54
88
|
} }, requestType: "agent", userAgent: "antigravity", requestId };
|
|
55
89
|
}
|
|
56
90
|
catch (error) {
|
|
@@ -59,64 +93,97 @@ export function serializeTextContext(input) {
|
|
|
59
93
|
fail("Invalid text context.");
|
|
60
94
|
}
|
|
61
95
|
}
|
|
96
|
+
function capabilityError(publicId, reasoning, capability) {
|
|
97
|
+
const reason = capability.state === "disabled" ? capability.reason : capability.state;
|
|
98
|
+
return `PI_TOOL_CAPABILITY_NOT_ENABLED: ${publicId}/${reasoning} is ${capability.state} (${reason}).`;
|
|
99
|
+
}
|
|
100
|
+
function isToolBearingContext(context) {
|
|
101
|
+
const tools = field(context, "tools");
|
|
102
|
+
if (tools !== undefined && isDenseArray(tools).length > 0)
|
|
103
|
+
return true;
|
|
104
|
+
const messages = isDenseArray(field(context, "messages", true));
|
|
105
|
+
for (const message of messages) {
|
|
106
|
+
if (!isRecord(message))
|
|
107
|
+
continue;
|
|
108
|
+
if (field(message, "role") === "toolResult")
|
|
109
|
+
return true;
|
|
110
|
+
const content = field(message, "content");
|
|
111
|
+
if (!Array.isArray(content))
|
|
112
|
+
continue;
|
|
113
|
+
for (const part of content)
|
|
114
|
+
if (isRecord(part) && field(part, "type") === "toolCall")
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
62
119
|
function messageParts(content, assistant, sameProviderAndModel) {
|
|
63
120
|
if (typeof content === "string")
|
|
64
121
|
return content ? [{ text: content }] : fail("A text conversation is required.");
|
|
65
122
|
const parts = isDenseArray(content);
|
|
66
123
|
if (!parts.length)
|
|
67
124
|
fail("A text conversation is required.");
|
|
68
|
-
return parts.map((part) =>
|
|
69
|
-
|
|
125
|
+
return parts.map((part) => messagePart(isRecord(part) ? part : fail("Only text context is supported by this provider."), assistant, sameProviderAndModel));
|
|
126
|
+
}
|
|
127
|
+
function messagePart(part, assistant, sameProviderAndModel) {
|
|
128
|
+
const type = field(part, "type", true);
|
|
129
|
+
if (type === "text") {
|
|
130
|
+
const text = field(part, "text", true);
|
|
131
|
+
if (typeof text !== "string")
|
|
70
132
|
fail("Only text context is supported by this provider.");
|
|
71
|
-
const
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (typeof text !== "string" || !text)
|
|
93
|
-
fail("Only text context is supported by this provider.");
|
|
94
|
-
return { text };
|
|
95
|
-
}
|
|
96
|
-
fail("Only text context is supported by this provider.");
|
|
97
|
-
});
|
|
133
|
+
const thoughtSignature = sameProviderAndModel ? validThoughtSignature(field(part, "textSignature")) : undefined;
|
|
134
|
+
if (!text && !thoughtSignature)
|
|
135
|
+
fail("Only text context is supported by this provider.");
|
|
136
|
+
return { text, ...(thoughtSignature ? { thoughtSignature } : {}) };
|
|
137
|
+
}
|
|
138
|
+
if (type === "thinking" && assistant && sameProviderAndModel) {
|
|
139
|
+
const text = field(part, "thinking", true);
|
|
140
|
+
if (typeof text !== "string")
|
|
141
|
+
fail("Only text context is supported by this provider.");
|
|
142
|
+
const thoughtSignature = validThoughtSignature(field(part, "thinkingSignature"));
|
|
143
|
+
if (!text && !thoughtSignature)
|
|
144
|
+
fail("Only text context is supported by this provider.");
|
|
145
|
+
return { thought: true, text, ...(thoughtSignature ? { thoughtSignature } : {}) };
|
|
146
|
+
}
|
|
147
|
+
if (type === "thinking" && assistant) {
|
|
148
|
+
const text = field(part, "thinking", true);
|
|
149
|
+
if (typeof text !== "string" || !text)
|
|
150
|
+
fail("Only text context is supported by this provider.");
|
|
151
|
+
return { text };
|
|
152
|
+
}
|
|
153
|
+
fail("Only text context is supported by this provider.");
|
|
98
154
|
}
|
|
99
|
-
function isSameProviderAndModel(message) {
|
|
100
|
-
return field(message, "provider") === PROVIDER && field(message, "model") ===
|
|
155
|
+
function isSameProviderAndModel(message, publicId) {
|
|
156
|
+
return field(message, "provider") === PROVIDER && field(message, "model") === publicId;
|
|
157
|
+
}
|
|
158
|
+
function serializeThinkingConfig(thinking) {
|
|
159
|
+
if (thinking.kind === "native-level")
|
|
160
|
+
return { thinkingLevel: thinking.thinkingLevel, includeThoughts: thinking.includeThoughts };
|
|
161
|
+
if (thinking.kind === "budget")
|
|
162
|
+
return { thinkingBudget: thinking.budget, includeThoughts: thinking.includeThoughts };
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
function resolveOutputTokens(maxTokens, maxAllowed, thinking) {
|
|
166
|
+
if (typeof maxTokens === "number") {
|
|
167
|
+
if (maxTokens > maxAllowed)
|
|
168
|
+
fail(`maxTokens must be a positive integer no greater than ${maxAllowed}.`);
|
|
169
|
+
if (thinking.kind === "budget" && thinking.budget > 0 && maxTokens <= thinking.budget)
|
|
170
|
+
fail("maxTokens must exceed the selected thinking budget.");
|
|
171
|
+
return maxTokens;
|
|
172
|
+
}
|
|
173
|
+
return thinking.kind === "budget" && thinking.budget > 0 ? Math.max(4096, thinking.budget + 1024) : 4096;
|
|
101
174
|
}
|
|
102
175
|
function validThoughtSignature(value) {
|
|
103
176
|
if (typeof value !== "string" || !value || value.length % 4 !== 0)
|
|
104
177
|
return undefined;
|
|
105
178
|
return /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined;
|
|
106
179
|
}
|
|
107
|
-
function resolveThinkingConfig(options) {
|
|
108
|
-
const reasoning = option(options, "reasoning");
|
|
109
|
-
if (isThinkingLevel(reasoning))
|
|
110
|
-
return { thinkingLevel: reasoning, includeThoughts: true };
|
|
111
|
-
return { thinkingLevel: "low", includeThoughts: false };
|
|
112
|
-
}
|
|
113
180
|
function validateOptions(options) {
|
|
114
181
|
if (options === undefined)
|
|
115
182
|
return;
|
|
116
183
|
if (!isRecord(options))
|
|
117
184
|
fail("Invalid generation options.");
|
|
118
185
|
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");
|
|
119
|
-
if (reasoning !== undefined && reasoning !== "off" && !isThinkingLevel(reasoning))
|
|
186
|
+
if (reasoning !== undefined && reasoning !== "off" && !isThinkingLevel(reasoning) && reasoning !== "minimal")
|
|
120
187
|
fail("Unsupported reasoning level.");
|
|
121
188
|
if (budgets !== undefined)
|
|
122
189
|
fail("Thinking budgets are not supported.");
|
|
@@ -128,7 +195,7 @@ function validateOptions(options) {
|
|
|
128
195
|
fail("Custom generation options are not supported.");
|
|
129
196
|
if (temperature !== undefined && (typeof temperature !== "number" || !Number.isFinite(temperature) || temperature < 0 || temperature > 2))
|
|
130
197
|
fail("Temperature must be finite and between 0 and 2.");
|
|
131
|
-
if (maxTokens !== undefined && (typeof maxTokens !== "number" || !Number.isInteger(maxTokens) || maxTokens <= 0 || maxTokens >
|
|
198
|
+
if (maxTokens !== undefined && (typeof maxTokens !== "number" || !Number.isInteger(maxTokens) || maxTokens <= 0 || maxTokens > 65_536))
|
|
132
199
|
fail("maxTokens must be a positive integer no greater than 65536.");
|
|
133
200
|
if (headers !== undefined && !isRecord(headers))
|
|
134
201
|
fail("Invalid generation options.");
|
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,16 +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: true,
|
|
18
|
-
thinkingLevelMap: { minimal: null, low: "low", medium: "medium", high: "high" },
|
|
19
|
-
input: ["text"],
|
|
20
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
21
|
-
contextWindow: 1_048_576,
|
|
22
|
-
maxTokens: 65_536,
|
|
23
|
-
}],
|
|
14
|
+
models: listCatalogEntries().map(toPiModelDescriptor),
|
|
24
15
|
oauth: {
|
|
25
16
|
name: "Antigravity Guard",
|
|
26
17
|
isSubscription: true,
|
package/dist/response.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { JsonObject } from "./tool-contract.ts";
|
|
2
|
+
export type ResponseSemantic = TextSemantic | ThinkingSemantic | ToolCallSemantic | FinishSemantic | UsageSemantic;
|
|
2
3
|
export interface TextSemantic {
|
|
3
4
|
type: "text";
|
|
4
5
|
text: string;
|
|
@@ -9,9 +10,18 @@ export interface ThinkingSemantic {
|
|
|
9
10
|
thinking: string;
|
|
10
11
|
signature?: string;
|
|
11
12
|
}
|
|
13
|
+
export interface ToolCallSemantic {
|
|
14
|
+
type: "toolCall";
|
|
15
|
+
callIndex: number;
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments: JsonObject;
|
|
19
|
+
argumentsJson: string;
|
|
20
|
+
signature?: string;
|
|
21
|
+
}
|
|
12
22
|
export interface FinishSemantic {
|
|
13
23
|
type: "finish";
|
|
14
|
-
reason: "stop" | "length";
|
|
24
|
+
reason: "stop" | "length" | "toolUse";
|
|
15
25
|
}
|
|
16
26
|
export interface UsageSemantic {
|
|
17
27
|
type: "usage";
|
|
@@ -22,16 +32,31 @@ export interface UsageSemantic {
|
|
|
22
32
|
reasoning: number;
|
|
23
33
|
total: number;
|
|
24
34
|
}
|
|
35
|
+
export type ToolResponsePolicy = {
|
|
36
|
+
kind: "reject";
|
|
37
|
+
} | {
|
|
38
|
+
kind: "accept";
|
|
39
|
+
declaredNames: ReadonlySet<string>;
|
|
40
|
+
};
|
|
25
41
|
export declare class ResponseSemanticError extends Error {
|
|
26
42
|
}
|
|
27
43
|
export declare class ResponseSemantics {
|
|
44
|
+
readonly policy: ToolResponsePolicy;
|
|
28
45
|
private finished;
|
|
29
|
-
private content;
|
|
30
46
|
private done;
|
|
47
|
+
private content;
|
|
48
|
+
private calls;
|
|
49
|
+
private readonly ids;
|
|
50
|
+
private pendingFinish;
|
|
31
51
|
private usage;
|
|
52
|
+
constructor(policy?: ToolResponsePolicy);
|
|
32
53
|
push(record: string): ResponseSemantic[];
|
|
33
|
-
finish():
|
|
34
|
-
addContent(text: string): void;
|
|
35
|
-
addFinish(): void;
|
|
54
|
+
finish(): FinishSemantic;
|
|
36
55
|
private updateUsage;
|
|
56
|
+
addContent(text: string): void;
|
|
57
|
+
addCall(): void;
|
|
58
|
+
setFinish(reason: FinishSemantic["reason"]): void;
|
|
59
|
+
hasId(id: string): boolean;
|
|
60
|
+
addId(id: string): void;
|
|
61
|
+
callIndex(): number;
|
|
37
62
|
}
|