prefix-safe-json 0.5.3 → 0.5.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 CHANGED
@@ -4,15 +4,14 @@
4
4
  [![CI](https://github.com/canblmz1/prefix-safe-json/actions/workflows/ci.yml/badge.svg)](https://github.com/canblmz1/prefix-safe-json/actions/workflows/ci.yml)
5
5
  [![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](#license)
6
6
 
7
- An incremental JSON parser and execution gate for **streamed LLM tool calls**.
7
+ Check original streamed arguments and terminal evidence **before your application
8
+ runs a tool**. `prefix-safe-json` refuses authority for incomplete input,
9
+ conflicting call identity, invalid schemas, or unsafe termination, even when a
10
+ parsed tool-call object looks usable.
8
11
 
9
- `prefix-safe-json` keeps incomplete, conflicting, or unconfirmed streamed arguments from being treated as final tool input. It is designed for callers that need to distinguish:
10
-
11
- 1. JSON that can be parsed right now,
12
- 2. values that are stable enough to expose, and
13
- 3. arguments that the provider actually completed and confirmed.
14
-
15
- It does not execute tools itself.
12
+ Your application consumes a decision once and performs the side effect itself.
13
+ PSJ never executes tools. Provider signals must accurately describe completion;
14
+ see the [provider-honesty limit](examples/openai-consumer/README.md).
16
15
 
17
16
  ```bash
18
17
  pnpm add prefix-safe-json ai zod
@@ -94,10 +93,10 @@ checkout without model requests or tool dispatch.
94
93
  ## Run a standalone npm demo
95
94
 
96
95
  To try it without cloning this repository, download the
97
- [standalone trial ZIP](https://github.com/canblmz1/prefix-safe-json/releases/tag/trial-2026-10-14).
96
+ [standalone trial ZIP](https://github.com/canblmz1/prefix-safe-json/releases/tag/trial-2026-10-16).
98
97
  It contains only the example sources and licenses; its release notes give
99
- the extraction and npm commands. It installs published PSJ `0.5.2`, not a new
100
- or unpublished package version.
98
+ the extraction and npm commands. Choose an AI SDK, OpenAI, Anthropic or
99
+ LangChain consumer; each installs published PSJ `0.5.3`.
101
100
 
102
101
  With Node 22+ and npm, you can run the published-package demo from this checkout
103
102
  without installing the repository's development dependencies:
@@ -119,7 +118,7 @@ npm ls prefix-safe-json ai zod
119
118
  npm start
120
119
  ```
121
120
 
122
- The example pins `prefix-safe-json@0.5.2`, `ai@7.0.77`, and `zod@4.4.3`.
121
+ The current checkout pins `prefix-safe-json@0.5.3`, `ai@7.0.77`, and `zod@4.4.3`.
123
122
  It needs no repository build, workspace aliases, or API key. It drives real
124
123
  `streamText().fullStream` using the SDK's official `MockLanguageModelV4` and
125
124
  imports the installed package's public entry point and `standard-schema`
@@ -161,6 +160,25 @@ It checks complete, token-limited, missing-`message_stop`, and post-terminal
161
160
  argument cases through the SDK's actual SSE parser. The LangChain comparison
162
161
  can also be launched with `node scripts/try-npm-demo.mjs langchain`.
163
162
 
163
+ Using the official OpenAI SDK? Run `node scripts/try-npm-demo.mjs openai` for
164
+ the [installed-package Chat Completions trial](examples/openai-consumer/README.md).
165
+ It checks complete and interrupted calls, duplicate IDs and late argument
166
+ evidence through `openai@7.34.0`. It also demonstrates a limit: PSJ cannot detect
167
+ upstream truncation when the provider supplies complete JSON and falsely
168
+ reports successful termination. These are mock-transport checks, not live
169
+ OpenAI or vLLM certification.
170
+
171
+ Using OpenRouter Agent? Run `node scripts/try-npm-demo.mjs openrouter` for the
172
+ [manual dispatch and continuation trial](examples/openrouter-consumer/README.md).
173
+ It checks that accepted tool results reach the next model request with matching
174
+ call IDs, while invalid batches cause zero effects and no continuation request.
175
+ It uses authored SSE through the installed SDK; no live model call is made.
176
+
177
+ 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.
181
+
164
182
  ## Why parse success is not enough
165
183
 
166
184
  Tool-call arguments often arrive as partial JSON. A renderer may reasonably repair or close an unfinished prefix so it can show progressive UI. That is useful for display, but it answers a different question from whether a side effect should run.
@@ -3,6 +3,8 @@ import { ProviderStreamAdapter } from "./adapter.js";
3
3
  export interface OpenRouterChoice {
4
4
  index?: number;
5
5
  delta?: {
6
+ content?: string | null;
7
+ role?: string;
6
8
  tool_calls?: Array<{
7
9
  index?: number;
8
10
  id?: string;
@@ -15,17 +17,27 @@ export interface OpenRouterChoice {
15
17
  reasoning?: string;
16
18
  };
17
19
  finish_reason?: string | null;
20
+ native_finish_reason?: string | null;
18
21
  error?: unknown;
19
22
  }
20
23
  export interface OpenRouterEvent {
21
24
  choices?: OpenRouterChoice[];
22
25
  error?: unknown;
26
+ usage?: {
27
+ prompt_tokens?: number;
28
+ completion_tokens?: number;
29
+ total_tokens?: number;
30
+ };
23
31
  }
24
32
  export declare class OpenRouterStreamAdapter implements ProviderStreamAdapter<unknown> {
25
33
  readonly provider: ProviderName;
26
34
  private sequence;
27
35
  private compatibleAdapter;
28
36
  private finished;
37
+ private terminalChoices;
38
+ private accountingTrailerSeen;
39
+ private nativeOutputLimitReason;
40
+ private isAccountingTrailer;
29
41
  push(rawEvent: unknown): readonly NormalizedToolStreamEvent[];
30
42
  finish(meta?: {
31
43
  reason?: StreamEndReason;
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGrD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,KAAK,CAAC;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,EAAE,CAAC,EAAE,MAAM,CAAC;YACZ,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,QAAQ,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAClD,CAAC,CAAC;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,uBAAwB,YAAW,qBAAqB,CAAC,OAAO,CAAC;IAC5E,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAK;IAGrB,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAS;IAEzB,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,yBAAyB,EAAE;IAwD7D,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,yBAAyB,EAAE;CAO3G"}
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGrD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,KAAK,CAAC;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,EAAE,CAAC,EAAE,MAAM,CAAC;YACZ,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,QAAQ,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAClD,CAAC,CAAC;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvF;AAcD,qBAAa,uBAAwB,YAAW,qBAAqB,CAAC,OAAO,CAAC;IAC5E,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAK;IAGrB,OAAO,CAAC,iBAAiB,CAAuC;IAChE,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,eAAe,CAAkF;IACzG,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,uBAAuB,CAAqB;IAEpD,OAAO,CAAC,mBAAmB;IAkB3B,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,yBAAyB,EAAE;IAmE7D,MAAM,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,eAAe,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,yBAAyB,EAAE;CAc3G"}
@@ -1,10 +1,45 @@
1
1
  import { OpenAICompatibleStreamAdapter } from "./openai-compatible.js";
2
+ // OpenRouter preserves the upstream finish reason separately from its normalized
3
+ // reason. A known output-limit signal cannot become successful execution merely
4
+ // because the router normalized that choice to `tool_calls` or `stop`.
5
+ const NATIVE_OUTPUT_LIMIT_REASONS = new Set(["max_output_tokens", "max_tokens", "MAX_TOKENS", "length"]);
6
+ function isAccountingDelta(delta) {
7
+ if (!delta || typeof delta !== "object" || Array.isArray(delta))
8
+ return false;
9
+ return Object.entries(delta).every(([key, value]) => (key === "content" && (value === "" || value === null)) ||
10
+ (key === "role" && value === "assistant"));
11
+ }
2
12
  export class OpenRouterStreamAdapter {
3
13
  provider = "openrouter";
4
14
  sequence = 0;
5
15
  // Delegate standard tool_call formats to compatible adapter
6
16
  compatibleAdapter = new OpenAICompatibleStreamAdapter();
7
17
  finished = false;
18
+ // Preserve coordinates unchanged; the compatible adapter owns index validity
19
+ // and duplicate-index diagnostics, even on a stripped accounting terminal.
20
+ terminalChoices = new Map();
21
+ accountingTrailerSeen = false;
22
+ nativeOutputLimitReason;
23
+ isAccountingTrailer(chunk) {
24
+ // The documented final usage chunk repeats the exact terminal metadata.
25
+ // Accept one content-free accounting frame only, for already-ended choices.
26
+ // Every other duplicate/conflicting terminal still reaches the strict
27
+ // compatible adapter, including errors, arguments and unknown choice IDs.
28
+ if (this.finished || this.accountingTrailerSeen || !Array.isArray(chunk.choices) || !chunk.choices.length ||
29
+ !chunk.usage || typeof chunk.usage !== "object" || Array.isArray(chunk.usage))
30
+ return false;
31
+ const usage = chunk.usage;
32
+ if (![usage.prompt_tokens, usage.completion_tokens, usage.total_tokens]
33
+ .every(value => typeof value === "number" && Number.isFinite(value) && value >= 0))
34
+ return false;
35
+ return chunk.choices.every(choice => {
36
+ if (choice.error || !isAccountingDelta(choice.delta))
37
+ return false;
38
+ const terminal = this.terminalChoices.get(choice.index);
39
+ return terminal !== undefined && choice.finish_reason === terminal.reason &&
40
+ (choice.native_finish_reason ?? null) === terminal.nativeReason;
41
+ });
42
+ }
8
43
  push(rawEvent) {
9
44
  // No `finished` early return here - see the identical fix and rationale
10
45
  // in AnthropicStreamAdapter.push()/OpenAIStreamAdapter.push() (same
@@ -50,7 +85,19 @@ export class OpenRouterStreamAdapter {
50
85
  this.finished = true;
51
86
  return events;
52
87
  }
53
- const compatibleEvents = this.compatibleAdapter.push(chunk);
88
+ const accounting = this.isAccountingTrailer(chunk);
89
+ if (accounting)
90
+ this.accountingTrailerSeen = true;
91
+ const choices = Array.isArray(chunk.choices) ? chunk.choices.map(choice => {
92
+ if (choice.finish_reason != null) {
93
+ this.terminalChoices.set(choice.index, { reason: choice.finish_reason, nativeReason: choice.native_finish_reason ?? null });
94
+ }
95
+ if (typeof choice.native_finish_reason === "string" && NATIVE_OUTPUT_LIMIT_REASONS.has(choice.native_finish_reason)) {
96
+ this.nativeOutputLimitReason ??= choice.native_finish_reason;
97
+ }
98
+ return accounting ? { ...choice, finish_reason: null } : choice;
99
+ }) : chunk.choices;
100
+ const compatibleEvents = this.compatibleAdapter.push({ ...chunk, choices });
54
101
  for (const e of compatibleEvents) {
55
102
  events.push({ ...e, provider: this.provider, sequence: ++this.sequence });
56
103
  }
@@ -60,7 +107,14 @@ export class OpenRouterStreamAdapter {
60
107
  if (this.finished)
61
108
  return [];
62
109
  this.finished = true;
63
- const compatibleEvents = this.compatibleAdapter.finish(meta);
110
+ // Aggregate native truncation with normalized choice reasons at the actual
111
+ // EOF boundary. Keep the raw reason for evidence instead of inventing a
112
+ // provider finish_reason string. Stronger caller failures retain priority.
113
+ const effectiveMeta = this.nativeOutputLimitReason !== undefined &&
114
+ (meta?.reason === undefined || meta.reason === "complete" || meta.reason === "cancelled" || meta.reason === "length")
115
+ ? { reason: "length", providerReason: this.nativeOutputLimitReason }
116
+ : meta;
117
+ const compatibleEvents = this.compatibleAdapter.finish(effectiveMeta);
64
118
  return compatibleEvents.map(e => ({ ...e, provider: this.provider, sequence: ++this.sequence }));
65
119
  }
66
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.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.3",
3
+ "version": "0.5.4",
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",
@@ -100,7 +100,7 @@
100
100
  "@stryker-mutator/vitest-runner": "^9.6.1",
101
101
  "@types/clarinet": "^0.12.3",
102
102
  "@types/node": "^26.1.2",
103
- "@vitest/coverage-v8": "^3.2.6",
103
+ "@vitest/coverage-v8": "^4.1.12",
104
104
  "ai-v5": "npm:ai@5.0.244",
105
105
  "ai-v6": "npm:ai@6.0.264",
106
106
  "ai-v7": "npm:ai@7.0.77",
@@ -114,15 +114,18 @@
114
114
  "tsx": "^4.16.0",
115
115
  "typescript": "^5.5.0",
116
116
  "typescript-eslint": "^8.0.0",
117
- "vitest": "^3.2.6",
117
+ "vitest": "^4.1.12",
118
118
  "zod": "4.4.3"
119
119
  },
120
120
  "pnpm": {
121
121
  "overrides": {
122
- "qs": "^6.15.2",
122
+ "qs": "^6.16.0",
123
123
  "vite": "^6.4.3",
124
124
  "nanoid": "^3.3.18",
125
- "undici": "^7.28.0"
125
+ "undici": "^7.29.1",
126
+ "brace-expansion@1": "^1.1.21",
127
+ "brace-expansion@5": "^5.0.12",
128
+ "js-yaml@4": "^4.3.2"
126
129
  }
127
130
  }
128
131
  }