prefix-safe-json 0.0.1-alpha.3 → 0.0.1-alpha.4
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 +70 -31
- package/dist/gate/decide.d.ts +7 -0
- package/dist/gate/decide.d.ts.map +1 -1
- package/dist/gate/decide.js +11 -0
- package/dist/gate/decide.js.map +1 -1
- package/dist/gate/gate.d.ts.map +1 -1
- package/dist/gate/gate.js +18 -2
- package/dist/gate/gate.js.map +1 -1
- package/dist/gate/types.d.ts +63 -1
- package/dist/gate/types.d.ts.map +1 -1
- package/dist/guard/ai-sdk.d.ts +48 -0
- package/dist/guard/ai-sdk.d.ts.map +1 -0
- package/dist/guard/ai-sdk.js +51 -0
- package/dist/guard/ai-sdk.js.map +1 -0
- package/dist/guard/provider-guard.d.ts +4 -0
- package/dist/guard/provider-guard.d.ts.map +1 -0
- package/dist/guard/provider-guard.js +38 -0
- package/dist/guard/provider-guard.js.map +1 -0
- package/dist/guard/types.d.ts +48 -0
- package/dist/guard/types.d.ts.map +1 -0
- package/dist/guard/types.js +2 -0
- package/dist/guard/types.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lexer/scanner.d.ts +0 -2
- package/dist/lexer/scanner.d.ts.map +1 -1
- package/dist/lexer/scanner.js +3 -21
- package/dist/lexer/scanner.js.map +1 -1
- package/dist/lexer/states.d.ts +0 -4
- package/dist/lexer/states.d.ts.map +1 -1
- package/dist/lexer/states.js +0 -4
- package/dist/lexer/states.js.map +1 -1
- package/dist/parser.js +26 -53
- package/dist/parser.js.map +1 -1
- package/dist/providers/ai-sdk.d.ts +5 -1
- package/dist/providers/ai-sdk.d.ts.map +1 -1
- package/dist/providers/ai-sdk.js +21 -0
- package/dist/providers/ai-sdk.js.map +1 -1
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +65 -7
- package/dist/providers/openai.js.map +1 -1
- package/dist/semantic/snapshot.d.ts +11 -4
- package/dist/semantic/snapshot.d.ts.map +1 -1
- package/dist/semantic/snapshot.js +11 -9
- package/dist/semantic/snapshot.js.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -34,43 +34,74 @@ model output truncated
|
|
|
34
34
|
|
|
35
35
|
*(Real terminal output from [`examples/anthropic-truncation-safety.mjs`](examples/anthropic-truncation-safety.mjs) — not staged. Same script CI runs on every push.)*
|
|
36
36
|
|
|
37
|
-
##
|
|
38
|
-
|
|
39
|
-
The highest-level API answers one question per tool call — is it safe to
|
|
40
|
-
execute right now — without requiring you to know anything about parser
|
|
41
|
-
state, coordinator events, or JSON Pointers:
|
|
37
|
+
## From stream to safe execution, in about 10 lines
|
|
42
38
|
|
|
43
39
|
```typescript
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
import { createAiSdkExecutionGuard } from "prefix-safe-json";
|
|
41
|
+
|
|
42
|
+
const guard = createAiSdkExecutionGuard({
|
|
43
|
+
schemas: {
|
|
44
|
+
write_file: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: { path: { type: "string" }, content: { type: "string" } },
|
|
47
|
+
required: ["path", "content"],
|
|
48
|
+
},
|
|
51
49
|
},
|
|
52
50
|
});
|
|
53
|
-
const adapter = new AnthropicStreamAdapter();
|
|
54
51
|
|
|
55
|
-
for (const
|
|
56
|
-
for (const normalized of adapter.push(rawEvent)) gate.push(normalized);
|
|
57
|
-
}
|
|
52
|
+
for await (const part of result.fullStream) guard.push(part);
|
|
58
53
|
|
|
59
|
-
for (const decision of
|
|
60
|
-
if (decision.action === "execute")
|
|
61
|
-
await tools[decision.name](decision.value);
|
|
62
|
-
} else {
|
|
63
|
-
console.warn(`${decision.name}: ${decision.action} (${decision.reason})`); // "retry" or "reject"
|
|
64
|
-
}
|
|
54
|
+
for (const decision of guard.finish().decisions) {
|
|
55
|
+
if (decision.action === "execute") await tools[decision.name](decision.value);
|
|
65
56
|
}
|
|
66
57
|
```
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
(`ai`
|
|
71
|
-
[`examples/ai-sdk-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
`createAiSdkExecutionGuard()` is a drop-in guard for the [Vercel AI
|
|
60
|
+
SDK](https://ai-sdk.dev)'s `fullStream` — not a dependency of this library
|
|
61
|
+
(no `ai` import, no runtime version lock; see
|
|
62
|
+
[`examples/ai-sdk-guard.mjs`](examples/ai-sdk-guard.mjs)). It's a thin
|
|
63
|
+
composition over the same lower-level adapter + gate API this library has
|
|
64
|
+
always exposed — nothing about the decision logic changes based on which one
|
|
65
|
+
you call.
|
|
66
|
+
|
|
67
|
+
**What the decisions mean**
|
|
68
|
+
|
|
69
|
+
Every tool call gets exactly one verdict:
|
|
70
|
+
|
|
71
|
+
- **`execute`** — complete, unfabricated, and (if a schema is registered)
|
|
72
|
+
schema-valid. `decision.value` is safe to pass to your tool.
|
|
73
|
+
- **`retry`** — nothing is wrong with what arrived; there just isn't a
|
|
74
|
+
trustworthy complete value yet. Continue generation.
|
|
75
|
+
- **`reject`** — the data itself is the problem (malformed JSON, a schema
|
|
76
|
+
mismatch, a resource limit, a provider error, a content-policy
|
|
77
|
+
termination). Retrying the same input won't help.
|
|
78
|
+
|
|
79
|
+
**Why the model's finish reason matters as much as the JSON's shape**
|
|
80
|
+
|
|
81
|
+
A tool call cut short by a token limit, a provider error, or a content
|
|
82
|
+
filter can still contain syntactically complete, schema-valid JSON — the
|
|
83
|
+
model was simply stopped before finishing the *rest* of its response, not
|
|
84
|
+
necessarily mid-argument. Executing on JSON shape alone means trusting data
|
|
85
|
+
nobody, including the model, confirmed as final. This is exactly the failure
|
|
86
|
+
mode independently reported and reproduced in
|
|
87
|
+
[vercel/ai#19063](https://github.com/vercel/ai/issues/19063): tool calls
|
|
88
|
+
executing regardless of an unsafe finish reason, across AI SDK v5, v6, and
|
|
89
|
+
v7, with fixes landing across all three release lines (v7's in `ai@7.0.70`).
|
|
90
|
+
`prefix-safe-json` does not patch or depend on that fix — it's a
|
|
91
|
+
provider-independent guard that requires positive proof a stream ended
|
|
92
|
+
safely before treating any tool call as final, regardless of which SDK,
|
|
93
|
+
provider, or framework hands it the data. See
|
|
94
|
+
`test/guard/ai-sdk-compatibility.test.ts` for the cross-version test
|
|
95
|
+
evidence.
|
|
96
|
+
|
|
97
|
+
**Supported providers**: OpenAI (legacy `function_call` and Responses API),
|
|
98
|
+
Anthropic, Gemini, OpenRouter, generic OpenAI-compatible endpoints, and the
|
|
99
|
+
Vercel AI SDK. High-level guards currently ship for the AI SDK; every
|
|
100
|
+
provider has a public low-level adapter (see below) that composes with
|
|
101
|
+
`createToolCallExecutionGate()` the same way.
|
|
102
|
+
|
|
103
|
+
Full semantics, the decision table, provider finish-reason mapping, and the
|
|
104
|
+
high-level guard API: [`docs/EXECUTION_GATE.md`](docs/EXECUTION_GATE.md).
|
|
74
105
|
|
|
75
106
|
## The lower-level problem this is built on
|
|
76
107
|
|
|
@@ -197,6 +228,7 @@ for the full list, including known internal limitations.
|
|
|
197
228
|
- Provider stream adapters for OpenAI (legacy `function_call` and Responses API), Anthropic, Gemini, OpenRouter, generic OpenAI-compatible endpoints, and the Vercel AI SDK, plus a coordinator for tracking multiple concurrent tool-call streams
|
|
198
229
|
- Optional per-tool JSON Schema validation on the coordinator (see below) — a value can be structurally complete and still not match what a tool declared it needs
|
|
199
230
|
- `createToolCallExecutionGate()` — a fail-closed `execute` / `retry` / `reject` decision per tool call, built on top of the coordinator (see [`docs/EXECUTION_GATE.md`](docs/EXECUTION_GATE.md))
|
|
231
|
+
- `createAiSdkExecutionGuard()` — a drop-in high-level guard for the Vercel AI SDK's `fullStream`, composing a provider adapter with the execution gate; every `ExecutionDecision` also carries an `evidence` object explaining why (see [`docs/EXECUTION_GATE.md`](docs/EXECUTION_GATE.md#high-level-guards))
|
|
200
232
|
|
|
201
233
|
### Not Yet Implemented
|
|
202
234
|
|
|
@@ -241,8 +273,11 @@ npm install prefix-safe-json
|
|
|
241
273
|
|
|
242
274
|
## Low-level API: Quick Start
|
|
243
275
|
|
|
244
|
-
The
|
|
245
|
-
|
|
276
|
+
The AI SDK guard (above) covers the common case. For other providers, or
|
|
277
|
+
when you need the adapter/gate instance directly, `createToolCallExecutionGate()`
|
|
278
|
+
and the provider adapters (`AnthropicStreamAdapter`, `OpenAIStreamAdapter`,
|
|
279
|
+
etc.) are equally public — see [`docs/EXECUTION_GATE.md`](docs/EXECUTION_GATE.md#example).
|
|
280
|
+
The `createParser()` API both are built on is also fully public, for anyone who
|
|
246
281
|
needs direct access to parser state:
|
|
247
282
|
|
|
248
283
|
```typescript
|
|
@@ -280,10 +315,14 @@ and asserts the truncated call is never reported executable while a
|
|
|
280
315
|
genuinely complete one is. This example runs in CI on every push, so it
|
|
281
316
|
can't silently rot into a stale claim.
|
|
282
317
|
|
|
283
|
-
For the same demonstration through the
|
|
318
|
+
For the same demonstration through the low-level execution gate and the
|
|
284
319
|
[Vercel AI SDK](https://ai-sdk.dev)'s `fullStream` shape instead, see
|
|
285
320
|
[`examples/ai-sdk-execution-gate.mjs`](examples/ai-sdk-execution-gate.mjs)
|
|
286
321
|
(`node examples/ai-sdk-execution-gate.mjs` after `pnpm run build`) — also
|
|
322
|
+
run in CI on every push. For the drop-in `createAiSdkExecutionGuard()` shown
|
|
323
|
+
at the top of this README, see
|
|
324
|
+
[`examples/ai-sdk-guard.mjs`](examples/ai-sdk-guard.mjs)
|
|
325
|
+
(`node examples/ai-sdk-guard.mjs`) — same scenarios, same guarantees, also
|
|
287
326
|
run in CI on every push.
|
|
288
327
|
|
|
289
328
|
## License
|
package/dist/gate/decide.d.ts
CHANGED
|
@@ -9,6 +9,13 @@ export interface DecisionContext {
|
|
|
9
9
|
* rather than per-call state.
|
|
10
10
|
*/
|
|
11
11
|
readonly streamEndReason: StreamEndReason;
|
|
12
|
+
/**
|
|
13
|
+
* The provider's own pre-normalization reason string for the stream-end
|
|
14
|
+
* event, when one was given (`ProviderStreamEndEvent.providerReason`).
|
|
15
|
+
* Observability only, mirrored onto `DecisionEvidence.providerReason` -
|
|
16
|
+
* never consulted by the decision logic itself.
|
|
17
|
+
*/
|
|
18
|
+
readonly streamEndProviderReason?: string;
|
|
12
19
|
/**
|
|
13
20
|
* The coordinator's full, unfiltered diagnostics list (`snapshot().diagnostics`).
|
|
14
21
|
* `decideExecution` splits this into per-call (`internalId` matches) and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../../src/gate/decide.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,
|
|
1
|
+
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../../src/gate/decide.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAoB,eAAe,EAAmB,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEvG,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAE1C;;;;;OAKG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAE1C;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACxD;AAeD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,eAAe,GACnB,eAAe,GAAG,qBAAqB,CA+HzC"}
|
package/dist/gate/decide.js
CHANGED
|
@@ -36,6 +36,16 @@ export function decideExecution(call, ctx) {
|
|
|
36
36
|
...globalCoordinatorDiagnostics,
|
|
37
37
|
...callCoordinatorDiagnostics,
|
|
38
38
|
];
|
|
39
|
+
const evidence = {
|
|
40
|
+
provider: call.provider,
|
|
41
|
+
providerReason: ctx.streamEndProviderReason,
|
|
42
|
+
streamEndReason: ctx.streamEndReason,
|
|
43
|
+
terminalConfirmed: ctx.streamEndReason !== "unknown",
|
|
44
|
+
structurallyComplete: call.parser.rootComplete,
|
|
45
|
+
parserExecutable: call.parser.executable,
|
|
46
|
+
schemaValid: call.schemaValid,
|
|
47
|
+
receivedBytes: call.parser.receivedBytes,
|
|
48
|
+
};
|
|
39
49
|
const common = {
|
|
40
50
|
internalId: call.internalId,
|
|
41
51
|
toolCallId: call.toolCallId,
|
|
@@ -44,6 +54,7 @@ export function decideExecution(call, ctx) {
|
|
|
44
54
|
stableValue: call.parser.stableValue,
|
|
45
55
|
parserDiagnostics,
|
|
46
56
|
coordinatorDiagnostics,
|
|
57
|
+
evidence,
|
|
47
58
|
};
|
|
48
59
|
function reject(reason) {
|
|
49
60
|
return { ...common, action: "reject", executable: false, reason };
|
package/dist/gate/decide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide.js","sourceRoot":"","sources":["../../src/gate/decide.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mCAAmC;AACnC,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,qEAAqE;AACrE,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AACzE,4EAA4E;AAC5E,yEAAyE;AACzE,yEAAyE;AACzE,0EAA0E;AAC1E,4EAA4E;AAC5E,MAAM;AACN,8EAA8E;AAE9E,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGzD,OAAO,EAAE,gCAAgC,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"decide.js","sourceRoot":"","sources":["../../src/gate/decide.ts"],"names":[],"mappings":"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"}
|
package/dist/gate/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,iBAAiB,EAEjB,cAAc,EAGf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAEL,qBAAqB,EAEtB,MAAM,YAAY,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,iBAAiB,EAEjB,cAAc,EAGf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAEL,qBAAqB,EAEtB,MAAM,YAAY,CAAC;AA+EpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACnC,aAAa,CAAC,EAAE,aAAa,EAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAC3C,qBAAqB,CAEvB"}
|
package/dist/gate/gate.js
CHANGED
|
@@ -14,8 +14,11 @@ class DefaultToolCallExecutionGate {
|
|
|
14
14
|
// Mirrors the coordinator's own "first provider_stream_end (from push(),
|
|
15
15
|
// or from finish()'s meta if none arrived) wins" rule - see push()/finish()
|
|
16
16
|
// below and DefaultToolCallStreamCoordinator.finish()'s matching
|
|
17
|
-
// `if (!this.isFinished)` guard.
|
|
17
|
+
// `if (!this.isFinished)` guard. streamEndProviderReason is captured
|
|
18
|
+
// alongside streamEndReason purely for DecisionEvidence.providerReason -
|
|
19
|
+
// it is never read by decideExecution()'s own logic.
|
|
18
20
|
streamEndReason;
|
|
21
|
+
streamEndProviderReason;
|
|
19
22
|
streamEndCaptured = false;
|
|
20
23
|
constructor(limits, parserOptions, toolSchemas) {
|
|
21
24
|
this.coordinator = createToolCallStreamCoordinator(limits, parserOptions, toolSchemas);
|
|
@@ -24,6 +27,7 @@ class DefaultToolCallExecutionGate {
|
|
|
24
27
|
if (event.type === "provider_stream_end" && !this.streamEndCaptured) {
|
|
25
28
|
this.streamEndCaptured = true;
|
|
26
29
|
this.streamEndReason = event.reason;
|
|
30
|
+
this.streamEndProviderReason = event.providerReason;
|
|
27
31
|
}
|
|
28
32
|
return this.coordinator.push(event);
|
|
29
33
|
}
|
|
@@ -35,6 +39,7 @@ class DefaultToolCallExecutionGate {
|
|
|
35
39
|
// every in-flight call is still "collecting" regardless, so this
|
|
36
40
|
// never influences whether something looks executable.
|
|
37
41
|
streamEndReason: this.streamEndReason ?? "unknown",
|
|
42
|
+
streamEndProviderReason: this.streamEndProviderReason,
|
|
38
43
|
diagnostics: snap.diagnostics,
|
|
39
44
|
};
|
|
40
45
|
return snap.calls.map((call) => decideExecution(call, ctx));
|
|
@@ -46,10 +51,21 @@ class DefaultToolCallExecutionGate {
|
|
|
46
51
|
if (!this.streamEndCaptured) {
|
|
47
52
|
this.streamEndCaptured = true;
|
|
48
53
|
this.streamEndReason = meta?.reason ?? "unknown";
|
|
54
|
+
this.streamEndProviderReason = meta?.providerReason;
|
|
49
55
|
}
|
|
50
56
|
const result = this.coordinator.finish(meta);
|
|
51
57
|
const diagnostics = this.coordinator.snapshot().diagnostics;
|
|
52
|
-
|
|
58
|
+
// Unlike snapshot() above, this point is only ever reached after
|
|
59
|
+
// streamEndCaptured is guaranteed true (set on entry to this method, or
|
|
60
|
+
// already true from an earlier push()/finish() call) - and every path
|
|
61
|
+
// that sets streamEndCaptured sets streamEndReason to a defined value in
|
|
62
|
+
// the same branch, so the pre-finish "unknown" placeholder never applies
|
|
63
|
+
// here.
|
|
64
|
+
const ctx = {
|
|
65
|
+
streamEndReason: this.streamEndReason,
|
|
66
|
+
streamEndProviderReason: this.streamEndProviderReason,
|
|
67
|
+
diagnostics,
|
|
68
|
+
};
|
|
53
69
|
const decisions = result.calls.map((call) => decideExecution(call, ctx));
|
|
54
70
|
return { decisions, diagnostics };
|
|
55
71
|
}
|
package/dist/gate/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6DAA6D;AAC7D,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,yEAAyE;AACzE,0EAA0E;AAC1E,4EAA4E;AAC5E,8EAA8E;AAE9E,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAUhF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAO9C,MAAM,4BAA4B;IACf,WAAW,CAA4B;IAExD,yEAAyE;IACzE,4EAA4E;IAC5E,iEAAiE;IACjE,
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../../src/gate/gate.ts"],"names":[],"mappings":"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"}
|
package/dist/gate/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { JsonValue, Diagnostic } from "../types.js";
|
|
2
2
|
import { CoordinatorDiagnostic, CoordinatorPushResult, ToolCallCoordinatorEvent } from "../coordinator/types.js";
|
|
3
|
-
import { NormalizedToolStreamEvent, StreamEndReason } from "../coordinator/protocol.js";
|
|
3
|
+
import { NormalizedToolStreamEvent, ProviderName, StreamEndReason } from "../coordinator/protocol.js";
|
|
4
4
|
/**
|
|
5
5
|
* What to do with a tool call right now.
|
|
6
6
|
*
|
|
@@ -19,6 +19,62 @@ export type ExecutionAction = "execute" | "retry" | "reject";
|
|
|
19
19
|
* See `docs/EXECUTION_GATE.md` for the full decision table.
|
|
20
20
|
*/
|
|
21
21
|
export type ExecutionReason = "complete" | "truncated" | "stream_incomplete" | "schema_invalid" | "malformed" | "resource_limit" | "provider_error" | "content_filtered" | "unknown";
|
|
22
|
+
/**
|
|
23
|
+
* Why a decision came out the way it did - observability, not a second
|
|
24
|
+
* decision input. Every field here is read-only, derived state already held
|
|
25
|
+
* elsewhere (`ToolCallState`, the stream-level `DecisionContext`); nothing
|
|
26
|
+
* on this object ever feeds back into `decideExecution()`'s own logic, and
|
|
27
|
+
* changing what it reports can never change `action`/`executable`/`reason`.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately excludes anything that would require new cross-layer
|
|
30
|
+
* plumbing to derive reliably (e.g. a received-chunk count - the parser
|
|
31
|
+
* only tracks cumulative bytes, not `push()` call counts, and adding that
|
|
32
|
+
* tracking purely to populate a metric here was judged not worth the
|
|
33
|
+
* coupling for this release).
|
|
34
|
+
*/
|
|
35
|
+
export interface DecisionEvidence {
|
|
36
|
+
/** Which provider adapter produced this call (`ToolCallState.provider`). */
|
|
37
|
+
readonly provider: ProviderName;
|
|
38
|
+
/**
|
|
39
|
+
* The provider's own, pre-normalization finish/stop reason string, when
|
|
40
|
+
* the stream-ending event carried one (e.g. `"length"`, `"max_output_tokens"`,
|
|
41
|
+
* `"content_filter"`). `undefined` when the stream never reported one, or
|
|
42
|
+
* reported one without a `providerReason` (e.g. a default `finish()` call
|
|
43
|
+
* with no `meta`).
|
|
44
|
+
*/
|
|
45
|
+
readonly providerReason?: string;
|
|
46
|
+
/** The normalized, library-level stream-end reason applied to this call. */
|
|
47
|
+
readonly streamEndReason: StreamEndReason;
|
|
48
|
+
/**
|
|
49
|
+
* Whether a real `provider_stream_end` reason was ever observed, as
|
|
50
|
+
* opposed to the neutral `"unknown"` default a gate reports before any
|
|
51
|
+
* stream-end signal has arrived at all. `true` here does NOT mean the
|
|
52
|
+
* reason was safe - a confirmed `"length"` still has `terminalConfirmed:
|
|
53
|
+
* true` - only that the stream's end state is genuinely known rather than
|
|
54
|
+
* merely unclassified or never observed.
|
|
55
|
+
*/
|
|
56
|
+
readonly terminalConfirmed: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the parser's root JSON container actually closed
|
|
59
|
+
* (`ToolCallState.parser.rootComplete`) - independent of whether that
|
|
60
|
+
* closure was genuine or reached via a safe-close repair, and independent
|
|
61
|
+
* of whether the stream-end reason makes it trustworthy. A syntactically
|
|
62
|
+
* complete value cut short by `length` has `structurallyComplete: true`
|
|
63
|
+
* and `parserExecutable: false` at the same time - that combination *is*
|
|
64
|
+
* the scenario this library exists to catch.
|
|
65
|
+
*/
|
|
66
|
+
readonly structurallyComplete: boolean;
|
|
67
|
+
/** `ToolCallState.parser.executable`, verbatim - the parser's own fail-closed verdict. */
|
|
68
|
+
readonly parserExecutable: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* `ToolCallState.schemaValid`, verbatim. `undefined` when no schema was
|
|
71
|
+
* registered for this tool, or the call never reached a structurally
|
|
72
|
+
* complete outcome.
|
|
73
|
+
*/
|
|
74
|
+
readonly schemaValid?: boolean;
|
|
75
|
+
/** Total bytes received for this call's argument stream (`ToolCallState.parser.receivedBytes`). */
|
|
76
|
+
readonly receivedBytes: number;
|
|
77
|
+
}
|
|
22
78
|
interface ExecutionDecisionCommon {
|
|
23
79
|
/** The coordinator's internal call identifier (see `ToolCallState.internalId`). */
|
|
24
80
|
readonly internalId: string;
|
|
@@ -38,6 +94,12 @@ interface ExecutionDecisionCommon {
|
|
|
38
94
|
readonly parserDiagnostics: readonly Diagnostic[];
|
|
39
95
|
/** Coordinator-level diagnostics attributable to this call, verbatim. */
|
|
40
96
|
readonly coordinatorDiagnostics: readonly CoordinatorDiagnostic[];
|
|
97
|
+
/**
|
|
98
|
+
* Why this decision came out the way it did. See `DecisionEvidence` -
|
|
99
|
+
* always present, purely observational, never a second source of truth
|
|
100
|
+
* for `action`/`executable`/`reason`.
|
|
101
|
+
*/
|
|
102
|
+
readonly evidence: DecisionEvidence;
|
|
41
103
|
}
|
|
42
104
|
/**
|
|
43
105
|
* The one and only shape a decision can have when `action === "execute"`.
|
package/dist/gate/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gate/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/gate/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AiSdkExecutionGuard, ExecutionGuardOptions } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* @public (Experimental)
|
|
4
|
+
* Drop-in fail-closed execution guard for the Vercel AI SDK's `fullStream`.
|
|
5
|
+
* Composes `AiSdkStreamAdapter` with `createToolCallExecutionGate()` - same
|
|
6
|
+
* decision logic, same fail-closed guarantees, no new parser or coordinator.
|
|
7
|
+
* Does not depend on the `ai` package at runtime and does not import its
|
|
8
|
+
* types; `push()` accepts `unknown` and reads only the fields it needs.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const guard = createAiSdkExecutionGuard({
|
|
12
|
+
* schemas: {
|
|
13
|
+
* write_file: {
|
|
14
|
+
* type: "object",
|
|
15
|
+
* properties: { path: { type: "string" }, content: { type: "string" } },
|
|
16
|
+
* required: ["path", "content"],
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* for await (const part of result.fullStream) {
|
|
22
|
+
* guard.push(part);
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* const final = guard.finish();
|
|
26
|
+
* for (const decision of final.decisions) {
|
|
27
|
+
* if (decision.action === "execute") {
|
|
28
|
+
* await tools[decision.name](decision.value);
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Equivalent to composing the low-level API yourself:
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* const gate = createToolCallExecutionGate(undefined, undefined, schemas);
|
|
37
|
+
* const adapter = new AiSdkStreamAdapter();
|
|
38
|
+
* for await (const part of result.fullStream) {
|
|
39
|
+
* for (const normalized of adapter.push(part)) gate.push(normalized);
|
|
40
|
+
* }
|
|
41
|
+
* const final = gate.finish();
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* Both APIs remain public; use this one for the common case, the low-level
|
|
45
|
+
* one when you need direct access to the adapter or gate instance.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createAiSdkExecutionGuard(options?: ExecutionGuardOptions): AiSdkExecutionGuard;
|
|
48
|
+
//# sourceMappingURL=ai-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../../src/guard/ai-sdk.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,mBAAmB,CAE9F"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AiSdkStreamAdapter } from "../providers/ai-sdk.js";
|
|
2
|
+
import { createProviderExecutionGuard } from "./provider-guard.js";
|
|
3
|
+
/**
|
|
4
|
+
* @public (Experimental)
|
|
5
|
+
* Drop-in fail-closed execution guard for the Vercel AI SDK's `fullStream`.
|
|
6
|
+
* Composes `AiSdkStreamAdapter` with `createToolCallExecutionGate()` - same
|
|
7
|
+
* decision logic, same fail-closed guarantees, no new parser or coordinator.
|
|
8
|
+
* Does not depend on the `ai` package at runtime and does not import its
|
|
9
|
+
* types; `push()` accepts `unknown` and reads only the fields it needs.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const guard = createAiSdkExecutionGuard({
|
|
13
|
+
* schemas: {
|
|
14
|
+
* write_file: {
|
|
15
|
+
* type: "object",
|
|
16
|
+
* properties: { path: { type: "string" }, content: { type: "string" } },
|
|
17
|
+
* required: ["path", "content"],
|
|
18
|
+
* },
|
|
19
|
+
* },
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* for await (const part of result.fullStream) {
|
|
23
|
+
* guard.push(part);
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* const final = guard.finish();
|
|
27
|
+
* for (const decision of final.decisions) {
|
|
28
|
+
* if (decision.action === "execute") {
|
|
29
|
+
* await tools[decision.name](decision.value);
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Equivalent to composing the low-level API yourself:
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* const gate = createToolCallExecutionGate(undefined, undefined, schemas);
|
|
38
|
+
* const adapter = new AiSdkStreamAdapter();
|
|
39
|
+
* for await (const part of result.fullStream) {
|
|
40
|
+
* for (const normalized of adapter.push(part)) gate.push(normalized);
|
|
41
|
+
* }
|
|
42
|
+
* const final = gate.finish();
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Both APIs remain public; use this one for the common case, the low-level
|
|
46
|
+
* one when you need direct access to the adapter or gate instance.
|
|
47
|
+
*/
|
|
48
|
+
export function createAiSdkExecutionGuard(options) {
|
|
49
|
+
return createProviderExecutionGuard(new AiSdkStreamAdapter(), options);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=ai-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../../src/guard/ai-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAGnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAA+B;IACvE,OAAO,4BAA4B,CAAC,IAAI,kBAAkB,EAAE,EAAE,OAAO,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ProviderStreamAdapter } from "../providers/adapter.js";
|
|
2
|
+
import { ExecutionGuard, ExecutionGuardOptions } from "./types.js";
|
|
3
|
+
export declare function createProviderExecutionGuard<TRawEvent>(adapter: ProviderStreamAdapter<TRawEvent>, options?: ExecutionGuardOptions): ExecutionGuard<TRawEvent>;
|
|
4
|
+
//# sourceMappingURL=provider-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-guard.d.ts","sourceRoot":"","sources":["../../src/guard/provider-guard.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnE,wBAAgB,4BAA4B,CAAC,SAAS,EACpD,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,EACzC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAAC,SAAS,CAAC,CA0B3B"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// createProviderExecutionGuard — internal composition helper.
|
|
3
|
+
//
|
|
4
|
+
// Not exported from the package root in this release. Every high-level
|
|
5
|
+
// per-provider guard (currently only createAiSdkExecutionGuard) is this
|
|
6
|
+
// function applied to one ProviderStreamAdapter instance - no duplicated
|
|
7
|
+
// state, no duplicated decision logic, no new parser or coordinator. Kept
|
|
8
|
+
// internal (rather than public) until a second provider guard actually
|
|
9
|
+
// needs it, per the "don't expose abstraction for its own sake" scoping
|
|
10
|
+
// this release deliberately holds to - promoting it to a public export is a
|
|
11
|
+
// non-breaking addition whenever that happens.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
import { createToolCallExecutionGate } from "../gate/gate.js";
|
|
14
|
+
export function createProviderExecutionGuard(adapter, options) {
|
|
15
|
+
const gate = createToolCallExecutionGate(options?.limits, options?.parserOptions, options?.schemas);
|
|
16
|
+
return {
|
|
17
|
+
push(rawEvent) {
|
|
18
|
+
for (const normalized of adapter.push(rawEvent)) {
|
|
19
|
+
gate.push(normalized);
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
snapshot() {
|
|
23
|
+
return gate.snapshot();
|
|
24
|
+
},
|
|
25
|
+
finish(meta) {
|
|
26
|
+
// If the provider stream already delivered its own terminal event
|
|
27
|
+
// through push() (the common case), the adapter is already
|
|
28
|
+
// `finished` and this is a no-op - meta is only consulted as a
|
|
29
|
+
// fail-closed backstop for a stream that ends without ever reporting
|
|
30
|
+
// why (see ProviderStreamAdapter.finish()'s own contract).
|
|
31
|
+
for (const normalized of adapter.finish(meta)) {
|
|
32
|
+
gate.push(normalized);
|
|
33
|
+
}
|
|
34
|
+
return gate.finish(meta);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=provider-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-guard.js","sourceRoot":"","sources":["../../src/guard/provider-guard.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8DAA8D;AAC9D,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,wEAAwE;AACxE,4EAA4E;AAC5E,+CAA+C;AAC/C,8EAA8E;AAE9E,OAAO,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAM9D,MAAM,UAAU,4BAA4B,CAC1C,OAAyC,EACzC,OAA+B;IAE/B,MAAM,IAAI,GAAG,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAEpG,OAAO;QACL,IAAI,CAAC,QAAmB;YACtB,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxB,CAAC;QACH,CAAC;QAED,QAAQ;YACN,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QAED,MAAM,CAAC,IAA4D;YACjE,kEAAkE;YAClE,2DAA2D;YAC3D,+DAA+D;YAC/D,qEAAqE;YACrE,2DAA2D;YAC3D,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxB,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { StreamEndReason } from "../coordinator/protocol.js";
|
|
2
|
+
import { CoordinatorLimits, JsonSchemaLike } from "../coordinator/types.js";
|
|
3
|
+
import { ToolCallExecutionGateFinalResult, ExecutionDecision } from "../gate/types.js";
|
|
4
|
+
import { ParserOptions } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* High-level, provider-agnostic execution guard: composes a
|
|
7
|
+
* `ProviderStreamAdapter` with a `ToolCallExecutionGate` behind three
|
|
8
|
+
* methods. `push()`/`snapshot()`/`finish()` mirror the underlying gate's own
|
|
9
|
+
* shape exactly - this is a thinner call surface over the same object, not a
|
|
10
|
+
* parallel implementation with its own state or decision logic.
|
|
11
|
+
*/
|
|
12
|
+
export interface ExecutionGuard<TRawEvent = unknown> {
|
|
13
|
+
/** Feed one raw provider event (e.g. one `fullStream` part) through the adapter into the gate. */
|
|
14
|
+
push(rawEvent: TRawEvent): void;
|
|
15
|
+
/**
|
|
16
|
+
* In-flight view of every call's current decision. As with the underlying
|
|
17
|
+
* gate, every decision here is `"retry"` or `"reject"` - `"execute"` is
|
|
18
|
+
* only ever returned from `finish()`.
|
|
19
|
+
*/
|
|
20
|
+
snapshot(): readonly ExecutionDecision[];
|
|
21
|
+
/**
|
|
22
|
+
* Signals stream completion and returns the final decision for every tool
|
|
23
|
+
* call the stream ever produced. Safe to call even if the underlying
|
|
24
|
+
* provider stream already delivered its own terminal event through
|
|
25
|
+
* `push()` - `meta` is only used as a fallback for a stream that ends
|
|
26
|
+
* without ever reporting why.
|
|
27
|
+
*/
|
|
28
|
+
finish(meta?: {
|
|
29
|
+
reason?: StreamEndReason;
|
|
30
|
+
providerReason?: string;
|
|
31
|
+
}): ToolCallExecutionGateFinalResult;
|
|
32
|
+
}
|
|
33
|
+
/** Options accepted by every high-level provider execution guard factory. */
|
|
34
|
+
export interface ExecutionGuardOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Optional JSON Schema (draft-07) per tool name - same option
|
|
37
|
+
* `createToolCallExecutionGate()`/`createToolCallStreamCoordinator()`
|
|
38
|
+
* accept as their third constructor argument.
|
|
39
|
+
*/
|
|
40
|
+
schemas?: Record<string, JsonSchemaLike>;
|
|
41
|
+
/** Coordinator-level limits (concurrent tool calls, tool name size, etc.). */
|
|
42
|
+
limits?: Partial<CoordinatorLimits>;
|
|
43
|
+
/** Per-call parser configuration (resource limits, repairs). */
|
|
44
|
+
parserOptions?: ParserOptions;
|
|
45
|
+
}
|
|
46
|
+
/** A guard specialized for the Vercel AI SDK's `fullStream` part shape. See `createAiSdkExecutionGuard`. */
|
|
47
|
+
export type AiSdkExecutionGuard = ExecutionGuard<unknown>;
|
|
48
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guard/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,WAAW,cAAc,CAAC,SAAS,GAAG,OAAO;IACjD,kGAAkG;IAClG,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;IAEhC;;;;OAIG;IACH,QAAQ,IAAI,SAAS,iBAAiB,EAAE,CAAC;IAEzC;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,gCAAgC,CAAC;CACxG;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAEzC,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEpC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED,4GAA4G;AAC5G,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/guard/types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,15 @@ export { CONTENT_FILTERED_DIAGNOSTIC_CODE } from "./coordinator/diagnostic-codes
|
|
|
21
21
|
* See docs/EXECUTION_GATE.md.
|
|
22
22
|
*/
|
|
23
23
|
export { createToolCallExecutionGate } from "./gate/gate.js";
|
|
24
|
-
export type { ToolCallExecutionGate, ToolCallExecutionGateFinalResult, ExecutionDecision, ExecuteDecision, NonExecutableDecision, ExecutionAction, ExecutionReason, } from "./gate/types.js";
|
|
24
|
+
export type { ToolCallExecutionGate, ToolCallExecutionGateFinalResult, ExecutionDecision, ExecuteDecision, NonExecutableDecision, ExecutionAction, ExecutionReason, DecisionEvidence, } from "./gate/types.js";
|
|
25
|
+
/**
|
|
26
|
+
* @public (Experimental)
|
|
27
|
+
* Drop-in fail-closed execution guard for the Vercel AI SDK's `fullStream`.
|
|
28
|
+
* Composes `AiSdkStreamAdapter` and `createToolCallExecutionGate()` - see
|
|
29
|
+
* `docs/EXECUTION_GATE.md#high-level-guards`.
|
|
30
|
+
*/
|
|
31
|
+
export { createAiSdkExecutionGuard } from "./guard/ai-sdk.js";
|
|
32
|
+
export type { ExecutionGuard, AiSdkExecutionGuard, ExecutionGuardOptions } from "./guard/types.js";
|
|
25
33
|
export type { ProviderStreamAdapter } from "./providers/adapter.js";
|
|
26
34
|
/**
|
|
27
35
|
* @public (Experimental)
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,YAAY,EAEV,qBAAqB,EACrB,aAAa,EAGb,UAAU,EACV,cAAc,EACd,WAAW,EAGX,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EAGnB,SAAS,EACT,UAAU,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD;;;GAGG;AACH,OAAO,EAAE,+BAA+B,EAAE,MAAM,8BAA8B,CAAC;AAC/E,YAAY,EACV,yBAAyB,EACzB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AAGrF;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAC7D,YAAY,EACV,qBAAqB,EACrB,gCAAgC,EAChC,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,YAAY,EAEV,qBAAqB,EACrB,aAAa,EAGb,UAAU,EACV,cAAc,EACd,WAAW,EAGX,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EAGnB,SAAS,EACT,UAAU,EACV,SAAS,EACT,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD;;;GAGG;AACH,OAAO,EAAE,+BAA+B,EAAE,MAAM,8BAA8B,CAAC;AAC/E,YAAY,EACV,yBAAyB,EACzB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,gCAAgC,EAAE,MAAM,mCAAmC,CAAC;AAGrF;;;;;GAKG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAC;AAC7D,YAAY,EACV,qBAAqB,EACrB,gCAAgC,EAChC,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB;;;;;GAKG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAGnG,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE;;;;GAIG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC"}
|