prefix-safe-json 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,6 +18,11 @@ pnpm add prefix-safe-json ai zod
18
18
  # or: npm install prefix-safe-json ai zod
19
19
  ```
20
20
 
21
+ Try a runnable comparison first: [download the six-SDK trial ZIP](https://github.com/canblmz1/prefix-safe-json/releases/download/v0.5.4/prefix-safe-json-0.5.4-trials.zip),
22
+ extract it, then run `node scripts/try-npm-demo.mjs tanstack` with Node 22+.
23
+ It uses published packages, authored streams and harmless counters; no API key
24
+ or repository build is needed. [Choose another SDK](examples/README.md).
25
+
21
26
  ## Safe execution example
22
27
 
23
28
  ```javascript
@@ -30,7 +35,7 @@ import {
30
35
  } from "prefix-safe-json";
31
36
  import { fromStandardSchema } from "prefix-safe-json/standard-schema";
32
37
 
33
- const writeFileSchema = z.object({ path: z.string(), content: z.string() });
38
+ const writeFileSchema = z.object({ path: z.string(), content: z.string() }).strict();
34
39
 
35
40
  const tools = createAiSdkExecutionLock({
36
41
  write_file: {
@@ -52,7 +57,8 @@ const { decisions, diagnostics } = guard.finish();
52
57
  for (const decision of decisions) {
53
58
  const authority = guard.takeDecision(decision.internalId);
54
59
  if (authority) {
55
- await writeFile(authority.value.path, authority.value.content);
60
+ const input = writeFileSchema.parse(authority.value);
61
+ await writeFile(input.path, input.content);
56
62
  } else {
57
63
  console.warn(`skipped ${decision.name}: ${decision.action} (${decision.reason})`);
58
64
  }
@@ -66,6 +72,16 @@ if (decisions.length === 0 && diagnostics.length > 0) {
66
72
 
67
73
  Using `jsonSchema()` from `ai` instead of Zod? Give the guard the raw schema inside it: `schemas: { write_file: writeFileSchema.jsonSchema }`. The wrapper itself is refused at construction, because it would otherwise validate nothing.
68
74
 
75
+ ## Typed inputs and a complete agent loop
76
+
77
+ Experimental `createTypedAiSdkExecutionGuard` in
78
+ `prefix-safe-json/standard-schema` requires **PSJ 0.6.0+**.
79
+ The [strict TypeScript agent-loop example](examples/typed-agent-loop/) checks
80
+ inferred raw inputs, matching result IDs and a final model answer, plus four
81
+ refusal/error scenarios. It runs against the actual tarball without an API
82
+ key. The caller owns dispatch and continuation. Schema transformations,
83
+ defaults and removed keys are refused; [validation details](docs/VALIDATION.md#typed-raw-input-guard).
84
+
69
85
  ## When this adds value
70
86
 
71
87
  Use PSJ when your application owns tool dispatch and needs original-stream
@@ -93,10 +109,18 @@ checkout without model requests or tool dispatch.
93
109
  ## Run a standalone npm demo
94
110
 
95
111
  To try it without cloning this repository, download the
96
- [standalone trial ZIP](https://github.com/canblmz1/prefix-safe-json/releases/tag/trial-2026-10-16).
97
- It contains only the example sources and licenses; its release notes give
98
- the extraction and npm commands. Choose an AI SDK, OpenAI, Anthropic or
99
- LangChain consumer; each installs published PSJ `0.5.3`.
112
+ [six-SDK trial ZIP](https://github.com/canblmz1/prefix-safe-json/releases/download/v0.5.4/prefix-safe-json-0.5.4-trials.zip)
113
+ and extract it. With Node 22+ and npm, run this from the extracted directory:
114
+
115
+ ```bash
116
+ node scripts/try-npm-demo.mjs tanstack
117
+ ```
118
+
119
+ Choose `ai-sdk`, `openai`, `anthropic`, `langchain`, `openrouter` or `tanstack`.
120
+ Every example installs published PSJ `0.5.4`; no API key or repository build
121
+ is needed. The ZIP contains example sources, the launcher and licenses;
122
+ dependencies are downloaded from npm. See the [trial index](examples/README.md)
123
+ for the SDK versions and checks. The earlier dated downloads remain unchanged.
100
124
 
101
125
  With Node 22+ and npm, you can run the published-package demo from this checkout
102
126
  without installing the repository's development dependencies:
@@ -118,7 +142,7 @@ npm ls prefix-safe-json ai zod
118
142
  npm start
119
143
  ```
120
144
 
121
- The current checkout pins `prefix-safe-json@0.5.3`, `ai@7.0.77`, and `zod@4.4.3`.
145
+ The current checkout pins `prefix-safe-json@0.5.4`, `ai@7.0.77`, and `zod@4.4.3`.
122
146
  It needs no repository build, workspace aliases, or API key. It drives real
123
147
  `streamText().fullStream` using the SDK's official `MockLanguageModelV4` and
124
148
  imports the installed package's public entry point and `standard-schema`
@@ -175,9 +199,11 @@ call IDs, while invalid batches cause zero effects and no continuation request.
175
199
  It uses authored SSE through the installed SDK; no live model call is made.
176
200
 
177
201
  Using TanStack AI with OpenRouter? Run `node scripts/try-npm-demo.mjs tanstack`
178
- for the [server-tool comparison](examples/tanstack-consumer/README.md), which
179
- uses PSJ `0.5.4` to check raw native finish reasons, usage trailers and paired
180
- results in the next request. It uses harmless counters and authored SSE.
202
+ for the [installed-package comparison](examples/tanstack-consumer/README.md).
203
+ It checks raw native finish reasons, usage trailers and paired results using
204
+ published PSJ `0.5.4`, harmless counters and authored SSE. The
205
+ [native-first standalone trial](https://gist.github.com/canblmz1/ba1e62d792da37d3f420c328f56d81be)
206
+ also offers a PSJ-free baseline and an optional comparison from npm or source.
181
207
 
182
208
  ## Why parse success is not enough
183
209
 
@@ -0,0 +1,43 @@
1
+ import type { ExecutionGuardOptions, AiSdkExecutionGuard } from "./types.js";
2
+ import type { ExecuteDecision } from "../gate/types.js";
3
+ import type { JsonValue } from "../types.js";
4
+ import { type StandardSchemaV1Like } from "../validation/standard-schema-validator.js";
5
+ /** @public (Experimental) Synchronous Standard Schema input registry. */
6
+ export type ToolInputSchemas = Record<string, StandardSchemaV1Like>;
7
+ type SchemaOutput<T> = T extends {
8
+ readonly "~standard": {
9
+ readonly types?: {
10
+ readonly output: infer Output;
11
+ };
12
+ };
13
+ } ? Output : unknown;
14
+ /**
15
+ * @public (Experimental)
16
+ * A discriminated union of registered tool names and their validated raw
17
+ * inputs. Schema outputs that change the raw JSON value are refused at runtime.
18
+ */
19
+ export type TypedExecuteDecision<TSchemas extends ToolInputSchemas> = {
20
+ [Name in keyof TSchemas & string]: Omit<ExecuteDecision, "name" | "value"> & {
21
+ readonly name: Name;
22
+ readonly value: SchemaOutput<TSchemas[Name]> & JsonValue;
23
+ };
24
+ }[keyof TSchemas & string];
25
+ /** @public (Experimental) Typed authority consumption; snapshots remain observations. */
26
+ export type TypedAiSdkExecutionGuard<TSchemas extends ToolInputSchemas> = Omit<AiSdkExecutionGuard, "takeDecision"> & {
27
+ takeDecision(internalId: string): TypedExecuteDecision<TSchemas> | undefined;
28
+ };
29
+ /** @public (Experimental) Only registered, unchanged schema inputs can execute. */
30
+ export interface TypedAiSdkExecutionGuardOptions<TSchemas extends ToolInputSchemas> extends Omit<ExecutionGuardOptions, "schemas" | "validators"> {
31
+ readonly inputSchemas: TSchemas;
32
+ }
33
+ /**
34
+ * @public (Experimental)
35
+ * Retains Standard Schema output types without substituting transformed
36
+ * values for raw arguments. Validation is synchronous and must preserve JSON
37
+ * structure (including keys); use strict or passthrough object schemas.
38
+ * Unknown tool names receive no authority. Existing untyped guard behavior
39
+ * and fromStandardSchema's validation-only semantics are unchanged.
40
+ */
41
+ export declare function createTypedAiSdkExecutionGuard<const TSchemas extends ToolInputSchemas>(options: TypedAiSdkExecutionGuardOptions<TSchemas>): TypedAiSdkExecutionGuard<TSchemas>;
42
+ export {};
43
+ //# sourceMappingURL=typed-ai-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-ai-sdk.d.ts","sourceRoot":"","sources":["../../src/guard/typed-ai-sdk.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,KAAK,EAAE,eAAe,EAAqB,MAAM,kBAAkB,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAiC,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAEtH,yEAAyE;AACzE,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEpE,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS;IAC/B,QAAQ,CAAC,WAAW,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CAC9E,GAAG,MAAM,GAAG,OAAO,CAAC;AAErB;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,gBAAgB,IAAI;KACnE,IAAI,IAAI,MAAM,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;QAC3E,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;QACpB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;KAC1D;CACF,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC;AAE3B,yFAAyF;AACzF,MAAM,MAAM,wBAAwB,CAAC,QAAQ,SAAS,gBAAgB,IAAI,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,GAAG;IACpH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;CAC9E,CAAC;AAEF,mFAAmF;AACnF,MAAM,WAAW,+BAA+B,CAAC,QAAQ,SAAS,gBAAgB,CAChF,SAAQ,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,YAAY,CAAC;IAC7D,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,CAAC,QAAQ,SAAS,gBAAgB,EACpF,OAAO,EAAE,+BAA+B,CAAC,QAAQ,CAAC,GACjD,wBAAwB,CAAC,QAAQ,CAAC,CA6CpC"}
@@ -0,0 +1,56 @@
1
+ import { createAiSdkExecutionGuard } from "./ai-sdk.js";
2
+ import { createStandardSchemaValidator } from "../validation/standard-schema-validator.js";
3
+ /**
4
+ * @public (Experimental)
5
+ * Retains Standard Schema output types without substituting transformed
6
+ * values for raw arguments. Validation is synchronous and must preserve JSON
7
+ * structure (including keys); use strict or passthrough object schemas.
8
+ * Unknown tool names receive no authority. Existing untyped guard behavior
9
+ * and fromStandardSchema's validation-only semantics are unchanged.
10
+ */
11
+ export function createTypedAiSdkExecutionGuard(options) {
12
+ // Extra properties survive structural typing and object spreads. Never
13
+ // silently ignore a caller's additional validation policy at runtime.
14
+ if ("schemas" in options || "validators" in options) {
15
+ throw new Error("createTypedAiSdkExecutionGuard: use inputSchemas only; schemas and validators cannot be combined with the typed guard.");
16
+ }
17
+ // Snapshot the registry: later caller mutations cannot change which names
18
+ // are allowed without changing the validators already held by the gate.
19
+ const validators = Object.fromEntries(Object.entries(options.inputSchemas).map(([name, schema]) => [name, createStandardSchemaValidator(schema, true)]));
20
+ const names = new Set(Object.keys(validators));
21
+ const guard = createAiSdkExecutionGuard({
22
+ validators, limits: options.limits, parserOptions: options.parserOptions,
23
+ });
24
+ const registered = (decision) => decision.name !== undefined && names.has(decision.name);
25
+ const observe = (decision) => {
26
+ if (decision.action !== "execute" || registered(decision))
27
+ return decision;
28
+ const { value: _value, ...rest } = decision;
29
+ return { ...rest, action: "reject", executable: false, reason: "schema_invalid" };
30
+ };
31
+ return {
32
+ push: part => guard.push(part),
33
+ snapshot: () => guard.snapshot().map(observe),
34
+ finish(meta) {
35
+ const final = guard.finish(meta);
36
+ const unknown = final.decisions.filter(d => !registered(d));
37
+ return {
38
+ decisions: final.decisions.map(observe),
39
+ diagnostics: [...final.diagnostics, ...unknown.map(d => ({
40
+ code: "E_UNREGISTERED_TOOL", severity: "error",
41
+ internalId: d.internalId,
42
+ message: "Typed guard has no input schema for this tool name.",
43
+ }))],
44
+ };
45
+ },
46
+ takeDecision(internalId) {
47
+ const decision = guard.takeDecision(internalId);
48
+ if (!decision || !registered(decision) || decision.evidence.schemaValid !== true)
49
+ return undefined;
50
+ // The registered validator checked the schema AND equality with raw
51
+ // input, and the underlying gate has consumed authority exactly once.
52
+ return decision;
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=typed-ai-sdk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-ai-sdk.js","sourceRoot":"","sources":["../../src/guard/typed-ai-sdk.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,4 @@
1
1
  export { fromStandardSchema } from "./validation/standard-schema-validator.js";
2
+ export { createTypedAiSdkExecutionGuard } from "./guard/typed-ai-sdk.js";
3
+ export type { ToolInputSchemas, TypedExecuteDecision, TypedAiSdkExecutionGuard, TypedAiSdkExecutionGuardOptions, } from "./guard/typed-ai-sdk.js";
2
4
  //# sourceMappingURL=standard-schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"standard-schema.d.ts","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC"}
1
+ {"version":3,"file":"standard-schema.d.ts","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AACzE,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,yBAAyB,CAAC"}
@@ -8,4 +8,5 @@
8
8
  // property the spec itself defines. See docs/VALIDATION.md.
9
9
  // ---------------------------------------------------------------------------
10
10
  export { fromStandardSchema } from "./validation/standard-schema-validator.js";
11
+ export { createTypedAiSdkExecutionGuard } from "./guard/typed-ai-sdk.js";
11
12
  //# sourceMappingURL=standard-schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"standard-schema.js","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,mEAAmE;AACnE,4EAA4E;AAC5E,yEAAyE;AACzE,uEAAuE;AACvE,4DAA4D;AAC5D,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC"}
1
+ {"version":3,"file":"standard-schema.js","sourceRoot":"","sources":["../src/standard-schema.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,mEAAmE;AACnE,4EAA4E;AAC5E,yEAAyE;AACzE,uEAAuE;AACvE,4DAA4D;AAC5D,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC"}
@@ -6,7 +6,7 @@ import type { ToolInputValidator } from "./types.js";
6
6
  * - just its documented shape, which Zod 4+, Valibot, ArkType, and others
7
7
  * already implement without any package of ours needing to know that.
8
8
  */
9
- interface StandardSchemaV1Like {
9
+ export interface StandardSchemaV1Like {
10
10
  readonly "~standard": {
11
11
  readonly version: 1;
12
12
  readonly vendor: string;
@@ -37,5 +37,6 @@ interface StandardSchemaV1Like {
37
37
  * `docs/VALIDATION.md#the-standard-schema-transformation-boundary`.
38
38
  */
39
39
  export declare function fromStandardSchema(schema: StandardSchemaV1Like): ToolInputValidator;
40
- export {};
40
+ /** @internal Typed guards additionally require an unchanged JSON output. */
41
+ export declare function createStandardSchemaValidator(schema: StandardSchemaV1Like, unchangedOutput?: boolean): ToolInputValidator;
41
42
  //# sourceMappingURL=standard-schema-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"standard-schema-validator.d.ts","sourceRoot":"","sources":["../../src/validation/standard-schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,YAAY,CAAC;AAE3E;;;;;;GAMG;AACH,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;KAChD,CAAC;CACH;AA6FD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,kBAAkB,CA8CnF"}
1
+ {"version":3,"file":"standard-schema-validator.d.ts","sourceRoot":"","sources":["../../src/validation/standard-schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,YAAY,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;KAChD,CAAC;CACH;AA6FD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,oBAAoB,GAAG,kBAAkB,CAEnF;AAED,4EAA4E;AAC5E,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,oBAAoB,EAAE,eAAe,UAAQ,GAAG,kBAAkB,CAuDvH"}
@@ -90,12 +90,22 @@ function assertStandardSchemaV1(schema) {
90
90
  * `docs/VALIDATION.md#the-standard-schema-transformation-boundary`.
91
91
  */
92
92
  export function fromStandardSchema(schema) {
93
+ return createStandardSchemaValidator(schema);
94
+ }
95
+ /** @internal Typed guards additionally require an unchanged JSON output. */
96
+ export function createStandardSchemaValidator(schema, unchangedOutput = false) {
93
97
  assertStandardSchemaV1(schema);
94
98
  const standard = schema["~standard"];
95
99
  return {
96
100
  validate(value) {
97
- const result = standard.validate(value);
101
+ // A typed boundary validates a copy: defaults/coercions or a mutating
102
+ // validator must never alter the raw value against which we compare.
103
+ const candidate = unchangedOutput ? structuredClone(value) : value;
104
+ const result = standard.validate(candidate);
98
105
  if (isThenable(result)) {
106
+ // Reject synchronously, but also observe an eventual async rejection
107
+ // so a misconfigured validator cannot cause an unhandled rejection.
108
+ void Promise.resolve(result).catch(() => undefined);
99
109
  throw new Error("prefix-safe-json: fromStandardSchema() only supports synchronous Standard Schema validators. " +
100
110
  "This schema's validate() returned a Promise - implement ToolInputValidator directly if you " +
101
111
  "need async validation, and resolve it before the call reaches this coordinator.");
@@ -124,8 +134,38 @@ export function fromStandardSchema(schema) {
124
134
  '"value" nor "issues" - not a valid Standard Schema SuccessResult or FailureResult. Received: ' +
125
135
  describeForError(result));
126
136
  }
137
+ if (unchangedOutput && !sameJsonValue(value, result.value)) {
138
+ return { valid: false, errors: ["Typed input validation must preserve the raw JSON value; transformations, defaults and removed keys are not accepted."] };
139
+ }
127
140
  return { valid: true };
128
141
  },
129
142
  };
130
143
  }
144
+ /** Compare JSON structure without depending on object property order. */
145
+ function sameJsonValue(raw, output) {
146
+ const pending = [[raw, output]];
147
+ while (pending.length > 0) {
148
+ const [left, right] = pending.pop();
149
+ if (Object.is(left, right))
150
+ continue;
151
+ if (!isRecord(left) || !isRecord(right))
152
+ return false;
153
+ if (Array.isArray(left) !== Array.isArray(right))
154
+ return false;
155
+ const prototype = Object.getPrototypeOf(right);
156
+ if (prototype !== null && prototype !== Object.prototype && prototype !== Array.prototype)
157
+ return false;
158
+ const keys = Object.keys(left);
159
+ if (keys.length !== Object.keys(right).length)
160
+ return false;
161
+ if (Array.isArray(left) && left.length !== right["length"])
162
+ return false;
163
+ for (const key of keys) {
164
+ if (!Object.hasOwn(right, key))
165
+ return false;
166
+ pending.push([left[key], right[key]]);
167
+ }
168
+ }
169
+ return true;
170
+ }
131
171
  //# sourceMappingURL=standard-schema-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"standard-schema-validator.js","sourceRoot":"","sources":["../../src/validation/standard-schema-validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"standard-schema-validator.js","sourceRoot":"","sources":["../../src/validation/standard-schema-validator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prefix-safe-json",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "Fail-closed execution integrity for streamed LLM tool calls — reject truncated or unconfirmed arguments before side effects",
5
5
  "keywords": [
6
6
  "json",