prefix-safe-json 0.4.5 → 0.5.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.
Files changed (52) hide show
  1. package/LICENSE +27 -4
  2. package/README.md +273 -398
  3. package/dist/ajv.d.ts +2 -0
  4. package/dist/ajv.d.ts.map +1 -0
  5. package/dist/ajv.js +19 -0
  6. package/dist/ajv.js.map +1 -0
  7. package/dist/conformance/runner.d.ts +19 -0
  8. package/dist/conformance/runner.d.ts.map +1 -0
  9. package/dist/conformance/runner.js +78 -0
  10. package/dist/conformance/runner.js.map +1 -0
  11. package/dist/conformance/types.d.ts +115 -0
  12. package/dist/conformance/types.d.ts.map +1 -0
  13. package/dist/conformance/types.js +2 -0
  14. package/dist/conformance/types.js.map +1 -0
  15. package/dist/conformance.d.ts +3 -0
  16. package/dist/conformance.d.ts.map +1 -0
  17. package/dist/conformance.js +11 -0
  18. package/dist/conformance.js.map +1 -0
  19. package/dist/coordinator/coordinator.d.ts +25 -11
  20. package/dist/coordinator/coordinator.d.ts.map +1 -1
  21. package/dist/coordinator/coordinator.js +140 -25
  22. package/dist/coordinator/coordinator.js.map +1 -1
  23. package/dist/coordinator/types.d.ts +3 -2
  24. package/dist/coordinator/types.d.ts.map +1 -1
  25. package/dist/gate/gate.d.ts +2 -2
  26. package/dist/gate/gate.d.ts.map +1 -1
  27. package/dist/gate/gate.js +4 -4
  28. package/dist/gate/gate.js.map +1 -1
  29. package/dist/guard/provider-guard.js +1 -1
  30. package/dist/guard/provider-guard.js.map +1 -1
  31. package/dist/guard/types.d.ts +11 -4
  32. package/dist/guard/types.d.ts.map +1 -1
  33. package/dist/index.d.ts +11 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/standard-schema.d.ts +2 -0
  37. package/dist/standard-schema.d.ts.map +1 -0
  38. package/dist/standard-schema.js +11 -0
  39. package/dist/standard-schema.js.map +1 -0
  40. package/dist/validation/ajv-validator.d.ts +43 -0
  41. package/dist/validation/ajv-validator.d.ts.map +1 -0
  42. package/dist/validation/ajv-validator.js +63 -0
  43. package/dist/validation/ajv-validator.js.map +1 -0
  44. package/dist/validation/standard-schema-validator.d.ts +41 -0
  45. package/dist/validation/standard-schema-validator.d.ts.map +1 -0
  46. package/dist/validation/standard-schema-validator.js +131 -0
  47. package/dist/validation/standard-schema-validator.js.map +1 -0
  48. package/dist/validation/types.d.ts +60 -0
  49. package/dist/validation/types.d.ts.map +1 -0
  50. package/dist/validation/types.js +77 -0
  51. package/dist/validation/types.js.map +1 -0
  52. package/package.json +13 -1
package/dist/ajv.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { createAjvValidator } from "./validation/ajv-validator.js";
2
+ //# sourceMappingURL=ajv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ajv.d.ts","sourceRoot":"","sources":["../src/ajv.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC"}
package/dist/ajv.js ADDED
@@ -0,0 +1,19 @@
1
+ // ---------------------------------------------------------------------------
2
+ // prefix-safe-json/ajv — explicit Ajv adapter entry point.
3
+ //
4
+ // `ajv` is a hard runtime dependency of the core execution-authority path
5
+ // (`.`) today, not an optional or lazy one: `coordinator.ts` statically
6
+ // imports `buildValidatorMap` from `./validation/types.js`, which statically
7
+ // imports `createAjvValidator` from `./ajv-validator.js`, which statically
8
+ // imports `ajv` itself - so merely importing the package root loads `ajv`
9
+ // into memory, whether or not a caller ever registers a `schemas` entry.
10
+ // See docs/VALIDATION.md's "Ajv loading" section for why this static-import
11
+ // shape was deliberately kept over a lazy-load alternative.
12
+ //
13
+ // Importing from here instead of the root is only useful if you want an
14
+ // explicit, discoverable Ajv entry point - e.g. to compile one validator up
15
+ // front and reuse it across multiple coordinators/gates. See
16
+ // docs/VALIDATION.md.
17
+ // ---------------------------------------------------------------------------
18
+ export { createAjvValidator } from "./validation/ajv-validator.js";
19
+ //# sourceMappingURL=ajv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ajv.js","sourceRoot":"","sources":["../src/ajv.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,6EAA6E;AAC7E,2EAA2E;AAC3E,0EAA0E;AAC1E,yEAAyE;AACzE,4EAA4E;AAC5E,4DAA4D;AAC5D,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,6DAA6D;AAC7D,sBAAsB;AACtB,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ConformanceFixture, ConformanceFixtureResult, ConformanceSuiteResult } from "./types.js";
2
+ /**
3
+ * @public (Experimental)
4
+ * Runs one conformance fixture against this package's real, unmodified
5
+ * `createToolCallExecutionGate()` - not a reimplementation - and compares
6
+ * the resulting decisions against `fixture.expected`. No network, no
7
+ * provider API key, no model call: `fixture.events` are pushed directly,
8
+ * exactly as a provider adapter would produce them.
9
+ */
10
+ export declare function runToolCallIntegrityFixture(fixture: ConformanceFixture): ConformanceFixtureResult;
11
+ /**
12
+ * @public (Experimental)
13
+ * Runs every fixture in `fixtures` through {@link runToolCallIntegrityFixture}
14
+ * and aggregates the results. Deterministic, synchronous, no I/O of its
15
+ * own - callers own reading fixture files from disk (see
16
+ * `conformance/fixtures/` and `docs/CONFORMANCE.md`).
17
+ */
18
+ export declare function runToolCallIntegritySuite(fixtures: readonly ConformanceFixture[]): ConformanceSuiteResult;
19
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/conformance/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAoBpB;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,kBAAkB,GAAG,wBAAwB,CA6CjG;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,sBAAsB,CAGzG"}
@@ -0,0 +1,78 @@
1
+ import { createToolCallExecutionGate } from "../gate/gate.js";
2
+ function matchDecision(expected, decisions, claimed) {
3
+ for (const decision of decisions) {
4
+ if (claimed.has(decision))
5
+ continue;
6
+ if (expected.name !== undefined && decision.name !== expected.name)
7
+ continue;
8
+ if (expected.toolIndex !== undefined && decision.toolIndex !== expected.toolIndex)
9
+ continue;
10
+ if (expected.name === undefined && expected.toolIndex === undefined) {
11
+ // No correlation key given - fall back to first unclaimed decision,
12
+ // in the order the gate produced them.
13
+ }
14
+ return decision;
15
+ }
16
+ return undefined;
17
+ }
18
+ /**
19
+ * @public (Experimental)
20
+ * Runs one conformance fixture against this package's real, unmodified
21
+ * `createToolCallExecutionGate()` - not a reimplementation - and compares
22
+ * the resulting decisions against `fixture.expected`. No network, no
23
+ * provider API key, no model call: `fixture.events` are pushed directly,
24
+ * exactly as a provider adapter would produce them.
25
+ */
26
+ export function runToolCallIntegrityFixture(fixture) {
27
+ // `schemaVersion`/`profile` are typed as literal `1`/`"normalized-gate"`,
28
+ // but that is a compile-time guarantee only - fixtures are ordinary JSON
29
+ // read off disk (or constructed by a JS caller), so both are checked at
30
+ // runtime here rather than trusted from the type. v1 only; no migration
31
+ // machinery for a schemaVersion that doesn't exist yet.
32
+ if (fixture.schemaVersion !== 1) {
33
+ throw new Error(`prefix-safe-json: fixture "${fixture.id}" declares schemaVersion ${JSON.stringify(fixture.schemaVersion)}, ` +
34
+ `which this runner does not support (only schemaVersion 1 exists). See docs/CONFORMANCE.md.`);
35
+ }
36
+ if (fixture.profile !== "normalized-gate") {
37
+ throw new Error(`prefix-safe-json: fixture "${fixture.id}" declares profile ${JSON.stringify(fixture.profile)}, ` +
38
+ `which this runner does not support (only "normalized-gate" exists in v1). See docs/CONFORMANCE.md.`);
39
+ }
40
+ const gate = createToolCallExecutionGate(undefined, undefined, fixture.toolSchemas);
41
+ for (const event of fixture.events)
42
+ gate.push(event);
43
+ const { decisions } = gate.finish();
44
+ const claimed = new Set();
45
+ const calls = fixture.expected.map((expected) => {
46
+ const actual = matchDecision(expected, decisions, claimed);
47
+ if (!actual) {
48
+ return { expected, pass: false, failureReason: "no matching decision was produced" };
49
+ }
50
+ claimed.add(actual);
51
+ const pass = actual.action === expected.action && actual.reason === expected.reason;
52
+ return {
53
+ expected,
54
+ pass,
55
+ actual: { action: actual.action, reason: actual.reason, name: actual.name, toolIndex: actual.toolIndex },
56
+ ...(pass ? {} : { failureReason: `expected ${expected.action}/${expected.reason}, got ${actual.action}/${actual.reason}` }),
57
+ };
58
+ });
59
+ const unmatchedActualCount = decisions.length - claimed.size;
60
+ return {
61
+ id: fixture.id,
62
+ pass: calls.every((c) => c.pass) && unmatchedActualCount === 0,
63
+ calls,
64
+ unmatchedActualCount,
65
+ };
66
+ }
67
+ /**
68
+ * @public (Experimental)
69
+ * Runs every fixture in `fixtures` through {@link runToolCallIntegrityFixture}
70
+ * and aggregates the results. Deterministic, synchronous, no I/O of its
71
+ * own - callers own reading fixture files from disk (see
72
+ * `conformance/fixtures/` and `docs/CONFORMANCE.md`).
73
+ */
74
+ export function runToolCallIntegritySuite(fixtures) {
75
+ const results = fixtures.map(runToolCallIntegrityFixture);
76
+ return { pass: results.every((r) => r.pass), results };
77
+ }
78
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/conformance/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ import type { ExecutionAction, ExecutionReason } from "../gate/types.js";
2
+ import type { NormalizedToolStreamEvent } from "../coordinator/protocol.js";
3
+ /**
4
+ * @public (Experimental)
5
+ * How a fixture's event sequence relates to what a real provider can
6
+ * actually send:
7
+ *
8
+ * - `"protocol-realistic"`: this exact sequence has been observed, or is
9
+ * directly documented as possible, in a real provider's stream.
10
+ * - `"sdk-representable"`: the installed provider SDK's own types permit
11
+ * constructing and receiving this sequence, but it has not been
12
+ * independently confirmed that a real provider sends it.
13
+ * - `"synthetic-adversarial"`: constructed to stress a specific boundary
14
+ * (a resource limit, a pathological ordering) with no claim that any
15
+ * provider produces it. Still a legitimate conformance case - not every
16
+ * invariant needs a real-world trigger to be worth holding - but never
17
+ * to be read as an observed production bug.
18
+ */
19
+ export type FixtureClassification = "protocol-realistic" | "sdk-representable" | "synthetic-adversarial";
20
+ /**
21
+ * @public (Experimental)
22
+ * Where a fixture came from, for provenance. `classification` is required;
23
+ * everything else is free-form context for a human reader, not consumed by
24
+ * the runner.
25
+ */
26
+ export interface FixtureProvenance {
27
+ readonly classification: FixtureClassification;
28
+ /** e.g. "vercel/ai", "continuedev/continue" - omitted for fixtures with no external origin. */
29
+ readonly project?: string;
30
+ /** An issue/PR/commit URL substantiating this fixture, when one exists. */
31
+ readonly reference?: string;
32
+ /** Free-text note, e.g. why a case is classified the way it is. */
33
+ readonly note?: string;
34
+ }
35
+ /** @public (Experimental) One call's expected execution-authority outcome. */
36
+ export interface ConformanceExpectedCall {
37
+ /** Correlates against the matching decision's own `name`, when present. */
38
+ readonly name?: string;
39
+ /** Correlates against the matching decision's own `toolIndex`, when present. */
40
+ readonly toolIndex?: number;
41
+ readonly action: ExecutionAction;
42
+ readonly reason: ExecutionReason;
43
+ }
44
+ /**
45
+ * @public (Experimental)
46
+ * Which conformance claim a fixture makes, made explicit rather than
47
+ * implied by the corpus's own name. `"normalized-gate"` is the only
48
+ * profile v1 defines:
49
+ *
50
+ * A `"normalized-gate"` fixture tests the coordinator/execution-gate's
51
+ * response to an already-normalized `NormalizedToolStreamEvent` sequence -
52
+ * identity correlation, lifecycle/terminal-state handling, and decision
53
+ * composition, entirely *after* a provider adapter has already produced
54
+ * that sequence. It does NOT test, and must never be described as
55
+ * testing, whether an adapter correctly derives that sequence (or a
56
+ * `provider_diagnostic` within it) from a specific provider's own raw
57
+ * wire format - that is provider-adapter conformance, a distinct,
58
+ * currently unwritten profile. See `docs/CONFORMANCE.md`'s "Provider-
59
+ * adapter conformance — future profile / not covered by normalized-gate
60
+ * v1" section for exactly what this excludes and why.
61
+ */
62
+ export type ConformanceProfile = "normalized-gate";
63
+ /**
64
+ * @public (Experimental)
65
+ * A single, portable tool-call-integrity conformance fixture: a
66
+ * provider-neutral normalized event sequence and the execution-authority
67
+ * decision it must produce. Deliberately expressed in terms of
68
+ * `NormalizedToolStreamEvent` - the same shape every provider adapter in
69
+ * this package already emits - rather than any raw provider wire format,
70
+ * so a fixture never requires understanding a specific SDK's shape to
71
+ * read or reuse. See `docs/CONFORMANCE.md`.
72
+ */
73
+ export interface ConformanceFixture {
74
+ readonly schemaVersion: 1;
75
+ readonly profile: ConformanceProfile;
76
+ readonly id: string;
77
+ readonly description: string;
78
+ readonly provenance: FixtureProvenance;
79
+ readonly events: readonly NormalizedToolStreamEvent[];
80
+ readonly expected: readonly ConformanceExpectedCall[];
81
+ /**
82
+ * Optional per-tool JSON Schema (draft-07), keyed by tool name, for
83
+ * fixtures that specifically exercise schema-validity outcomes (e.g.
84
+ * `reason: "schema_invalid"`). Deliberately raw JSON Schema only, not a
85
+ * `ToolInputValidator` - fixtures must stay JSON-serializable. See
86
+ * `docs/VALIDATION.md` for how validators plug in outside fixtures.
87
+ */
88
+ readonly toolSchemas?: Record<string, object>;
89
+ }
90
+ /** @public (Experimental) One expected call matched (or not) against the real decisions produced. */
91
+ export interface ConformanceCallResult {
92
+ readonly expected: ConformanceExpectedCall;
93
+ readonly pass: boolean;
94
+ readonly actual?: {
95
+ readonly action: ExecutionAction;
96
+ readonly reason: ExecutionReason;
97
+ readonly name?: string;
98
+ readonly toolIndex?: number;
99
+ };
100
+ readonly failureReason?: string;
101
+ }
102
+ /** @public (Experimental) The result of running one fixture. */
103
+ export interface ConformanceFixtureResult {
104
+ readonly id: string;
105
+ readonly pass: boolean;
106
+ readonly calls: readonly ConformanceCallResult[];
107
+ /** Present when the fixture produced more or fewer decisions than `expected` entries. */
108
+ readonly unmatchedActualCount: number;
109
+ }
110
+ /** @public (Experimental) The result of running a whole fixture suite. */
111
+ export interface ConformanceSuiteResult {
112
+ readonly pass: boolean;
113
+ readonly results: readonly ConformanceFixtureResult[];
114
+ }
115
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/conformance/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,qBAAqB,GAAG,oBAAoB,GAAG,mBAAmB,GAAG,uBAAuB,CAAC;AAEzG;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC;IAC/C,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,8EAA8E;AAC9E,MAAM,WAAW,uBAAuB;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACtD;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED,qGAAqG;AACrG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9I,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,gEAAgE;AAChE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,yFAAyF;IACzF,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC;AAED,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,wBAAwB,EAAE,CAAC;CACvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/conformance/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export { runToolCallIntegrityFixture, runToolCallIntegritySuite } from "./conformance/runner.js";
2
+ export type { ConformanceFixture, ConformanceFixtureResult, ConformanceSuiteResult, ConformanceCallResult, ConformanceExpectedCall, FixtureProvenance, FixtureClassification, } from "./conformance/types.js";
3
+ //# sourceMappingURL=conformance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.d.ts","sourceRoot":"","sources":["../src/conformance.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACjG,YAAY,EACV,kBAAkB,EAClB,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,11 @@
1
+ // ---------------------------------------------------------------------------
2
+ // prefix-safe-json/conformance — Tool Call Integrity conformance runner.
3
+ //
4
+ // Runs portable, provider-neutral fixtures (see conformance/fixtures/ and
5
+ // docs/CONFORMANCE.md) against this package's own real execution-authority
6
+ // gate. Useful even to projects that do not install this package at
7
+ // runtime: the fixture format and expected outcomes are meaningful on
8
+ // their own, against any implementation of this problem class.
9
+ // ---------------------------------------------------------------------------
10
+ export { runToolCallIntegrityFixture, runToolCallIntegritySuite } from "./conformance/runner.js";
11
+ //# sourceMappingURL=conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.js","sourceRoot":"","sources":["../src/conformance.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yEAAyE;AACzE,EAAE;AACF,0EAA0E;AAC1E,2EAA2E;AAC3E,oEAAoE;AACpE,sEAAsE;AACtE,+DAA+D;AAC/D,8EAA8E;AAE9E,OAAO,EAAE,2BAA2B,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { ParserOptions } from "../types.js";
2
+ import { type ToolInputValidator } from "../validation/types.js";
2
3
  import { NormalizedToolStreamEvent, StreamEndReason } from "./protocol.js";
3
- import { ToolCallStreamCoordinator, ToolCallCoordinatorSnapshot, ToolCallCoordinatorEvent, ToolCallCoordinatorFinalResult, CoordinatorPushResult, CoordinatorLimits, JsonSchemaLike } from "./types.js";
4
+ import { ToolCallStreamCoordinator, ToolCallCoordinatorSnapshot, ToolCallCoordinatorEvent, ToolCallCoordinatorFinalResult, CoordinatorPushResult, CoordinatorLimits } from "./types.js";
4
5
  export declare class DefaultToolCallStreamCoordinator implements ToolCallStreamCoordinator {
5
6
  private readonly limits;
6
7
  private readonly parserOptions;
@@ -21,17 +22,30 @@ export declare class DefaultToolCallStreamCoordinator implements ToolCallStreamC
21
22
  * underlying per-call IncrementalJsonParser (limits, repairs). Previously
22
23
  * there was no way to pass the latter through at all; every parser the
23
24
  * coordinator created was hardcoded to library defaults.
24
- * @param toolSchemas Optional JSON Schema (draft-07) per tool name. When a
25
- * call for a registered tool reaches outcome "complete", its
26
- * `stableValue` is validated against the schema. Structural
27
- * prefix-safety (no truncated/fabricated values) and schema validity
28
- * are independent concerns — a value can be genuinely complete and
29
- * still not match what the tool declared it needs (wrong type, missing
25
+ * @param toolSchemas Optional raw JSON Schema (draft-07) per tool name,
26
+ * compiled internally through `prefix-safe-json/ajv`'s
27
+ * `createAjvValidator()`. This is the pre-0.5 option, unchanged in
28
+ * shape - existing callers need no code change.
29
+ * @param validators Optional {@link ToolInputValidator} per tool name -
30
+ * additive, 0.5.0+. Use this to plug in Zod, TypeBox, Valibot, a
31
+ * Standard Schema-compliant validator, or a hand-written check, with no
32
+ * forced dependency on this package's own validation stack. A tool name
33
+ * present in both `toolSchemas` and `validators` is a construction-time
34
+ * error - there is no structural discrimination or silent precedence
35
+ * between the two; each tool's validation comes from exactly one
36
+ * explicit source. See `docs/VALIDATION.md`.
37
+ *
38
+ * When a call for a registered tool reaches outcome "complete", its
39
+ * `stableValue` is validated. Structural prefix-safety (no
40
+ * truncated/fabricated values) and validator-reported validity are
41
+ * independent concerns — a value can be genuinely complete and still
42
+ * not match what the tool declared it needs (wrong type, missing
30
43
  * required field the model never provided at all). Both must hold for
31
- * `executable` to be true. Schemas are compiled eagerly here so a
32
- * malformed schema fails fast at construction, not mid-stream.
44
+ * `executable` to be true. Entries are resolved eagerly here so a
45
+ * malformed schema (or a misconfigured validator's constructor-time
46
+ * setup) fails fast at construction, not mid-stream.
33
47
  */
34
- constructor(limits?: Partial<CoordinatorLimits>, parserOptions?: ParserOptions, toolSchemas?: Record<string, JsonSchemaLike>);
48
+ constructor(limits?: Partial<CoordinatorLimits>, parserOptions?: ParserOptions, toolSchemas?: Record<string, object>, validators?: Record<string, ToolInputValidator>);
35
49
  push(event: NormalizedToolStreamEvent): CoordinatorPushResult;
36
50
  private handleStart;
37
51
  private handleIdentity;
@@ -51,5 +65,5 @@ export declare class DefaultToolCallStreamCoordinator implements ToolCallStreamC
51
65
  providerReason?: string;
52
66
  }): ToolCallCoordinatorFinalResult;
53
67
  }
54
- export declare function createToolCallStreamCoordinator(limits?: Partial<CoordinatorLimits>, parserOptions?: ParserOptions, toolSchemas?: Record<string, JsonSchemaLike>): ToolCallStreamCoordinator;
68
+ export declare function createToolCallStreamCoordinator(limits?: Partial<CoordinatorLimits>, parserOptions?: ParserOptions, toolSchemas?: Record<string, object>, validators?: Record<string, ToolInputValidator>): ToolCallStreamCoordinator;
55
69
  //# sourceMappingURL=coordinator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../src/coordinator/coordinator.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAE,yBAAyB,EAAgB,eAAe,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,wBAAwB,EACxB,8BAA8B,EAC9B,qBAAqB,EACrB,iBAAiB,EAIjB,cAAc,EACf,MAAM,YAAY,CAAC;AA4CpB,qBAAa,gCAAiC,YAAW,yBAAyB;IAChF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAC/D,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,qBAAqB,CAAkC;IAC/D,OAAO,CAAC,0BAA0B,CAAmD;IACrF,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAC,CAAkB;IAEzC;;;;;;;;;;;;;;;OAeG;gBAED,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC;IAa9C,IAAI,CAAC,KAAK,EAAE,yBAAyB,GAAG,qBAAqB;IAuE7D,OAAO,CAAC,WAAW;IAiEnB,OAAO,CAAC,cAAc;IAgDtB,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,UAAU;IAoElB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,aAAa;IASrB,QAAQ,IAAI,2BAA2B;IAmBvC,WAAW,IAAI,SAAS,wBAAwB,EAAE;IAMlD,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,8BAA8B;CAcrG;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAC3C,yBAAyB,CAE3B"}
1
+ {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../src/coordinator/coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAqB,KAAK,kBAAkB,EAA6B,MAAM,wBAAwB,CAAC;AAC/G,OAAO,EAAE,yBAAyB,EAAgB,eAAe,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EACL,yBAAyB,EACzB,2BAA2B,EAC3B,wBAAwB,EACxB,8BAA8B,EAC9B,qBAAqB,EACrB,iBAAiB,EAIlB,MAAM,YAAY,CAAC;AAyFpB,qBAAa,gCAAiC,YAAW,yBAAyB;IAChF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkC;IACjE,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,qBAAqB,CAAkC;IAC/D,OAAO,CAAC,0BAA0B,CAAmD;IACrF,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAC,CAAkB;IAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;gBAED,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC;IAOjD,IAAI,CAAC,KAAK,EAAE,yBAAyB,GAAG,qBAAqB;IAuE7D,OAAO,CAAC,WAAW;IAiEnB,OAAO,CAAC,cAAc;IAgDtB,OAAO,CAAC,eAAe;IAqCvB,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,UAAU;IAgIlB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,wBAAwB;IAwChC,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,OAAO;IAcf,OAAO,CAAC,aAAa;IASrB,QAAQ,IAAI,2BAA2B;IAmBvC,WAAW,IAAI,SAAS,wBAAwB,EAAE;IAMlD,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,8BAA8B;CAcrG;AAED,wBAAgB,+BAA+B,CAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAC9C,yBAAyB,CAE3B"}
@@ -1,6 +1,6 @@
1
- import { Ajv } from "ajv";
2
1
  import { createParser } from "../parser.js";
3
- import { DEFAULT_COORDINATOR_LIMITS } from "./types.js";
2
+ import { buildValidatorMap } from "../validation/types.js";
3
+ import { DEFAULT_COORDINATOR_LIMITS, } from "./types.js";
4
4
  import { AUTHORITY_PROTOCOL_VIOLATION_CODES, DUPLICATE_TOOL_CALL_START_DIAGNOSTIC_CODE, SDK_EXECUTION_OBSERVED_DIAGNOSTIC_CODE, SDK_EXECUTION_ERROR_DIAGNOSTIC_CODE, EVENT_AFTER_STREAM_END_DIAGNOSTIC_CODE, TERMINAL_REASON_CONFLICT_DIAGNOSTIC_CODE, } from "./diagnostic-codes.js";
5
5
  // A `provider_diagnostic` carrying either code is proof that a provider SDK's
6
6
  // own tool loop already invoked this call's `execute` callback (see the two
@@ -12,6 +12,53 @@ const SDK_EXECUTION_OBSERVED_CODES = new Set([
12
12
  SDK_EXECUTION_OBSERVED_DIAGNOSTIC_CODE,
13
13
  SDK_EXECUTION_ERROR_DIAGNOSTIC_CODE,
14
14
  ]);
15
+ /**
16
+ * @internal
17
+ * Runtime shape check for what `validator.validate()` actually returned.
18
+ * `ToolInputValidator` is a TypeScript interface, not a runtime guarantee -
19
+ * `result && result.valid === true/false` alone is not a safe discriminator:
20
+ * an array is `typeof "object"` and could carry an incidental `.valid`
21
+ * property; a function can too (`fn.valid = true` is legal JS), and
22
+ * `typeof fn !== "object"` is what actually excludes it here. Also rejects
23
+ * internally contradictory shapes a conforming implementation never
24
+ * produces: `valid: true` alongside a defined `errors` field, or
25
+ * `valid: false` with an `errors` field that is anything other than absent
26
+ * or a `string[]` - a bare string or a non-string array would otherwise
27
+ * reach `(result.errors ?? []).join("; ")` below, which throws on a string
28
+ * (no `.join`) and silently stringifies non-string elements on an array.
29
+ */
30
+ function isWellFormedValidationResult(result) {
31
+ if (typeof result !== "object" || result === null || Array.isArray(result))
32
+ return false;
33
+ const value = result;
34
+ if (value.valid === true)
35
+ return value.errors === undefined;
36
+ if (value.valid === false) {
37
+ if (value.errors === undefined)
38
+ return true;
39
+ return Array.isArray(value.errors) && value.errors.every((e) => typeof e === "string");
40
+ }
41
+ return false;
42
+ }
43
+ /**
44
+ * @internal
45
+ * Describes an arbitrary, possibly-malformed validator result for a
46
+ * diagnostic message without ever throwing. A circular object or a value
47
+ * containing a BigInt both make `JSON.stringify` throw - a diagnostic
48
+ * describing *why* a result was rejected must never itself crash the
49
+ * stream it exists to fail closed.
50
+ */
51
+ function describeMalformedResult(value) {
52
+ if (typeof value === "object") {
53
+ try {
54
+ return JSON.stringify(value);
55
+ }
56
+ catch {
57
+ return Object.prototype.toString.call(value);
58
+ }
59
+ }
60
+ return `${typeof value} ${String(value)}`;
61
+ }
15
62
  class CoordinatorCallState {
16
63
  internalId;
17
64
  provider;
@@ -49,26 +96,33 @@ export class DefaultToolCallStreamCoordinator {
49
96
  * underlying per-call IncrementalJsonParser (limits, repairs). Previously
50
97
  * there was no way to pass the latter through at all; every parser the
51
98
  * coordinator created was hardcoded to library defaults.
52
- * @param toolSchemas Optional JSON Schema (draft-07) per tool name. When a
53
- * call for a registered tool reaches outcome "complete", its
54
- * `stableValue` is validated against the schema. Structural
55
- * prefix-safety (no truncated/fabricated values) and schema validity
56
- * are independent concerns — a value can be genuinely complete and
57
- * still not match what the tool declared it needs (wrong type, missing
99
+ * @param toolSchemas Optional raw JSON Schema (draft-07) per tool name,
100
+ * compiled internally through `prefix-safe-json/ajv`'s
101
+ * `createAjvValidator()`. This is the pre-0.5 option, unchanged in
102
+ * shape - existing callers need no code change.
103
+ * @param validators Optional {@link ToolInputValidator} per tool name -
104
+ * additive, 0.5.0+. Use this to plug in Zod, TypeBox, Valibot, a
105
+ * Standard Schema-compliant validator, or a hand-written check, with no
106
+ * forced dependency on this package's own validation stack. A tool name
107
+ * present in both `toolSchemas` and `validators` is a construction-time
108
+ * error - there is no structural discrimination or silent precedence
109
+ * between the two; each tool's validation comes from exactly one
110
+ * explicit source. See `docs/VALIDATION.md`.
111
+ *
112
+ * When a call for a registered tool reaches outcome "complete", its
113
+ * `stableValue` is validated. Structural prefix-safety (no
114
+ * truncated/fabricated values) and validator-reported validity are
115
+ * independent concerns — a value can be genuinely complete and still
116
+ * not match what the tool declared it needs (wrong type, missing
58
117
  * required field the model never provided at all). Both must hold for
59
- * `executable` to be true. Schemas are compiled eagerly here so a
60
- * malformed schema fails fast at construction, not mid-stream.
118
+ * `executable` to be true. Entries are resolved eagerly here so a
119
+ * malformed schema (or a misconfigured validator's constructor-time
120
+ * setup) fails fast at construction, not mid-stream.
61
121
  */
62
- constructor(limits, parserOptions, toolSchemas) {
122
+ constructor(limits, parserOptions, toolSchemas, validators) {
63
123
  this.limits = { ...DEFAULT_COORDINATOR_LIMITS, ...limits };
64
124
  this.parserOptions = parserOptions;
65
- this.toolValidators = new Map();
66
- if (toolSchemas) {
67
- const ajv = new Ajv({ allErrors: true, strict: false });
68
- for (const [toolName, schema] of Object.entries(toolSchemas)) {
69
- this.toolValidators.set(toolName, ajv.compile(schema));
70
- }
71
- }
125
+ this.toolValidators = buildValidatorMap(toolSchemas, validators);
72
126
  }
73
127
  push(event) {
74
128
  if (this.isFinished) {
@@ -365,14 +419,53 @@ export class DefaultToolCallStreamCoordinator {
365
419
  // Schema validation is a separate concern from structural
366
420
  // prefix-safety: only meaningful once the value is genuinely complete,
367
421
  // and only for tools with a registered schema.
422
+ //
423
+ // Stryker disable next-line ConditionalExpression: `call.name !==
424
+ // undefined` cannot observably differ from `true` here - the outcome
425
+ // assignment above already forces outcome to "invalid" whenever
426
+ // call.name is undefined, so reaching outcome === "complete" already
427
+ // guarantees call.name is defined. This conjunct exists for TypeScript's
428
+ // type narrowing (removing it is a real compile error at
429
+ // `this.toolValidators.get(call.name)` below, verified directly), not
430
+ // as an independently reachable runtime branch.
368
431
  if (outcome === "complete" && call.name !== undefined) {
369
432
  const validator = this.toolValidators.get(call.name);
370
433
  if (validator) {
371
- call.schemaValid = validator(res.stableValue) === true;
372
- if (!call.schemaValid) {
373
- const errors = (validator.errors ?? [])
374
- .map((e) => `${e.instancePath || "<root>"} ${e.message ?? ""}`.trim())
375
- .join("; ");
434
+ // A validator that throws (e.g. a Standard Schema adapter refusing
435
+ // an async validator - see docs/VALIDATION.md) fails this one call
436
+ // closed rather than aborting the whole stream: caught here, not
437
+ // left to propagate out of push()/finish() and take every other
438
+ // in-flight call down with it.
439
+ let didThrow = false;
440
+ let thrown;
441
+ // Deliberately `unknown`, not `ToolValidationResult` - the interface
442
+ // declares validate()'s return type, but a plain-JS implementation
443
+ // (or an `as`-cast) can hand back anything at runtime, and this
444
+ // value is trusted no further than any other external input until
445
+ // isWellFormedValidationResult() below actually proves the shape.
446
+ let result;
447
+ try {
448
+ result = validator.validate(res.stableValue);
449
+ }
450
+ catch (error) {
451
+ didThrow = true;
452
+ thrown = error;
453
+ }
454
+ if (didThrow) {
455
+ call.schemaValid = false;
456
+ this.addDiagnostic({
457
+ code: "E_SCHEMA_VALIDATION_FAILED",
458
+ severity: "error",
459
+ internalId: call.internalId,
460
+ message: `Validator for "${call.name}" threw instead of returning a result: ${thrown instanceof Error ? thrown.message : String(thrown)}`,
461
+ });
462
+ }
463
+ else if (isWellFormedValidationResult(result) && result.valid === true) {
464
+ call.schemaValid = true;
465
+ }
466
+ else if (isWellFormedValidationResult(result) && result.valid === false) {
467
+ call.schemaValid = false;
468
+ const errors = (result.errors ?? []).join("; ");
376
469
  this.addDiagnostic({
377
470
  code: "E_SCHEMA_VALIDATION_FAILED",
378
471
  severity: "error",
@@ -380,6 +473,28 @@ export class DefaultToolCallStreamCoordinator {
380
473
  message: `Tool call arguments for "${call.name}" do not match its schema: ${errors || "unknown validation error"}`,
381
474
  });
382
475
  }
476
+ else {
477
+ // A conforming ToolInputValidator never reaches here - validate()
478
+ // is typed to return exactly {valid: true} or {valid: false,
479
+ // ...}. This branch exists for every shape the type system cannot
480
+ // prevent at runtime: undefined/null, an array, a function
481
+ // (including one with an incidental `.valid` property), a
482
+ // non-boolean `valid`, `valid: true` alongside a defined `errors`
483
+ // field, or `valid: false` with `errors` that isn't absent or a
484
+ // plain `string[]` - see isWellFormedValidationResult() above.
485
+ // Fails closed the same as a thrown error - the alternative
486
+ // (falling through with schemaValid left unset) would let
487
+ // executable's `call.schemaValid !== false` check treat an
488
+ // unproven, malformed validator result as if no validator had run
489
+ // at all.
490
+ call.schemaValid = false;
491
+ this.addDiagnostic({
492
+ code: "E_SCHEMA_VALIDATION_FAILED",
493
+ severity: "error",
494
+ internalId: call.internalId,
495
+ message: `Validator for "${call.name}" returned a malformed result instead of {valid: true} or {valid: false, ...}: ${describeMalformedResult(result)}`,
496
+ });
497
+ }
383
498
  }
384
499
  }
385
500
  // Determine executable according to Executable Policy — structural
@@ -515,7 +630,7 @@ export class DefaultToolCallStreamCoordinator {
515
630
  };
516
631
  }
517
632
  }
518
- export function createToolCallStreamCoordinator(limits, parserOptions, toolSchemas) {
519
- return new DefaultToolCallStreamCoordinator(limits, parserOptions, toolSchemas);
633
+ export function createToolCallStreamCoordinator(limits, parserOptions, toolSchemas, validators) {
634
+ return new DefaultToolCallStreamCoordinator(limits, parserOptions, toolSchemas, validators);
520
635
  }
521
636
  //# sourceMappingURL=coordinator.js.map