@benjamolina/pi-antigravity-guard 0.4.1 → 0.4.3

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
@@ -18,7 +18,7 @@ All seven rows below are registered for text. The exact `antigravity-gemini-3.8-
18
18
 
19
19
  | Public ID | Exposed Pi levels | Tool state | Admission |
20
20
  |---|---|---|---|
21
- | `antigravity-gemini-3.8-flash` | off, low, medium, high | Enabled (off) | pi-json-tool-loop/gemini-3.8-flash-off-v1 |
21
+ | `antigravity-gemini-3.8-flash` | off, low, medium, high | Enabled (off, low, medium, high) | pi-json-tool-loop/gemini-3.8-flash-off-v1 |
22
22
  | `antigravity-gemini-3.7-flash` | off, low, medium, high | Disabled | missing-direct-evidence |
23
23
  | `antigravity-gemini-3.6-flash` | off, low, medium, high | Disabled | missing-direct-evidence |
24
24
  | `antigravity-gemini-3.1-pro` | off, low, high | Disabled | missing-direct-evidence |
@@ -38,7 +38,7 @@ Maintainers can run `npm run build:pi` followed by `npx tsx scripts/pi-tool-loop
38
38
 
39
39
  Tool-bearing requests are never silently downgraded to text. When an exact route is eventually enabled, declarations are retained in order and omitted or `auto` choice serializes as `AUTO`; explicit `none` serializes as `NONE`. Forced, required, named, unknown, and constrained-sampling choices are rejected before transport.
40
40
 
41
- Schemas must use the documented strict object-rooted subset. Unsupported keywords, references, unions, lossy constraints, empty objects, and oversized input are rejected rather than cleaned. Tool results must match the exact call ID and name, remain text-only, and are replayed in assistant source-call order; image-bearing or ambiguous results are rejected. A missing persisted result uses the documented failed response only during context reconstruction.
41
+ Only the evidence-enabled Gemini 3.8 Flash `off` route uses the `parametersJsonSchema` profile. Its hermetically verified preflight preserves ordinary JSON Schema constraints and JSON values in canonical immutable output, including safe local RFC 6901 `$ref` expansion (`#` and `#/...`) with resolved definitions removed. External, malformed, unresolved, cyclic, or otherwise unsafe references fail closed before transport. This is local serialization and safety evidence, not direct live backend acceptance of constrained or reference-based schemas. Tool results must match the exact call ID and name, remain text-only, and are replayed in assistant source-call order; image-bearing or ambiguous results are rejected. A missing persisted result uses the documented failed response only during context reconstruction.
42
42
 
43
43
  ## Text catalog limits and safety
44
44
 
package/dist/catalog.d.ts CHANGED
@@ -7,6 +7,8 @@ export interface ToolEvidenceRef {
7
7
  readonly reasoning: StandardReasoningLevel;
8
8
  readonly wireModel: string;
9
9
  }
10
+ export declare const GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE: "gemini-parameters-json-schema";
11
+ export type ToolSchemaProfile = typeof GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE;
10
12
  export type ToolCapability = {
11
13
  readonly state: "disabled";
12
14
  readonly contractRevision: 1;
@@ -18,6 +20,7 @@ export type ToolCapability = {
18
20
  } | {
19
21
  readonly state: "enabled";
20
22
  readonly contractRevision: 1;
23
+ readonly schemaProfile: ToolSchemaProfile;
21
24
  readonly fixtureEvidence: ToolEvidenceRef;
22
25
  readonly directEvidence: ToolEvidenceRef;
23
26
  };
@@ -75,37 +78,37 @@ declare const CATALOG: readonly [{
75
78
  };
76
79
  readonly routes: {
77
80
  readonly off: {
78
- readonly wireModel: "gemini-3.8-flash-tiered";
81
+ readonly wireModel: "gemini-3.8-flash-low";
79
82
  readonly thinking: {
80
- readonly kind: "native-level";
81
- readonly thinkingLevel: "low";
83
+ readonly kind: "budget";
84
+ readonly budget: 0;
82
85
  readonly includeThoughts: false;
83
86
  };
84
87
  readonly tools: ToolCapability;
85
88
  };
86
89
  readonly low: {
87
- readonly wireModel: "gemini-3.8-flash-tiered";
90
+ readonly wireModel: "gemini-3.8-flash-low";
88
91
  readonly thinking: {
89
- readonly kind: "native-level";
90
- readonly thinkingLevel: "low";
92
+ readonly kind: "budget";
93
+ readonly budget: 1000;
91
94
  readonly includeThoughts: true;
92
95
  };
93
96
  readonly tools: ToolCapability;
94
97
  };
95
98
  readonly medium: {
96
- readonly wireModel: "gemini-3.8-flash-tiered";
99
+ readonly wireModel: "gemini-3.8-flash-medium";
97
100
  readonly thinking: {
98
- readonly kind: "native-level";
99
- readonly thinkingLevel: "medium";
101
+ readonly kind: "budget";
102
+ readonly budget: 4000;
100
103
  readonly includeThoughts: true;
101
104
  };
102
105
  readonly tools: ToolCapability;
103
106
  };
104
107
  readonly high: {
105
- readonly wireModel: "gemini-3.8-flash-tiered";
108
+ readonly wireModel: "gemini-3.8-flash-high";
106
109
  readonly thinking: {
107
- readonly kind: "native-level";
108
- readonly thinkingLevel: "high";
110
+ readonly kind: "budget";
111
+ readonly budget: -1;
109
112
  readonly includeThoughts: true;
110
113
  };
111
114
  readonly tools: ToolCapability;
@@ -402,7 +405,7 @@ type Catalog = typeof CATALOG;
402
405
  export type AntigravityPublicModelId = Catalog[number]["publicId"];
403
406
  export type AntigravityWireModelId = Catalog[number]["routes"][keyof Catalog[number]["routes"]]["wireModel"];
404
407
  /** Constructs deterministic capability data for hermetic serializer tests only. */
405
- export declare function createEnabledToolCapability(evidence: ToolEvidenceRef): Extract<ToolCapability, {
408
+ export declare function createEnabledToolCapability(evidence: ToolEvidenceRef, schemaProfile?: ToolSchemaProfile): Extract<ToolCapability, {
406
409
  readonly state: "enabled";
407
410
  }>;
408
411
  export declare function defineCatalog<T extends readonly {
package/dist/catalog.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const ANSWER_RESERVE = 1024;
2
+ export const GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE = "gemini-parameters-json-schema";
2
3
  const CATALOG = freeze(defineCatalog([{
3
4
  publicId: "antigravity-gemini-3.8-flash",
4
5
  descriptor: {
@@ -8,10 +9,10 @@ const CATALOG = freeze(defineCatalog([{
8
9
  maxTokens: 65_536,
9
10
  },
10
11
  routes: {
11
- off: { wireModel: "gemini-3.8-flash-tiered", thinking: { kind: "native-level", thinkingLevel: "low", includeThoughts: false }, tools: enabled({ record: "pi-json-tool-loop", revision: "gemini-3.8-flash-off-v1", publicModelId: "antigravity-gemini-3.8-flash", reasoning: "off", wireModel: "gemini-3.8-flash-tiered" }) },
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") },
12
+ off: { wireModel: "gemini-3.8-flash-low", thinking: { kind: "budget", budget: 0, includeThoughts: false }, tools: enabled({ record: "pi-json-tool-loop", revision: "gemini-3.8-flash-off-v1", publicModelId: "antigravity-gemini-3.8-flash", reasoning: "off", wireModel: "gemini-3.8-flash-low" }, GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) },
13
+ low: { wireModel: "gemini-3.8-flash-low", thinking: { kind: "budget", budget: 1000, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "gemini-3.8-flash-low-v1", publicModelId: "antigravity-gemini-3.8-flash", reasoning: "low", wireModel: "gemini-3.8-flash-low" }, GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) },
14
+ medium: { wireModel: "gemini-3.8-flash-medium", thinking: { kind: "budget", budget: 4000, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "gemini-3.8-flash-medium-v1", publicModelId: "antigravity-gemini-3.8-flash", reasoning: "medium", wireModel: "gemini-3.8-flash-medium" }, GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) },
15
+ high: { wireModel: "gemini-3.8-flash-high", thinking: { kind: "budget", budget: -1, includeThoughts: true }, tools: enabled({ record: "pi-json-tool-loop", revision: "gemini-3.8-flash-high-v1", publicModelId: "antigravity-gemini-3.8-flash", reasoning: "high", wireModel: "gemini-3.8-flash-high" }, GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) },
15
16
  },
16
17
  replay: { kind: "same-public-model" },
17
18
  response: { kind: "gemini-envelope", family: "gemini" },
@@ -109,12 +110,12 @@ const lookup = new Map(CATALOG.map((entry) => [entry.publicId, entry]));
109
110
  function disabled(reason) {
110
111
  return { state: "disabled", contractRevision: 1, reason };
111
112
  }
112
- function enabled(evidence) {
113
- return { state: "enabled", contractRevision: 1, fixtureEvidence: evidence, directEvidence: evidence };
113
+ function enabled(evidence, schemaProfile = GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) {
114
+ return { state: "enabled", contractRevision: 1, schemaProfile, fixtureEvidence: evidence, directEvidence: evidence };
114
115
  }
115
116
  /** Constructs deterministic capability data for hermetic serializer tests only. */
116
- export function createEnabledToolCapability(evidence) {
117
- return freeze({ state: "enabled", contractRevision: 1, fixtureEvidence: evidence, directEvidence: evidence });
117
+ export function createEnabledToolCapability(evidence, schemaProfile = GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE) {
118
+ return freeze({ state: "enabled", contractRevision: 1, schemaProfile, fixtureEvidence: evidence, directEvidence: evidence });
118
119
  }
119
120
  function freeze(value) {
120
121
  if (typeof value === "object" && value !== null) {
package/dist/context.js CHANGED
@@ -20,7 +20,7 @@ export function serializeContext(input, injectedSelection, onDiagnostics) {
20
20
  fail("Invalid tool capability selection.");
21
21
  if (selection.tools.state !== "enabled")
22
22
  fail(capabilityError(entry.publicId, selection.level, selection.tools));
23
- const prepared = prepareToolContext(field(context, "tools") ?? [], isRecord(options) ? field(options, "toolChoice") : undefined);
23
+ const prepared = prepareToolContext(field(context, "tools") ?? [], isRecord(options) ? field(options, "toolChoice") : undefined, selection.tools.schemaProfile);
24
24
  const { tools: _tools, messages, ...textContext } = context;
25
25
  const { toolChoice: _choice, ...textOptions } = isRecord(options) ? options : {};
26
26
  const text = serializeTextContext({ ...input, context: { ...textContext, messages: [{ role: "user", content: "placeholder", timestamp: 0 }] }, options: textOptions });
@@ -4,7 +4,7 @@ export interface PreparedToolContext {
4
4
  readonly mode: "AUTO" | "NONE";
5
5
  }
6
6
  export declare function hasToolContext(context: unknown): boolean;
7
- export declare function prepareToolContext(tools: unknown, choice: unknown): PreparedToolContext | undefined;
7
+ export declare function prepareToolContext(tools: unknown, choice: unknown, schemaProfile: unknown): PreparedToolContext | undefined;
8
8
  type WirePart = object;
9
9
  type WireContent = {
10
10
  role: "user" | "model";
@@ -1,4 +1,5 @@
1
1
  import { canonicalJson, ToolPreflightError } from "./tool-contract.js";
2
+ import { GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE } from "./catalog.js";
2
3
  import { normalizeToolDeclarations } from "./tool-schema.js";
3
4
  export function hasToolContext(context) {
4
5
  if (!isRecord(context))
@@ -16,7 +17,9 @@ export function hasToolContext(context) {
16
17
  return Array.isArray(content) && content.some((part) => isRecord(part) && value(part, "type") === "toolCall");
17
18
  });
18
19
  }
19
- export function prepareToolContext(tools, choice) {
20
+ export function prepareToolContext(tools, choice, schemaProfile) {
21
+ if (schemaProfile !== GEMINI_PARAMETERS_JSON_SCHEMA_PROFILE)
22
+ throw new ToolPreflightError("PI_TOOL_SCHEMA_PROFILE_UNSUPPORTED", "tools", "$");
20
23
  const declarations = normalizeToolDeclarations(tools ?? []);
21
24
  if (!declarations.length) {
22
25
  if (choice === "auto")
@@ -1,10 +1,12 @@
1
- import { canonicalJson, denseArray, fail, freeze, ownData, plainObject } from "./tool-contract.js";
2
- const TYPES = new Set(["object", "array", "string", "number", "integer", "boolean"]);
3
- const KEYWORDS = new Set(["type", "description", "nullable", "enum", "const", "properties", "required", "items"]);
1
+ import { denseArray, fail, freeze, ownData, plainObject } from "./tool-contract.js";
4
2
  const MAX_SCHEMA_DEPTH = 32;
5
3
  const MAX_SCHEMA_NODES = 2048;
6
4
  const MAX_SCHEMA_BYTES = 256 * 1024;
7
5
  const MAX_TOTAL_SCHEMA_BYTES = 1024 * 1024;
6
+ const METADATA = new Set(["$schema", "$id", "$anchor", "$dynamicAnchor", "$vocabulary", "$comment"]);
7
+ const SCHEMA_MAPS = new Set(["properties", "patternProperties", "dependentSchemas"]);
8
+ const SCHEMAS = new Set(["additionalItems", "additionalProperties", "contains", "contentSchema", "else", "if", "items", "not", "propertyNames", "then", "unevaluatedItems", "unevaluatedProperties"]);
9
+ const SCHEMA_ARRAYS = new Set(["allOf", "anyOf", "oneOf", "prefixItems"]);
8
10
  export function normalizeToolDeclarations(value) {
9
11
  const tools = denseArray(value, "tools", "$");
10
12
  const names = new Set();
@@ -32,94 +34,179 @@ function normalizeDeclaration(value, index, names) {
32
34
  const constrainedSampling = ownData(tool, "constrainedSampling", name, path);
33
35
  if (constrainedSampling !== undefined && constrainedSampling !== false)
34
36
  fail("PI_TOOL_CONSTRAINED_SAMPLING_UNSUPPORTED", name, `${path}.constrainedSampling`);
35
- const parameterRoot = plainObject(ownData(tool, "parameters", name, path), name, "$");
36
- if (ownData(parameterRoot, "type", name, "$") !== "object")
37
+ const root = plainObject(ownData(tool, "parameters", name, path), name, "$");
38
+ if (ownData(root, "type", name, "$") !== "object")
39
+ fail("PI_TOOL_SCHEMA_INVALID", name, "$.type");
40
+ const snapshot = snapshotJson(root, name, "$", { ancestors: new Set(), nodes: 0 });
41
+ if (!isJsonObject(snapshot))
42
+ fail("PI_TOOL_SCHEMA_INVALID", name, "$");
43
+ const parameters = emitSchema(snapshot, snapshot, name, "$", { nodes: 0 }, new Set());
44
+ if (!isJsonObject(parameters) || parameters.type !== "object")
37
45
  fail("PI_TOOL_SCHEMA_INVALID", name, "$.type");
38
- const parameters = normalizeSchema(parameterRoot, name, "$");
39
46
  if (Buffer.byteLength(JSON.stringify(parameters), "utf8") > MAX_SCHEMA_BYTES)
40
47
  fail("PI_TOOL_SCHEMA_LIMIT", name, "$");
41
48
  return freeze({ name, description, parameters });
42
49
  }
43
- function normalizeSchema(value, declaration, path, state = { ancestors: new Set(), nodes: 0 }, depth = 0) {
50
+ function snapshotJson(value, declaration, path, state, depth = 0) {
44
51
  if (depth > MAX_SCHEMA_DEPTH || ++state.nodes > MAX_SCHEMA_NODES)
45
52
  fail("PI_TOOL_SCHEMA_LIMIT", declaration, path);
46
- if (typeof value === "object" && value !== null) {
47
- if (state.ancestors.has(value))
48
- fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
49
- state.ancestors.add(value);
53
+ if (value === null || typeof value === "string" || typeof value === "boolean")
54
+ return value;
55
+ if (typeof value === "number")
56
+ return Number.isFinite(value) ? value : fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
57
+ if (typeof value !== "object" || state.ancestors.has(value))
58
+ fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
59
+ state.ancestors.add(value);
60
+ try {
61
+ if (Array.isArray(value))
62
+ return freeze(snapshotArray(value, declaration, path, state, depth));
63
+ const object = plainObject(value, declaration, path);
64
+ const output = {};
65
+ for (const key of Object.keys(object).sort())
66
+ define(output, key, snapshotJson(ownData(object, key, declaration, path), declaration, pathSegment(path, key), state, depth + 1));
67
+ return freeze(output);
50
68
  }
51
- const schema = plainObject(value, declaration, path);
52
- for (const key of Object.keys(schema))
53
- if (!KEYWORDS.has(key))
54
- fail("PI_TOOL_SCHEMA_UNSUPPORTED", declaration, `${path}.${key}`);
55
- const type = ownData(schema, "type", declaration, path);
56
- if (typeof type !== "string" || !TYPES.has(type))
57
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.type`);
58
- const output = { type };
59
- const description = ownData(schema, "description", declaration, path);
60
- if (description !== undefined) {
61
- if (typeof description !== "string")
62
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.description`);
63
- output.description = description;
69
+ finally {
70
+ state.ancestors.delete(value);
64
71
  }
65
- const nullable = ownData(schema, "nullable", declaration, path);
66
- if (nullable !== undefined) {
67
- if (typeof nullable !== "boolean")
68
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.nullable`);
69
- output.nullable = nullable;
72
+ }
73
+ function snapshotArray(value, declaration, path, state, depth) {
74
+ if (Object.getPrototypeOf(value) !== Array.prototype || Object.getOwnPropertySymbols(value).length || Object.getOwnPropertyNames(value).length !== value.length + 1)
75
+ fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
76
+ const values = denseArray(value, declaration, path);
77
+ const output = [];
78
+ for (let index = 0; index < values.length; index += 1) {
79
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
80
+ if (!descriptor || !("value" in descriptor))
81
+ fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}[${index}]`);
82
+ output.push(snapshotJson(descriptor.value, declaration, `${path}[${index}]`, state, depth + 1));
70
83
  }
71
- const enumValue = ownData(schema, "enum", declaration, path);
72
- const constant = ownData(schema, "const", declaration, path);
73
- if (enumValue !== undefined && constant !== undefined)
74
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.const`);
75
- if (enumValue !== undefined)
76
- output.enum = normalizeEnum(enumValue, type, nullable === true, declaration, `${path}.enum`);
77
- if (constant !== undefined)
78
- output.enum = freeze([validateEnumValue(constant, type, nullable === true, declaration, `${path}.const`)]);
79
- if (type === "object")
80
- normalizeObject(schema, output, declaration, path, state, depth);
81
- else if (type === "array")
82
- normalizeArray(schema, output, declaration, path, state, depth);
83
- else if (ownData(schema, "properties", declaration, path) !== undefined || ownData(schema, "required", declaration, path) !== undefined || ownData(schema, "items", declaration, path) !== undefined)
84
+ return output;
85
+ }
86
+ function emitSchema(value, root, declaration, path, state, targets, depth = 0) {
87
+ if (depth > MAX_SCHEMA_DEPTH || ++state.nodes > MAX_SCHEMA_NODES)
88
+ fail("PI_TOOL_SCHEMA_LIMIT", declaration, path);
89
+ if (typeof value === "boolean")
90
+ return value;
91
+ if (!isJsonObject(value))
84
92
  fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
85
- return freeze(output);
93
+ const reference = value.$ref;
94
+ if (reference !== undefined) {
95
+ const referencePath = pathSegment(path, "$ref");
96
+ const target = resolveReference(reference, root, declaration, referencePath);
97
+ if (isJsonObject(target) && targets.has(target))
98
+ fail("PI_TOOL_SCHEMA_REFERENCE_CYCLE", declaration, referencePath);
99
+ const next = new Set(targets);
100
+ if (isJsonObject(target))
101
+ next.add(target);
102
+ const resolved = emitSchema(target, root, declaration, path, state, next, depth + 1);
103
+ const sibling = emitObject(value, root, declaration, path, state, targets, depth + 1, new Set(["$ref"]));
104
+ if (!Object.keys(sibling).length)
105
+ return resolved;
106
+ if (resolved === true || resolved === false)
107
+ return resolved === true ? sibling : false;
108
+ return freeze({ allOf: freeze([resolved, sibling]) });
109
+ }
110
+ return emitObject(value, root, declaration, path, state, targets, depth, new Set());
86
111
  }
87
- function normalizeObject(schema, output, declaration, path, state, depth) {
88
- if (ownData(schema, "items", declaration, path) !== undefined)
89
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.items`);
90
- const properties = plainObject(ownData(schema, "properties", declaration, path), declaration, `${path}.properties`);
91
- const names = Object.keys(properties).sort();
92
- if (!names.length)
93
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.properties`);
94
- const normalized = {};
95
- for (const name of names)
96
- normalized[name] = normalizeSchema(properties[name], declaration, `${path}.properties.${name}`, state, depth + 1);
97
- output.properties = freeze(normalized);
98
- const required = ownData(schema, "required", declaration, path);
99
- if (required !== undefined) {
100
- const values = denseArray(required, declaration, `${path}.required`);
101
- if (values.some((name) => typeof name !== "string" || !Object.hasOwn(properties, name)) || new Set(values).size !== values.length)
102
- fail("PI_TOOL_SCHEMA_INVALID", declaration, `${path}.required`);
103
- output.required = freeze([...values].sort());
112
+ function emitObject(value, root, declaration, path, state, targets, depth, omitted) {
113
+ const output = {};
114
+ for (const key of Object.keys(value)) {
115
+ const child = value[key];
116
+ if (omitted.has(key) || METADATA.has(key))
117
+ continue;
118
+ if (key === "$defs" || key === "definitions") {
119
+ emitSchemaMap(child, root, declaration, pathSegment(path, key), state, targets, depth + 1);
120
+ continue;
121
+ }
122
+ if (SCHEMA_MAPS.has(key))
123
+ define(output, key, emitSchemaMap(child, root, declaration, pathSegment(path, key), state, targets, depth + 1));
124
+ else if (SCHEMA_ARRAYS.has(key))
125
+ define(output, key, emitSchemaArray(child, root, declaration, pathSegment(path, key), state, targets, depth + 1));
126
+ else if (key === "dependencies")
127
+ define(output, key, emitDependencies(child, root, declaration, pathSegment(path, key), state, targets, depth + 1));
128
+ else if (key === "items" && Array.isArray(child))
129
+ define(output, key, emitSchemaArray(child, root, declaration, pathSegment(path, key), state, targets, depth + 1));
130
+ else if (SCHEMAS.has(key))
131
+ define(output, key, emitSchema(child, root, declaration, pathSegment(path, key), state, targets, depth + 1));
132
+ else
133
+ define(output, key, child);
104
134
  }
135
+ return freeze(output);
105
136
  }
106
- function normalizeArray(schema, output, declaration, path, state, depth) {
107
- if (ownData(schema, "properties", declaration, path) !== undefined || ownData(schema, "required", declaration, path) !== undefined)
137
+ function emitSchemaMap(value, root, declaration, path, state, targets, depth) {
138
+ if (!isJsonObject(value))
108
139
  fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
109
- output.items = normalizeSchema(ownData(schema, "items", declaration, path), declaration, `${path}.items`, state, depth + 1);
140
+ const output = {};
141
+ for (const key of Object.keys(value))
142
+ define(output, key, emitSchema(value[key], root, declaration, pathSegment(path, key), state, targets, depth));
143
+ return freeze(output);
110
144
  }
111
- function normalizeEnum(value, type, nullable, declaration, path) {
112
- const values = denseArray(value, declaration, path);
113
- if (!values.length)
114
- fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
115
- const normalized = values.map((item, index) => validateEnumValue(item, type, nullable, declaration, `${path}[${index}]`));
116
- if (new Set(normalized.map((item) => JSON.stringify(item))).size !== normalized.length)
145
+ function emitSchemaArray(value, root, declaration, path, state, targets, depth) {
146
+ if (!Array.isArray(value))
117
147
  fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
118
- return freeze(normalized);
148
+ return freeze(value.map((item, index) => emitSchema(item, root, declaration, `${path}[${index}]`, state, targets, depth)));
119
149
  }
120
- function validateEnumValue(value, type, nullable, declaration, path) {
121
- const json = canonicalJson(value, declaration, path);
122
- if (typeof json === "object" || (json === null && !nullable) || (json !== null && (type === "integer" ? typeof json !== "number" || !Number.isInteger(json) : typeof json !== type)))
150
+ function emitDependencies(value, root, declaration, path, state, targets, depth) {
151
+ if (!isJsonObject(value))
123
152
  fail("PI_TOOL_SCHEMA_INVALID", declaration, path);
124
- return json;
153
+ const output = {};
154
+ for (const key of Object.keys(value)) {
155
+ const dependency = value[key];
156
+ if (Array.isArray(dependency) && dependency.every((entry) => typeof entry === "string"))
157
+ define(output, key, dependency);
158
+ else
159
+ define(output, key, emitSchema(dependency, root, declaration, pathSegment(path, key), state, targets, depth));
160
+ }
161
+ return freeze(output);
162
+ }
163
+ function resolveReference(reference, root, declaration, path) {
164
+ if (typeof reference !== "string" || (reference !== "#" && !reference.startsWith("#/")))
165
+ fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
166
+ if (reference === "#")
167
+ return root;
168
+ let pointer;
169
+ try {
170
+ pointer = decodeURIComponent(reference.slice(1));
171
+ }
172
+ catch {
173
+ return fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
174
+ }
175
+ let current = root;
176
+ for (const rawToken of pointer.slice(1).split("/")) {
177
+ const token = decodeToken(rawToken, declaration, path);
178
+ if (Array.isArray(current)) {
179
+ if (!/^(0|[1-9][0-9]*)$/.test(token) || !Number.isSafeInteger(Number(token)) || Number(token) >= current.length)
180
+ fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
181
+ current = current[Number(token)];
182
+ }
183
+ else if (isJsonObject(current)) {
184
+ if (!Object.hasOwn(current, token))
185
+ fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
186
+ current = current[token];
187
+ }
188
+ else
189
+ fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
190
+ }
191
+ if (typeof current !== "boolean" && !isJsonObject(current))
192
+ fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
193
+ return current;
194
+ }
195
+ function decodeToken(value, declaration, path) {
196
+ return value.replace(/~(.)?/g, (escape, next) => {
197
+ if (escape === "~0")
198
+ return "~";
199
+ if (escape === "~1")
200
+ return "/";
201
+ return fail("PI_TOOL_SCHEMA_REFERENCE_INVALID", declaration, path);
202
+ });
203
+ }
204
+ function pathSegment(path, key) {
205
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? `${path}.${key}` : `${path}[${JSON.stringify(key)}]`;
206
+ }
207
+ function isJsonObject(value) {
208
+ return typeof value === "object" && value !== null && !Array.isArray(value);
209
+ }
210
+ function define(target, key, value) {
211
+ Object.defineProperty(target, key, { value, enumerable: true, configurable: false, writable: false });
125
212
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjamolina/pi-antigravity-guard",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Pi adapter for Antigravity Guard",
5
5
  "type": "module",
6
6
  "license": "MIT",