{"version":3,"file":"json-stream-guard.mjs","names":[],"sources":["../../../../../../../ai/src/agent/json-stream-guard.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ModelToolCallRequest } from \"../contracts/model-tool-call-request.type\";\nimport type { ToolContract } from \"../tool/tool\";\n\n/**\n * Default cap on bytes accumulated in a single suspect buffer before\n * the guard gives up, flushes as text, and resets to pass-through.\n * Real envelope payloads observed in production leaks are well under\n * 1 KB; this is a safety valve against runaway / adversarial input.\n */\nconst DEFAULT_MAX_BUFFER_BYTES = 4096;\n\n/**\n * Fence opener the guard recognizes in pass-through mode. Targets the\n * lowercase form ```` ```json ```` only — that is the form models\n * actually emit in the wild when they fence-wrap a JSON tool envelope.\n * Other languages / casings flush as plain text.\n */\nconst FENCE_OPENER = \"```json\";\n\n/**\n * Closing fence sequence inside `bufferingFence` mode. Three backticks,\n * no language tag.\n */\nconst FENCE_CLOSER = \"```\";\n\n/**\n * Options passed when constructing a `JsonStreamGuard`.\n *\n * The guard is deliberately framework-agnostic of *how* deltas are\n * emitted or how recovered calls are dispatched — callers wire those\n * via `onSafeDelta` / `onRecoveredCall`. This keeps the unit-testable\n * surface tiny and lets the agent loop own all event-emission policy.\n */\nexport type JsonStreamGuardOptions = {\n  /** Tools the agent has registered for this trip. Envelope lookups use `.name`. */\n  tools: ReadonlyArray<ToolContract<unknown, unknown>>;\n  /**\n   * Hard cap on a single suspect buffer's size. When exceeded, the\n   * buffer is flushed verbatim as text and the guard returns to\n   * pass-through. Defaults to {@link DEFAULT_MAX_BUFFER_BYTES}.\n   */\n  maxBufferBytes?: number;\n  /**\n   * Called for every chunk of text that survived the guard — exactly\n   * what the consumer should treat as the visible delta. May be\n   * called many times per `feed()` call, possibly with a single\n   * character or with a multi-character flush.\n   */\n  onSafeDelta: (delta: string) => void;\n  /**\n   * Called once per envelope the guard successfully classifies as a\n   * tool-call recovery. The request carries `recoveredFrom:\n   * \"stream-text\"` so downstream consumers can distinguish synthesized\n   * calls from real ones.\n   */\n  onRecoveredCall: (request: ModelToolCallRequest) => void;\n};\n\n/**\n * Per-trip state machine that intercepts streamed text deltas, detects\n * JSON envelopes the model has emitted as plain text (the\n * tool-call-leakage symptom), and synthesizes real `ModelToolCallRequest`\n * entries for them while suppressing the JSON from visible output.\n *\n * **Role.** A `JsonStreamGuard` is the per-trip implementation of the\n * opt-in `streamingToolGuard` config. It sits between the model\n * adapter's `delta` chunks and the agent's `agent.trip.streaming`\n * emit + `content` accumulator — text that survives the guard is what\n * the consumer sees and what the trip records as `output`.\n *\n * **Responsibility.**\n * - Owns: a small character-level state machine (pass-through,\n *   brace-buffering, fence-buffering), string-literal-aware brace\n *   tracking, fence-opener / fence-closer detection, named-envelope\n *   matching against registered tool schemas, buffer-cap enforcement.\n * - Does NOT own: event emission (delegated via callbacks), tool\n *   dispatch, `finishReason` normalization, dedupe vs. real tool\n *   calls — the agent loop handles all four.\n *\n * **Matcher tier — named envelope only (v1).** A buffer matches when\n * it parses as a JSON object containing both:\n *   - a `name` or `tool` key resolving to a registered tool name, AND\n *   - an `arguments` or `input` key whose value validates against the\n *     resolved tool's `~standard` schema.\n * Bare-object matching (where any registered tool's schema is the\n * sole signal) is deferred until tool input schemas are tight enough\n * to distinguish — `v.record(v.any())` would match everything.\n *\n * **Per-trip lifecycle.** One instance per trip. The agent loop calls\n * `feed(chunk)` for every `delta` chunk and `finalize()` exactly once\n * after the stream's `done` chunk. Mid-stream cancellation: the loop\n * simply stops calling `feed`; any open buffer is discarded with the\n * guard instance.\n *\n * Modeled as a class (see §4.2 of code-style.md — per-call execution\n * state across phases): the machine has 3 states, accumulators for\n * brace depth, string-literal escape tracking, and a synthesized-call\n * counter for stable ids across the trip.\n *\n * @example\n * // Inside the agent's streaming trip body:\n * const guard = new JsonStreamGuard({\n *   tools: this.config.tools ?? [],\n *   maxBufferBytes: guardConfig.maxBufferBytes,\n *   onSafeDelta: (delta) => {\n *     content += delta;\n *     this.emit(\"agent.trip.streaming\", { delta, tripIndex });\n *   },\n *   onRecoveredCall: (request) => recoveredCalls.push(request),\n * });\n *\n * for await (const chunk of model.stream(messages, callOptions)) {\n *   if (chunk.type === \"delta\") await guard.feed(chunk.content);\n *   // ... other chunk types\n * }\n *\n * await guard.finalize();\n */\nexport class JsonStreamGuard {\n  private readonly tools: ReadonlyArray<ToolContract<unknown, unknown>>;\n  private readonly maxBufferBytes: number;\n  private readonly onSafeDelta: (delta: string) => void;\n  private readonly onRecoveredCall: (request: ModelToolCallRequest) => void;\n\n  private mode: \"passThrough\" | \"bufferingBrace\" | \"bufferingFence\" = \"passThrough\";\n\n  /**\n   * Characters held back in pass-through mode while we resolve whether\n   * a partial fence opener (`` ` ``, `` `` ``, `` ``` ``, `` ```j ``, …)\n   * will complete or break. Always a strict prefix of {@link FENCE_OPENER};\n   * emptied (and emitted verbatim) the moment a non-matching character\n   * arrives.\n   */\n  private holdback = \"\";\n\n  /**\n   * Accumulator while `mode === \"bufferingBrace\"` or `\"bufferingFence\"`.\n   * In brace mode it carries the JSON including the outermost `{`/`}`.\n   * In fence mode it carries everything between the opener and the\n   * closer (the opener and closer themselves are NOT in the buffer —\n   * they are reconstructed only on a flush-as-text fallback).\n   */\n  private buffer = \"\";\n\n  /**\n   * Brace-depth counter for `bufferingBrace` mode. Increments on `{`,\n   * decrements on `}` — but only when {@link inString} is false, so a\n   * `{` inside a JSON string literal does not skew the depth. Buffer\n   * closes when depth returns to zero.\n   */\n  private braceDepth = 0;\n\n  /** True while the scanner is inside a `\"...\"` JSON string literal. */\n  private inString = false;\n\n  /**\n   * True when the previous character inside a string literal was a\n   * backslash, so the current character is escaped (`\\\"` does not end\n   * the string; `\\\\` resets the flag without escaping anything else).\n   */\n  private escapeNext = false;\n\n  /**\n   * Trailing tail of the fence buffer used to detect the closing\n   * ```` ``` ```` sequence. Length capped at the closer length; rotated\n   * forward as new characters arrive.\n   */\n  private fenceCloseTail = \"\";\n\n  /**\n   * Count of envelopes the guard has successfully synthesized this\n   * trip. Used to assign deterministic, collision-free ids on\n   * recovered `ModelToolCallRequest` entries.\n   */\n  private recoveredCount = 0;\n\n  public constructor(options: JsonStreamGuardOptions) {\n    this.tools = options.tools;\n    this.maxBufferBytes = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;\n    this.onSafeDelta = options.onSafeDelta;\n    this.onRecoveredCall = options.onRecoveredCall;\n  }\n\n  /**\n   * Feed the next raw delta from the model. Splits the chunk into\n   * characters and runs each through the state machine, awaiting\n   * envelope classification whenever a buffer closes mid-chunk.\n   *\n   * The hot path (pass-through prose with no `{` / `` ` ``) is fully\n   * synchronous — `await` here only blocks at buffer-close points,\n   * which are rare in normal traffic.\n   */\n  public async feed(chunk: string): Promise<void> {\n    for (let i = 0; i < chunk.length; i++) {\n      await this.processChar(chunk[i]);\n    }\n  }\n\n  /**\n   * Stream ended. Anything still in the holdback was prose\n   * misclassified as a partial fence opener — emit it. Anything still\n   * in an open buffer never closed — emit it as text too (a leak\n   * truncated mid-flight is still text the user partially saw).\n   */\n  public async finalize(): Promise<void> {\n    if (this.holdback.length > 0) {\n      this.onSafeDelta(this.holdback);\n      this.holdback = \"\";\n    }\n\n    if (this.mode === \"bufferingBrace\") {\n      this.flushBraceBufferAsText();\n      return;\n    }\n\n    if (this.mode === \"bufferingFence\") {\n      this.flushFenceBufferAsText();\n    }\n  }\n\n  /**\n   * True when at least one envelope was recovered this trip. The\n   * agent loop reads this to override `finishReason` from `\"stop\"` to\n   * `\"tool_calls\"` when the model reported a natural stop but the\n   * guard found tool calls hiding in the text channel.\n   */\n  public hasRecoveredCalls(): boolean {\n    return this.recoveredCount > 0;\n  }\n\n  /**\n   * Route a single character based on the current mode. The\n   * `passThrough` branch handles holdback expansion / flushing\n   * iteratively (no recursion) so a character that \"breaks\" a fence\n   * opener can be re-evaluated as a fresh pass-through input in the\n   * same call.\n   */\n  private async processChar(char: string): Promise<void> {\n    if (this.mode === \"bufferingBrace\") {\n      await this.processBraceChar(char);\n      return;\n    }\n\n    if (this.mode === \"bufferingFence\") {\n      await this.processFenceChar(char);\n      return;\n    }\n\n    let current = char;\n\n    while (true) {\n      if (this.holdback.length === 0 && current === \"{\") {\n        this.openBraceBuffer(current);\n        return;\n      }\n\n      const extended = this.holdback + current;\n\n      if (this.isFenceOpenerPrefix(extended)) {\n        this.holdback = extended;\n\n        if (extended === FENCE_OPENER) {\n          this.openFenceBuffer();\n        }\n\n        return;\n      }\n\n      if (this.holdback.length === 0) {\n        this.onSafeDelta(current);\n        return;\n      }\n\n      this.onSafeDelta(this.holdback);\n      this.holdback = \"\";\n    }\n  }\n\n  /**\n   * Recognize any strict prefix of {@link FENCE_OPENER} including the\n   * full string. Used to decide whether to keep extending the holdback\n   * or flush it as plain text.\n   */\n  private isFenceOpenerPrefix(candidate: string): boolean {\n    return candidate.length <= FENCE_OPENER.length && FENCE_OPENER.startsWith(candidate);\n  }\n\n  /**\n   * Enter `bufferingBrace` mode with the seed `{` as the first buffer\n   * character and the initial brace depth set to one. Any holdback at\n   * this point was already a non-fence sequence so it stays empty.\n   */\n  private openBraceBuffer(seed: string): void {\n    this.mode = \"bufferingBrace\";\n    this.buffer = seed;\n    this.braceDepth = 1;\n    this.inString = false;\n    this.escapeNext = false;\n  }\n\n  /**\n   * Enter `bufferingFence` mode immediately after the opener\n   * ```` ```json ```` matched in the holdback. Holdback resets;\n   * subsequent characters accumulate into the buffer until the\n   * closing fence is seen.\n   */\n  private openFenceBuffer(): void {\n    this.mode = \"bufferingFence\";\n    this.buffer = \"\";\n    this.fenceCloseTail = \"\";\n    this.holdback = \"\";\n  }\n\n  /**\n   * Process one character while accumulating a brace-delimited JSON\n   * object. Tracks string-literal context so `{` / `}` inside `\"...\"`\n   * do not skew brace depth. Closes (and classifies) on balanced\n   * braces; flushes-as-text on cap overflow.\n   */\n  private async processBraceChar(char: string): Promise<void> {\n    this.buffer += char;\n\n    if (this.inString) {\n      if (this.escapeNext) {\n        this.escapeNext = false;\n        return;\n      }\n\n      if (char === \"\\\\\") {\n        this.escapeNext = true;\n        return;\n      }\n\n      if (char === '\"') {\n        this.inString = false;\n      }\n\n      this.guardBufferCap(\"brace\");\n      return;\n    }\n\n    if (char === '\"') {\n      this.inString = true;\n      this.guardBufferCap(\"brace\");\n      return;\n    }\n\n    if (char === \"{\") {\n      this.braceDepth++;\n      this.guardBufferCap(\"brace\");\n      return;\n    }\n\n    if (char === \"}\") {\n      this.braceDepth--;\n\n      if (this.braceDepth === 0) {\n        await this.closeBraceBuffer();\n        return;\n      }\n\n      this.guardBufferCap(\"brace\");\n      return;\n    }\n\n    this.guardBufferCap(\"brace\");\n  }\n\n  /**\n   * Process one character while accumulating a fence-delimited JSON\n   * block. The closing fence ```` ``` ```` ends the block; the closing\n   * characters are NOT included in the classified buffer (they are\n   * re-emitted only when the block flushes back to text).\n   */\n  private async processFenceChar(char: string): Promise<void> {\n    this.fenceCloseTail += char;\n\n    if (this.fenceCloseTail.length > FENCE_CLOSER.length) {\n      this.fenceCloseTail = this.fenceCloseTail.slice(-FENCE_CLOSER.length);\n    }\n\n    if (this.fenceCloseTail === FENCE_CLOSER) {\n      const innerLength = this.buffer.length - (FENCE_CLOSER.length - 1);\n      this.buffer = this.buffer.slice(0, Math.max(0, innerLength));\n\n      await this.closeFenceBuffer();\n      return;\n    }\n\n    this.buffer += char;\n    this.guardBufferCap(\"fence\");\n  }\n\n  /**\n   * Enforce the buffer-byte cap. When the current buffer exceeds the\n   * cap, flush it back to the consumer as plain text and reset to\n   * pass-through. Acts as a runaway / adversarial-input safety valve.\n   */\n  private guardBufferCap(source: \"brace\" | \"fence\"): void {\n    if (this.buffer.length <= this.maxBufferBytes) {\n      return;\n    }\n\n    if (source === \"brace\") {\n      this.flushBraceBufferAsText();\n      return;\n    }\n\n    this.flushFenceBufferAsText();\n  }\n\n  /**\n   * Run the envelope matcher against the closed brace buffer. On a\n   * match, synthesize a recovered `ModelToolCallRequest`; on no\n   * match, flush the buffer back as plain text. Resets state to\n   * pass-through either way.\n   */\n  private async closeBraceBuffer(): Promise<void> {\n    const closed = this.buffer;\n\n    this.resetToPassThrough();\n\n    const matched = await this.tryMatchEnvelope(closed);\n\n    if (matched) {\n      return;\n    }\n\n    this.onSafeDelta(closed);\n  }\n\n  /**\n   * Run the envelope matcher against the closed fence buffer. On a\n   * match, synthesize a recovered call; on no match, flush as text\n   * **with** the original opener and closer reconstructed so the\n   * customer sees exactly the markdown the model emitted.\n   */\n  private async closeFenceBuffer(): Promise<void> {\n    const closed = this.buffer;\n\n    this.resetToPassThrough();\n\n    const matched = await this.tryMatchEnvelope(closed);\n\n    if (matched) {\n      return;\n    }\n\n    this.onSafeDelta(`${FENCE_OPENER}${closed}${FENCE_CLOSER}`);\n  }\n\n  /**\n   * Emit the brace-buffer verbatim as text and reset to pass-through.\n   * Used on cap overflow and on `finalize()` for an unclosed buffer.\n   */\n  private flushBraceBufferAsText(): void {\n    const closed = this.buffer;\n    this.resetToPassThrough();\n    this.onSafeDelta(closed);\n  }\n\n  /**\n   * Emit the fence-buffer verbatim as text, reconstructing the\n   * opener and closer so the original markdown structure is\n   * preserved for the consumer.\n   */\n  private flushFenceBufferAsText(): void {\n    const closed = this.buffer;\n    this.resetToPassThrough();\n    this.onSafeDelta(`${FENCE_OPENER}${closed}`);\n  }\n\n  /**\n   * Reset all per-buffer state back to the pass-through baseline.\n   * Called whenever a buffer closes — by recovery, by flush, or by\n   * cap overflow — so the next character starts a fresh scan.\n   */\n  private resetToPassThrough(): void {\n    this.mode = \"passThrough\";\n    this.buffer = \"\";\n    this.braceDepth = 0;\n    this.inString = false;\n    this.escapeNext = false;\n    this.fenceCloseTail = \"\";\n  }\n\n  /**\n   * Attempt to classify a closed buffer as a tool-call envelope. On\n   * success, invoke `onRecoveredCall` with a synthesized request and\n   * return `true`; on failure return `false` so the caller can flush\n   * the buffer back as text.\n   */\n  private async tryMatchEnvelope(raw: string): Promise<boolean> {\n    const parsed = safeParseJson(raw);\n\n    if (parsed === undefined || typeof parsed !== \"object\" || parsed === null) {\n      return false;\n    }\n\n    const envelope = parsed as Record<string, unknown>;\n    const candidateName = readString(envelope, \"name\") ?? readString(envelope, \"tool\");\n    const candidateInput = readObject(envelope, \"arguments\") ?? readObject(envelope, \"input\");\n\n    if (!candidateName || !candidateInput) {\n      return false;\n    }\n\n    const tool = this.tools.find((entry) => entry.name === candidateName);\n\n    if (!tool || !tool.input) {\n      return false;\n    }\n\n    const schema = tool.input as StandardSchemaV1<unknown>;\n\n    let validationResult: StandardSchemaV1.Result<unknown>;\n\n    try {\n      validationResult = await schema[\"~standard\"].validate(candidateInput);\n    } catch {\n      return false;\n    }\n\n    if (validationResult.issues) {\n      return false;\n    }\n\n    this.recoveredCount++;\n\n    this.onRecoveredCall({\n      id: `synth_${candidateName}_${this.recoveredCount}`,\n      name: candidateName,\n      input: validationResult.value,\n      recoveredFrom: \"stream-text\",\n    });\n\n    return true;\n  }\n}\n\n/**\n * Parse a JSON string returning `undefined` on any failure. Local to\n * the guard so it can distinguish \"not JSON\" from a parsed `null`\n * value, which `safeJsonParse` cannot — a parsed `null` is a valid\n * JSON value but not a valid envelope, and we want the difference.\n */\nfunction safeParseJson(raw: string): unknown {\n  try {\n    return JSON.parse(raw);\n  } catch {\n    return undefined;\n  }\n}\n\n/**\n * Read a string-typed field from an envelope candidate. Returns\n * `undefined` when the key is missing or the value is non-string —\n * the matcher rejects either case.\n */\nfunction readString(envelope: Record<string, unknown>, key: string): string | undefined {\n  const value = envelope[key];\n\n  return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/**\n * Read an object-typed field from an envelope candidate. Returns\n * `undefined` when the key is missing or the value is not a\n * plain object (rejects arrays, primitives, null) — tool input\n * schemas always validate against an object root.\n */\nfunction readObject(\n  envelope: Record<string, unknown>,\n  key: string,\n): Record<string, unknown> | undefined {\n  const value = envelope[key];\n\n  if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n    return undefined;\n  }\n\n  return value as Record<string, unknown>;\n}\n"],"mappings":"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"}