@benjamolina/pi-antigravity-guard 0.4.6 → 0.4.8

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
@@ -22,9 +22,9 @@ All seven rows below are registered for text. The exact `antigravity-gemini-3.8-
22
22
  | `antigravity-gemini-3.7-flash` | off, low, medium, high | Enabled (off, low, medium, high) | pi-json-tool-loop/gemini-3.7-flash-off-v1 |
23
23
  | `antigravity-gemini-3.6-flash` | off, low, medium, high | Enabled (off, low, medium, high) | pi-json-tool-loop/gemini-3.6-flash-off-v1 |
24
24
  | `antigravity-gemini-3.1-pro` | off, low, high | Enabled (off, low, high) | pi-json-tool-loop/gemini-3.1-pro-off-v1 |
25
- | `antigravity-claude-sonnet-4.6` | off, high | Disabled | claude-continuity-unproven |
26
- | `antigravity-claude-opus-4.6-thinking` | off, high | Disabled | claude-continuity-unproven |
27
- | `antigravity-gpt-oss-120b` | off, medium | Disabled | missing-direct-evidence |
25
+ | `antigravity-claude-sonnet-4.6` | off, high | Enabled (off, high) | pi-json-tool-loop/claude-sonnet-4.6-off-v1 |
26
+ | `antigravity-claude-opus-4.6-thinking` | off, high | Enabled (off, high) | pi-json-tool-loop/claude-opus-4.6-thinking-off-v1 |
27
+ | `antigravity-gpt-oss-120b` | off, medium | Enabled (off, medium) | pi-json-tool-loop/gpt-oss-120b-off-v1 |
28
28
 
29
29
  `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. `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. A future `fixture-qualified` route remains disabled for ordinary tool use; only separately authorized direct validation for the exact route may make it enabled.
30
30
 
package/dist/catalog.d.ts CHANGED
@@ -8,7 +8,8 @@ export interface ToolEvidenceRef {
8
8
  readonly wireModel: string;
9
9
  }
10
10
  export declare const GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE: "gemini-parameters-json-schema";
11
- export type ToolSchemaProfile = typeof GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE;
11
+ export declare const CLAUDE_CUSTOM_PARAMETERS_PROFILE: "claude-custom-parameters";
12
+ export type ToolSchemaProfile = typeof GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE | typeof CLAUDE_CUSTOM_PARAMETERS_PROFILE;
12
13
  export type ToolCapability = {
13
14
  readonly state: "disabled";
14
15
  readonly contractRevision: 1;
@@ -316,7 +317,7 @@ declare const CATALOG: readonly [{
316
317
  };
317
318
  };
318
319
  readonly replay: {
319
- readonly kind: "strip";
320
+ readonly kind: "same-public-model";
320
321
  };
321
322
  readonly response: {
322
323
  readonly kind: "gemini-envelope";
@@ -356,7 +357,7 @@ declare const CATALOG: readonly [{
356
357
  };
357
358
  };
358
359
  readonly replay: {
359
- readonly kind: "strip";
360
+ readonly kind: "same-public-model";
360
361
  };
361
362
  readonly response: {
362
363
  readonly kind: "gemini-envelope";
@@ -394,7 +395,7 @@ declare const CATALOG: readonly [{
394
395
  };
395
396
  };
396
397
  readonly replay: {
397
- readonly kind: "strip";
398
+ readonly kind: "same-public-model";
398
399
  };
399
400
  readonly response: {
400
401
  readonly kind: "gemini-envelope";
package/dist/catalog.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const ANSWER_RESERVE = 1024;
2
2
  export const GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE = "gemini-parameters-json-schema";
3
+ export const CLAUDE_CUSTOM_PARAMETERS_PROFILE = "claude-custom-parameters";
3
4
  const CATALOG = freeze(defineCatalog([{
4
5
  publicId: "antigravity-gemini-3.8-flash",
5
6
  descriptor: {
@@ -72,10 +73,10 @@ const CATALOG = freeze(defineCatalog([{
72
73
  maxTokens: 64_000,
73
74
  },
74
75
  routes: {
75
- off: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 0, includeThoughts: false }, tools: disabled("claude-continuity-unproven") },
76
- high: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: disabled("claude-continuity-unproven") },
76
+ off: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 0, includeThoughts: false }, tools: enabled({ record: "pi-json-tool-loop", revision: "claude-sonnet-4.6-off-v1", publicModelId: "antigravity-claude-sonnet-4.6", reasoning: "off", wireModel: "claude-sonnet-4-6" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
77
+ high: { wireModel: "claude-sonnet-4-6", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "claude-sonnet-4.6-high-v1", publicModelId: "antigravity-claude-sonnet-4.6", reasoning: "high", wireModel: "claude-sonnet-4-6" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
77
78
  },
78
- replay: { kind: "strip" },
79
+ replay: { kind: "same-public-model" },
79
80
  response: { kind: "gemini-envelope", family: "claude" },
80
81
  }, {
81
82
  publicId: "antigravity-claude-opus-4.6-thinking",
@@ -86,10 +87,10 @@ const CATALOG = freeze(defineCatalog([{
86
87
  maxTokens: 64_000,
87
88
  },
88
89
  routes: {
89
- off: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 0, includeThoughts: false }, tools: disabled("claude-continuity-unproven") },
90
- high: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: disabled("claude-continuity-unproven") },
90
+ off: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 0, includeThoughts: false }, tools: enabled({ record: "pi-json-tool-loop", revision: "claude-opus-4.6-thinking-off-v1", publicModelId: "antigravity-claude-opus-4.6-thinking", reasoning: "off", wireModel: "claude-opus-4-6-thinking" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
91
+ high: { wireModel: "claude-opus-4-6-thinking", thinking: { kind: "budget", budget: 1024, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "claude-opus-4.6-thinking-high-v1", publicModelId: "antigravity-claude-opus-4.6-thinking", reasoning: "high", wireModel: "claude-opus-4-6-thinking" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
91
92
  },
92
- replay: { kind: "strip" },
93
+ replay: { kind: "same-public-model" },
93
94
  response: { kind: "gemini-envelope", family: "claude" },
94
95
  }, {
95
96
  publicId: "antigravity-gpt-oss-120b",
@@ -100,10 +101,10 @@ const CATALOG = freeze(defineCatalog([{
100
101
  maxTokens: 32_768,
101
102
  },
102
103
  routes: {
103
- off: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "omit" }, tools: disabled("missing-direct-evidence") },
104
- medium: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "budget", budget: 8192, includeThoughts: true }, tools: disabled("missing-direct-evidence") },
104
+ off: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "omit" }, tools: enabled({ record: "pi-json-tool-loop", revision: "gpt-oss-120b-off-v1", publicModelId: "antigravity-gpt-oss-120b", reasoning: "off", wireModel: "gpt-oss-120b-medium" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
105
+ medium: { wireModel: "gpt-oss-120b-medium", thinking: { kind: "budget", budget: 8192, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "gpt-oss-120b-medium-v1", publicModelId: "antigravity-gpt-oss-120b", reasoning: "medium", wireModel: "gpt-oss-120b-medium" }, CLAUDE_CUSTOM_PARAMETERS_PROFILE) },
105
106
  },
106
- replay: { kind: "strip" },
107
+ replay: { kind: "same-public-model" },
107
108
  response: { kind: "gemini-envelope", family: "gpt-oss" },
108
109
  }]));
109
110
  const lookup = new Map(CATALOG.map((entry) => [entry.publicId, entry]));
package/dist/context.d.ts CHANGED
@@ -17,7 +17,8 @@ interface Content {
17
17
  interface GeminiFunctionDeclaration {
18
18
  readonly name: string;
19
19
  readonly description: string;
20
- readonly parametersJsonSchema: object;
20
+ readonly parametersJsonSchema?: object;
21
+ readonly parameters?: object;
21
22
  }
22
23
  export interface GenerationRequest {
23
24
  project: string;
package/dist/context.js CHANGED
@@ -1,4 +1,5 @@
1
- import { getCatalogEntry, resolveGenerationSelection } from "./catalog.js";
1
+ import { CLAUDE_CUSTOM_PARAMETERS_PROFILE, getCatalogEntry, resolveGenerationSelection } from "./catalog.js";
2
+ import { normalizeCustomToolSchema } from "./tool-schema.js";
2
3
  import { hasToolContext, prepareToolContext, replayToolHistory } from "./tool-context.js";
3
4
  const MAX_TEXT_BYTES = 8 * 1024 * 1024;
4
5
  const PROVIDER = "antigravity-guard";
@@ -25,18 +26,27 @@ export function serializeContext(input, injectedSelection, onDiagnostics) {
25
26
  const { toolChoice: _choice, ...textOptions } = isRecord(options) ? options : {};
26
27
  const text = serializeTextContext({ ...input, context: { ...textContext, messages: [{ role: "user", content: "placeholder", timestamp: 0 }] }, options: textOptions });
27
28
  const sameModel = (message) => field(message, "role") === "assistant" && entry.replay.kind === "same-public-model" && isSameProviderAndModel(message, entry.publicId);
28
- const signedToolReplay = geminiRequiresSignedToolReplay(selection.route.wireModel) ? {
29
- requireSignedToolCalls: true,
29
+ const requireSigned = geminiRequiresSignedToolReplay(selection.route.wireModel);
30
+ const includeToolCallId = entry.response.family === "claude" || entry.response.family === "gpt-oss";
31
+ const toolReplayPolicy = (requireSigned || includeToolCallId) ? {
32
+ requireSignedToolCalls: requireSigned,
30
33
  isSameModel: sameModel,
31
34
  toolCallSignature: (part, message) => sameModel(message) ? validThoughtSignature(field(part, "thoughtSignature")) : undefined,
35
+ includeToolCallId,
32
36
  } : undefined;
33
- const contents = replayToolHistory(isDenseArray(messages), (part, message) => messagePart(part, field(message, "role") === "assistant", sameModel(message)), onDiagnostics, signedToolReplay, prepared?.declarations.length ?? 0);
37
+ const contents = replayToolHistory(isDenseArray(messages), (part, message) => messagePart(part, field(message, "role") === "assistant", sameModel(message)), onDiagnostics, toolReplayPolicy, prepared?.declarations.length ?? 0);
34
38
  const { systemInstruction, generationConfig } = text.request;
35
39
  return { ...text, request: {
36
40
  contents,
37
41
  ...(systemInstruction ? { systemInstruction } : {}),
38
42
  ...(prepared ? {
39
- tools: [{ functionDeclarations: prepared.declarations.map(({ name, description, parameters }) => ({ name, description, parametersJsonSchema: parameters })) }],
43
+ tools: [{ functionDeclarations: prepared.declarations.map(({ name, description, parameters }) => ({
44
+ name,
45
+ description,
46
+ ...(selection.tools.state === "enabled" && selection.tools.schemaProfile === CLAUDE_CUSTOM_PARAMETERS_PROFILE
47
+ ? { parameters: normalizeCustomToolSchema(parameters) }
48
+ : { parametersJsonSchema: parameters }),
49
+ })) }],
40
50
  ...(prepared.mode === "NONE" ? { toolConfig: { functionCallingConfig: { mode: "NONE" } } } : {}),
41
51
  } : {}),
42
52
  generationConfig,
@@ -20,10 +20,12 @@ export interface ToolReplayDiagnostics {
20
20
  readonly assistantToolCallBlockCount: number;
21
21
  readonly declaredToolCount: number;
22
22
  }
23
+ export declare function sanitizeToolCallId(id: string, fallbackName?: string): string;
23
24
  export interface ToolReplayPolicy {
24
25
  readonly requireSignedToolCalls: boolean;
25
26
  readonly isSameModel: (message: Record<string, unknown>) => boolean;
26
27
  readonly toolCallSignature: (part: Record<string, unknown>, message: Record<string, unknown>) => string | undefined;
28
+ readonly includeToolCallId?: boolean;
27
29
  }
28
30
  export declare function replayToolHistory(messages: unknown[], serializePart: (part: Record<string, unknown>, message: Record<string, unknown>) => WirePart, onDiagnostics?: (diagnostics: ToolReplayDiagnostics) => void, policy?: ToolReplayPolicy, declaredToolCount?: number): WireContent[];
29
31
  export {};
@@ -1,5 +1,5 @@
1
1
  import { canonicalJson, ToolPreflightError } from "./tool-contract.js";
2
- import { GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE } from "./catalog.js";
2
+ import { CLAUDE_CUSTOM_PARAMETERS_PROFILE, GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE } from "./catalog.js";
3
3
  import { normalizeToolDeclarations } from "./tool-schema.js";
4
4
  export function hasToolContext(context) {
5
5
  if (!isRecord(context))
@@ -18,7 +18,7 @@ export function hasToolContext(context) {
18
18
  });
19
19
  }
20
20
  export function prepareToolContext(tools, choice, schemaProfile) {
21
- if (schemaProfile !== GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE)
21
+ if (schemaProfile !== GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE && schemaProfile !== CLAUDE_CUSTOM_PARAMETERS_PROFILE)
22
22
  throw new ToolPreflightError("PI_TOOL_SCHEMA_PROFILE_UNSUPPORTED", "tools", "$");
23
23
  const declarations = normalizeToolDeclarations(tools ?? []);
24
24
  if (!declarations.length) {
@@ -32,6 +32,12 @@ export function prepareToolContext(tools, choice, schemaProfile) {
32
32
  unsupportedChoice();
33
33
  return { declarations, mode: choice === "none" ? "NONE" : "AUTO" };
34
34
  }
35
+ let toolCallCounter = 0;
36
+ export function sanitizeToolCallId(id, fallbackName) {
37
+ const cleaned = id.replace(/[^a-zA-Z0-9_-]/g, "_");
38
+ const capped = cleaned.slice(0, 64);
39
+ return capped || `${fallbackName || "tool"}_${++toolCallCounter}`;
40
+ }
35
41
  export function replayToolHistory(messages, serializePart, onDiagnostics, policy, declaredToolCount = 0) {
36
42
  const output = [];
37
43
  const calls = new Set();
@@ -63,7 +69,7 @@ export function replayToolHistory(messages, serializePart, onDiagnostics, policy
63
69
  if (actual)
64
70
  return actual;
65
71
  recoveryCount.value += 1;
66
- return pending.observations ? observation(call, missingResultText()) : missingResult(call);
72
+ return pending.observations ? observation(call, missingResultText()) : missingResult(call, policy?.includeToolCallId);
67
73
  });
68
74
  if (pending.extraParts?.length) {
69
75
  turnParts.push(...pending.extraParts);
@@ -109,7 +115,7 @@ export function replayToolHistory(messages, serializePart, onDiagnostics, policy
109
115
  const text = textParts.join("\n\n");
110
116
  const outputText = text || (imageParts.length > 0 ? "[Image content]" : "");
111
117
  const response = value(current, "isError") === true ? { error: outputText } : { output: outputText };
112
- pending.results.set(id, pending.observations ? observation(call, outputText) : { functionResponse: { name: call.name, response } });
118
+ pending.results.set(id, pending.observations ? observation(call, outputText) : { functionResponse: { ...(policy?.includeToolCallId ? { id: sanitizeToolCallId(call.id, call.name) } : {}), name: call.name, response } });
113
119
  if (imageParts.length > 0) {
114
120
  if (!pending.extraParts)
115
121
  pending.extraParts = [];
@@ -174,7 +180,7 @@ export function replayToolHistory(messages, serializePart, onDiagnostics, policy
174
180
  return [part];
175
181
  if (observations)
176
182
  return [];
177
- return [{ functionCall: { name: part.call.name, args: part.call.args }, ...(part.signature ? { thoughtSignature: part.signature } : {}) }];
183
+ return [{ functionCall: { ...(policy?.includeToolCallId ? { id: sanitizeToolCallId(part.call.id, part.call.name) } : {}), name: part.call.name, args: part.call.args }, ...(part.signature ? { thoughtSignature: part.signature } : {}) }];
178
184
  }));
179
185
  pending = { calls: group, results: new Map(), observations };
180
186
  }
@@ -190,9 +196,10 @@ function observation(call, text) {
190
196
  function missingResultText() {
191
197
  return "Tool execution did not complete or its result was not recorded. Treat the call as failed; do not assume it had no side effects and do not retry it automatically.";
192
198
  }
193
- function missingResult(call) {
199
+ function missingResult(call, includeToolCallId) {
194
200
  return {
195
201
  functionResponse: {
202
+ ...(includeToolCallId ? { id: sanitizeToolCallId(call.id, call.name) } : {}),
196
203
  name: call.name,
197
204
  response: {
198
205
  error: {
@@ -5,3 +5,4 @@ export interface ToolDeclaration {
5
5
  readonly parameters: JsonObject;
6
6
  }
7
7
  export declare function normalizeToolDeclarations(value: unknown): readonly ToolDeclaration[];
8
+ export declare function normalizeCustomToolSchema(schema: unknown): JsonObject;
@@ -210,3 +210,34 @@ function isJsonObject(value) {
210
210
  function define(target, key, value) {
211
211
  Object.defineProperty(target, key, { value, enumerable: true, configurable: false, writable: false });
212
212
  }
213
+ const CUSTOM_TOOL_SCHEMA_ALLOW = new Set([
214
+ "type",
215
+ "description",
216
+ "properties",
217
+ "required",
218
+ "items",
219
+ "enum",
220
+ ]);
221
+ export function normalizeCustomToolSchema(schema) {
222
+ if (!schema || typeof schema !== "object" || Array.isArray(schema))
223
+ return {};
224
+ const out = {};
225
+ for (const [key, value] of Object.entries(schema)) {
226
+ if (!CUSTOM_TOOL_SCHEMA_ALLOW.has(key))
227
+ continue;
228
+ if (key === "properties" && value && typeof value === "object" && !Array.isArray(value)) {
229
+ const props = {};
230
+ for (const [propName, propSchema] of Object.entries(value)) {
231
+ props[propName] = normalizeCustomToolSchema(propSchema);
232
+ }
233
+ out.properties = props;
234
+ continue;
235
+ }
236
+ if (key === "items") {
237
+ out.items = normalizeCustomToolSchema(value);
238
+ continue;
239
+ }
240
+ out[key] = value;
241
+ }
242
+ return out;
243
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjamolina/pi-antigravity-guard",
3
- "version": "0.4.6",
3
+ "version": "0.4.8",
4
4
  "description": "Pi adapter for Antigravity Guard",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,7 +24,9 @@
24
24
  "README.md"
25
25
  ],
26
26
  "pi": {
27
- "extensions": ["./dist/extension.js"]
27
+ "extensions": [
28
+ "./dist/extension.js"
29
+ ]
28
30
  },
29
31
  "dependencies": {
30
32
  "@benjamolina/antigravity-guard-core": "0.1.0"