@tangle-network/agent-app 0.60.204 → 0.60.206
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/dist/chat-routes/index.js +7 -6
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-routes/turn-routes.d.ts +5 -0
- package/dist/{chunk-ZHJ2TDWA.js → chunk-7O6UQJJA.js} +2 -2
- package/dist/{chunk-ZRUR3DRU.js → chunk-HA5USG2Z.js} +6 -3
- package/dist/chunk-HA5USG2Z.js.map +1 -0
- package/dist/{chunk-N56KDEC4.js → chunk-HPFROQZS.js} +36 -18
- package/dist/chunk-HPFROQZS.js.map +1 -0
- package/dist/{chunk-VQSDYGQ6.js → chunk-J4BZ6NPT.js} +3 -3
- package/dist/chunk-PIUYJTTK.js +63 -0
- package/dist/chunk-PIUYJTTK.js.map +1 -0
- package/dist/{chunk-JKQPKS4O.js → chunk-PMIM6DE7.js} +2 -2
- package/dist/{chunk-7XQIY2LU.js → chunk-T7CWWC3F.js} +5 -52
- package/dist/chunk-T7CWWC3F.js.map +1 -0
- package/dist/public-consultation/index.js +3 -2
- package/dist/public-consultation/index.js.map +1 -1
- package/dist/runtime/index.js +5 -4
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/router-turn.js +4 -3
- package/dist/sandbox/index.js +3 -2
- package/dist/turn-trace/index.js +21 -7
- package/dist/turn-trace/index.js.map +1 -1
- package/dist/turn-trace/recorder.d.ts +2 -0
- package/dist/turn-trace/server-timing.d.ts +2 -0
- package/package.json +1 -1
- package/dist/chunk-7XQIY2LU.js.map +0 -1
- package/dist/chunk-N56KDEC4.js.map +0 -1
- package/dist/chunk-ZRUR3DRU.js.map +0 -1
- /package/dist/{chunk-ZHJ2TDWA.js.map → chunk-7O6UQJJA.js.map} +0 -0
- /package/dist/{chunk-VQSDYGQ6.js.map → chunk-J4BZ6NPT.js.map} +0 -0
- /package/dist/{chunk-JKQPKS4O.js.map → chunk-PMIM6DE7.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/chat-routes/model-failover-stream.ts","../../src/chat-routes/reasoning-clock.ts","../../src/chat-routes/sandbox-turn-usage.ts","../../src/chat-routes/sandbox-producer.ts","../../src/chat-routes/router-producer.ts","../../src/chat-routes/protected-runtime-producer.ts","../../src/chat-routes/detached-turn.ts","../../src/chat-routes/completed-sandbox-turn.ts","../../src/chat-routes/native-completion.ts","../../src/chat-routes/plan-follow-up.ts","../../src/chat-routes/durable-projection.ts","../../src/chat-routes/upload.ts","../../src/chat-routes/attachment-store.ts","../../src/chat-routes/resolve-attachments.ts","../../src/chat-routes/attachment-write-safety.ts","../../src/chat-routes/promote-file-part.ts","../../src/chat-routes/attachment-upload.ts","../../src/chat-routes/dispatch-parts.ts","../../src/chat-routes/intelligence.ts"],"sourcesContent":["/**\n * Model failover for a STREAMING turn.\n *\n * `/model-resolution`'s `runWithModelFailover` already owns the policy: walk a\n * chain, classify a resolved-or-thrown signal, re-throw a non-outage failure\n * immediately, and carry the attempt trail. This module does NOT re-implement\n * any of that — it composes it. What it adds is the one thing a whole-call\n * primitive cannot express, because a stream fails PARTWAY:\n *\n * **A turn may only fail over before its first client-visible byte.**\n *\n * Once a text delta, tool call, or ask has reached the browser (and the\n * persisted transcript), restarting on another model would duplicate the\n * answer. So each attempt is probed: open the stream, pull events into a small\n * buffer, and decide at the first meaningful event whether this model is\n * serving. Committing replays the buffer and hands the live iterator through;\n * abandoning discards the buffer (those events describe the dead model's\n * session — including its `step-finish` usage, which must never be billed) and\n * lets `runWithModelFailover` walk to the next model.\n *\n * `liveLifecycleEvents` relaxes the buffer for progress only. `start`,\n * `status`, `model-processing`, and session lifecycle carry no answer and no\n * receipt, so a product may take them the moment they arrive and show what\n * the turn is waiting on. Measured 2026-09-11 on a warm production box:\n * OpenCode reports whole parts, so the buffer held every event for 16 s and\n * the product could show nothing through provisioning and agent startup. Off\n * by default: with it on, an abandoned model's progress stays in the stream.\n *\n * The classification itself is `isUpstreamUnavailable` verbatim, so this path\n * inherits the measured facts from the 2026-07-25 outage — above all that an\n * outage is NOT always a thrown error: the sandbox RESOLVES a terminal `error`\n * event carrying `{ errorCode: 'provider_inference_unavailable' }`, which a\n * classifier inspecting only `catch` misses entirely. That resolved shape is\n * the whole reason the breakage went unnoticed, so it is classified here first.\n *\n * Conservative by construction:\n * - A terminal failure that is NOT an outage (400, bad schema, content filter)\n * COMMITS rather than failing over — it surfaces to the user exactly as it\n * does today. Those fail identically on every model; walking the chain would\n * only multiply latency and spend to reach the same error.\n * - A clean stream that produced nothing NEVER walks the chain. An empty answer\n * is not evidence of a dead upstream, and a silent re-roll on another model is\n * precisely the unattributable downgrade this work exists to prevent. Opt-in\n * `emptyTurnRetries` re-runs the SAME model instead, which leaves attribution\n * untouched — measured on production 2026-07-27, an empty turn is a transient\n * platform flake that a same-model re-run recovers (8 hard cases: 7/8\n * delivered on the first pass, 8/8 with one re-run, at a cost of 1 extra turn\n * in 9). Default `0`, so the behavior is unchanged unless a product asks.\n * - A responsive chain of length 1 costs no extra call or latency and remains\n * byte-identical to no failover. A silent chain is now deliberately bounded.\n */\n\nimport {\n isUpstreamUnavailable,\n ModelFailoverExhaustedError,\n readUpstreamUnavailableCode,\n readHttpStatusHint,\n runWithModelFailover,\n type ModelFailoverAttempt,\n} from '../model-resolution/failover'\nimport type { ModelPolicy } from '../policy-filters/index'\nimport { asRecord, asString } from '../stream/index'\n\n/** Terminal event types that end an attempt with a failure verdict. */\nconst TERMINAL_FAILURE_TYPES = new Set(['error', 'session.run.failed'])\n\n/**\n * Part types that carry no transcript content. They are buffered like anything\n * else — replayed verbatim when the attempt commits — but their presence alone\n * never commits a model, so an outage arriving right after a `step-start` is\n * still recoverable. `step-finish` matters most: it carries the abandoned\n * attempt's token/cost receipt, which must be discarded rather than billed.\n */\nconst NON_COMMITTING_PART_TYPES = new Set(['step-start', 'step-finish'])\n\n/**\n * True when `event` puts content in front of the user (or in the persisted\n * transcript), making a restart on another model unsafe.\n *\n * Deliberately an allow-list of KNOWN-INERT types rather than a deny-list: an\n * unrecognized event commits. Getting this wrong in the safe direction costs a\n * missed failover; getting it wrong the other way duplicates a user's answer.\n */\nexport function isCommittingSandboxEvent(event: unknown): boolean {\n const record = asRecord(event)\n if (!record) return false\n const type = asString(record.type) ?? ''\n if (!type) return false\n\n if (type === 'message.part.updated') {\n const part = asRecord(asRecord(record.data)?.part)\n const partType = asString(part?.type) ?? ''\n if (NON_COMMITTING_PART_TYPES.has(partType)) return false\n // A text/reasoning part with NO content yet (the platform opens the part\n // with `text: \"\"` before the first token — verbatim in the 2026-07-26\n // capture) puts nothing in front of the user; discarding it cannot\n // duplicate an answer. The first real token commits.\n if (partType === 'text' || partType === 'reasoning') {\n const text = asString(part?.text) ?? asString(part?.content) ?? ''\n return text.length > 0\n }\n return true\n }\n\n // Session/turn lifecycle, progress status, and warnings belong to the\n // attempt, not the transcript — discardable with an abandoned model.\n // Captured examples (2026-07-26): a dead model\n // emits `start`, `execution.started`, `status`×3, `session.updated`×2,\n // `warning`×2 BEFORE its terminal `error` — any one of them committing\n // would pin the turn to the dead model and kill the failover.\n if (\n type === 'start' ||\n type === 'execution.started' ||\n type === 'status' ||\n type === 'model-processing' ||\n type === 'model.processing' ||\n // Public Sandbox timing metadata: a request was attempted, not answered.\n type === 'model.first_request' ||\n type === 'session.created' ||\n type === 'session.updated' ||\n type === 'session.idle' ||\n type === 'step-start' ||\n type === 'step-finish' ||\n type === 'turn' ||\n type === 'warning'\n ) {\n return false\n }\n\n // Everything else — text/tool shapes the producer folds, asks, plans,\n // `result`/`done`, and any type this shell does not recognize — commits.\n return true\n}\n\n/**\n * Condense an abandoned attempt's raw failure text into something safe to show\n * a customer in the transcript.\n *\n * Measured on the live router 2026-07-27 19:40 UTC: an edge 5xx arrives as\n * Cloudflare's full HTML error PAGE, so the verbatim reason began\n * `<!DOCTYPE html>\\n<!--[if lt IE 7]> <html class=\"no-js ie6 oldie\"…` and the\n * fallback notice pasted 200 bytes of that markup into the answer the customer\n * reads. The cause is worth stating; the markup is not.\n *\n * Only the DISPLAY string is condensed. The full text stays on\n * `modelFailover.attempts[].reason` for the operator, so nothing is lost —\n * this narrows what the customer sees, never what telemetry records.\n */\nexport function summarizeFailoverReason(reason: string): string {\n const collapsed = reason.replace(/\\s+/g, ' ').trim()\n if (!collapsed) return 'upstream unavailable'\n\n // An HTML error page carries no message worth quoting — the status is the\n // whole content. Detected on the ORIGINAL text, before collapsing, because\n // that is the form the producer hands over.\n if (/<!doctype html|<html[\\s>]/i.test(collapsed)) {\n const status = readHttpStatusHint(collapsed)\n return status ? `HTTP ${status}` : 'upstream returned an error page'\n }\n\n // Anything else is a real message. Cap it so a verbose upstream cannot push\n // the answer off the screen, and cut on a word boundary when one is near.\n const LIMIT = 160\n if (collapsed.length <= LIMIT) return collapsed\n const clipped = collapsed.slice(0, LIMIT)\n const lastSpace = clipped.lastIndexOf(' ')\n return `${(lastSpace > LIMIT - 30 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}…`\n}\n\n/** A terminal failure event, classified. `outage` decides failover vs surface. */\ninterface TerminalFailure {\n outage: boolean\n reason: string\n code?: string\n}\n\n/**\n * Classify a terminal failure event. Returns `null` for any non-terminal event.\n *\n * The RESOLVED shape is checked first and deliberately: the sandbox reports an\n * upstream outage by resolving `{ success: false, errorCode:\n * 'provider_inference_unavailable' }` inside a terminal `error` event's `data`,\n * never by throwing. Both `data` and the whole record are offered to\n * `isUpstreamUnavailable` so a payload nested either way is caught.\n */\n/**\n * Progress the consumer may see before the commit point: it names no model\n * output and carries no billing receipt, so streaming it live cannot duplicate\n * an answer or bill an abandoned attempt. `warning` and every\n * `message.part.updated` (including `step-start`/`step-finish`) stay buffered.\n */\nexport function isLiveLifecycleEvent(event: unknown): boolean {\n const type = asString(asRecord(event)?.type) ?? ''\n return (\n type === 'start' ||\n type === 'execution.started' ||\n type === 'status' ||\n type === 'model-processing' ||\n type === 'model.processing' ||\n // Public Sandbox timing metadata: a request was attempted, not answered.\n type === 'model.first_request' ||\n type === 'session.created' ||\n type === 'session.updated' ||\n type === 'session.idle'\n )\n}\n\nexport function classifyTerminalFailure(event: unknown): TerminalFailure | null {\n const record = asRecord(event)\n if (!record) return null\n const type = asString(record.type) ?? ''\n if (!TERMINAL_FAILURE_TYPES.has(type)) return null\n\n const data = asRecord(record.data)\n const outage = isUpstreamUnavailable(data) || isUpstreamUnavailable(record)\n const reason =\n asString(data?.message) ??\n asString(data?.error) ??\n asString(data?.reason) ??\n asString(record.message) ??\n `sandbox stream reported ${type}`\n const code =\n readUpstreamUnavailableCode(data) ??\n asString(data?.errorCode) ??\n asString(data?.code) ??\n readUpstreamUnavailableCode(record) ??\n asString(record.errorCode) ??\n asString(record.code)\n return { outage, reason, ...(code ? { code } : {}) }\n}\n\n/** A model that served: its buffered preamble plus the still-live iterator. */\ninterface AttemptCommit {\n committed: true\n buffered: unknown[]\n /** `null` when the stream already ended during the probe. */\n iterator: AsyncIterator<unknown> | null\n /** Cancel transport work when this attempt is drained or abandoned. */\n abort: () => void\n}\n\n/**\n * A model whose upstream is down. `error`/`errorCode` are read by\n * `runWithModelFailover`'s attempt-trail describe(), so the trail names the\n * real upstream cause rather than a wrapper's own words.\n */\ninterface AttemptOutage {\n committed: false\n error: string\n errorCode?: string\n timeoutCode?: ModelAttemptTimeoutCode\n}\n\ntype AttemptOutcome = AttemptCommit | AttemptOutage\n\n/** Open the raw turn stream for one specific model. */\nexport type OpenModelStream = (args: {\n model: string\n /** 1 for the preferred model, 2 for the first fallback, and so on. */\n attempt: number\n /** Aborted when this attempt times out, is abandoned, or finishes. Forward\n * this to `streamSandboxPrompt(..., { signal })` so a blocked transport is\n * cancelled immediately rather than only receiving iterator.return(). */\n signal: AbortSignal\n}) => AsyncIterable<unknown> | Promise<AsyncIterable<unknown>>\n\n/** Fired when a model is abandoned and the next one is about to be tried. */\nexport interface ModelFallbackInfo {\n from: string\n to: string\n reason: string\n}\n\n/** Define inputs for streaming a turn across a model failover chain */\nexport interface ModelFailoverStreamOptions {\n /** Preferred model first, then fallbacks in descending preference. */\n models: readonly string[]\n open: OpenModelStream\n /**\n * Product admission policy applied to EVERY chain member before its stream\n * is opened — see `runWithModelFailover`'s `policy`. A member the policy\n * refuses is skipped (never opened, never sent the turn's data) and the walk\n * continues; an all-refused chain ends in `ModelFailoverExhaustedError`.\n */\n policy?: ModelPolicy\n /**\n * Maximum time to open/start one model's event source, including its first\n * iterator pull. Default 120 seconds, matching the sandbox provisioning\n * ceiling so cold infrastructure startup is bounded independently from\n * provider inference.\n */\n openTimeoutMs?: number\n /**\n * Hard deadline from the source's first lifecycle event to its first\n * answer-bearing event. Default 60 seconds. Processing/lifecycle events do\n * not commit the attempt and do not extend this deadline.\n */\n firstResponseTimeoutMs?: number\n /** Override the commit-point rule. Default {@link isCommittingSandboxEvent}. */\n isCommitting?: (event: unknown) => boolean\n /**\n * Stream {@link isLiveLifecycleEvent} progress to the consumer as it arrives\n * instead of holding it until the attempt commits. Default `false`, which\n * keeps an abandoned model's events entirely off the wire. With it on, such\n * an event is never replayed from the buffer, and one from an abandoned\n * attempt stays in the stream — it describes the sandbox, not the answer.\n */\n liveLifecycleEvents?: boolean\n onFallback?: (info: ModelFallbackInfo) => void\n /**\n * How many times to RE-RUN THE SAME MODEL when a turn completes having\n * produced no assistant text at all. Default `0` — byte-identical to no\n * retry.\n *\n * This is deliberately not a chain walk. Falling over to a different model\n * on an empty answer is the unattributable downgrade this module refuses to\n * do; re-running the SAME model changes nothing about attribution, because\n * the model that serves is the model that was asked for.\n *\n * Bounded by the same commit rule as failover: only a turn whose ONLY\n * committing event is a terminal receipt with no text is retried, so nothing\n * that reached the user can ever be produced twice.\n */\n emptyTurnRetries?: number\n /** Fired when an empty turn is discarded and the same model re-run. */\n onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** One same-model re-run of a turn that completed with no assistant text. */\nexport interface EmptyTurnRetryInfo {\n model: string\n /** 1 for the first re-run. */\n retry: number\n /** How many re-runs remain after this one. */\n remaining: number\n}\n\n/** The failover-wrapped stream plus the attribution every consumer needs. */\nexport interface ModelFailoverStreamHandle {\n events: AsyncGenerator<unknown, void, unknown>\n /** The model that actually served. `undefined` until the first pull resolves it. */\n servingModel(): string | undefined\n /** Every model tried, in order, with the reason each was abandoned. */\n attempts(): ModelFailoverAttempt[]\n /** True when the preferred model did not serve — the attributability signal. */\n usedFallback(): boolean\n}\n\n/** Default ceiling for opening/starting one source through its first event. */\nexport const DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS = 120_000\n\n/** Default ceiling for the first answer-bearing event after the source's first event. */\nexport const DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS = 60_000\n\n/** Structured timeout codes surfaced by the producer on final exhaustion. */\nexport type ModelAttemptTimeoutCode =\n | 'model_stream_open_timeout'\n | 'provider_first_response_timeout'\n\n/** Every configured model was exhausted and the final one timed out. */\nexport class ModelFailoverTimeoutError extends ModelFailoverExhaustedError {\n readonly code: ModelAttemptTimeoutCode\n readonly model: string\n readonly timeoutMs: number\n\n constructor(input: {\n attempts: ModelFailoverAttempt[]\n code: ModelAttemptTimeoutCode\n model: string\n timeoutMs: number\n }) {\n super(input.attempts)\n this.name = 'ModelFailoverTimeoutError'\n this.code = input.code\n this.model = input.model\n this.timeoutMs = input.timeoutMs\n this.message = input.code === 'model_stream_open_timeout'\n ? `Opening the model event source for ${input.model} exceeded ${input.timeoutMs}ms.`\n : `Model ${input.model} produced no first answer-bearing event within ${input.timeoutMs}ms.`\n }\n}\n\nfunction resolveTimeoutMs(value: number | undefined, fallback: number, label: string): number {\n if (value === undefined) return fallback\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`${label} must be a finite number greater than 0`)\n }\n return Math.max(1, Math.trunc(value))\n}\n\nconst TIMED_OUT = Symbol('model-attempt-timed-out')\n\nfunction deadlineAfter(timeoutMs: number): {\n promise: Promise<typeof TIMED_OUT>\n clear: () => void\n} {\n let timer: ReturnType<typeof setTimeout> | undefined\n const promise = new Promise<typeof TIMED_OUT>((resolve) => {\n timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs)\n })\n return {\n promise,\n clear: () => {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n },\n }\n}\n\n/**\n * True when `event` is a terminal receipt (`result` / `done`) carrying no\n * assistant text.\n *\n * Measured on production `sandbox.tangle.tools` (2026-07-27, 28 turns through\n * the gtm-agent profile): a turn can end `{ outcome: { type: 'completed' } }`\n * with `finalText: ''` and zero token events — the platform reports success and\n * the customer's message is blank. It is not an outage, nothing throws, and\n * `isUpstreamUnavailable` correctly declines to classify it, so before this the\n * blank answer committed and shipped.\n */\nfunction isEmptyTerminalReceipt(event: unknown): boolean {\n const record = asRecord(event)\n if (!record) return false\n const type = asString(record.type) ?? ''\n if (type !== 'result' && type !== 'done') return false\n const data = asRecord(record.data)\n const text = asString(data?.finalText) ?? asString(data?.text) ?? ''\n return text.trim().length === 0\n}\n\n/**\n * Hard ceiling on same-model re-runs. A turn that comes back blank three times\n * running is not a flake this can retry away, and each pass costs a full\n * sandbox turn — so the budget is capped rather than trusted.\n */\nexport const MAX_EMPTY_TURN_RETRIES = 3\n\n/**\n * Coerce the caller's budget to a finite, bounded, non-negative integer.\n *\n * `Math.trunc(NaN)` is `NaN` and `retry >= NaN` is false for every `retry`, so\n * a naive clamp turns a bad config value into a loop that opens sandbox streams\n * until the worker dies. `Infinity` has the same shape. Both resolve to `0` —\n * an unusable budget disables the retry rather than running unbounded.\n */\nexport function resolveEmptyTurnRetries(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 0\n const truncated = Math.trunc(value)\n if (truncated <= 0) return 0\n return Math.min(truncated, MAX_EMPTY_TURN_RETRIES)\n}\n\n/** Abandon a dead attempt's iterator. Never allowed to mask the outage. */\nasync function closeIterator(iterator: AsyncIterator<unknown>, log?: ModelFailoverStreamOptions['log']): Promise<void> {\n try {\n await iterator.return?.()\n } catch (err) {\n log?.('[chat-routes] abandoning a failed model stream threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n}\n\n/**\n * Wrap `open` in reactive model failover, streaming from the first model in\n * `models` that reaches its commit point.\n *\n * Zero added latency on the happy path: the preferred model is opened first and,\n * the moment it emits anything meaningful, its events flow straight through.\n *\n * @throws ModelFailoverTimeoutError when the final model times out,\n * ModelFailoverExhaustedError when every model's upstream is down, and\n * re-throws a non-outage error from the FIRST model without walking the\n * chain (the latter behaviors are inherited from\n * `runWithModelFailover`).\n */\nexport function streamWithModelFailover(\n options: ModelFailoverStreamOptions,\n): ModelFailoverStreamHandle {\n const committing = options.isCommitting ?? isCommittingSandboxEvent\n const emptyTurnRetries = resolveEmptyTurnRetries(options.emptyTurnRetries)\n const openTimeoutMs = resolveTimeoutMs(\n options.openTimeoutMs,\n DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS,\n 'openTimeoutMs',\n )\n const firstResponseTimeoutMs = resolveTimeoutMs(\n options.firstResponseTimeoutMs,\n DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS,\n 'firstResponseTimeoutMs',\n )\n const liveLifecycle = options.liveLifecycleEvents === true\n const progress = createProgressQueue()\n let serving: string | undefined\n let trail: ModelFailoverAttempt[] = []\n let fellBack = false\n let attemptIndex = 0\n let finalTimeout: {\n code: ModelAttemptTimeoutCode\n model: string\n timeoutMs: number\n } | undefined\n\n /**\n * One open-and-drain pass. `empty` marks the pass as an EMPTY TURN — the\n * only committing event was a terminal receipt with no assistant text, so\n * nothing reached the user and the pass can be discarded without any risk of\n * producing an answer twice. It still carries the outcome it would otherwise\n * have returned, so exhausting the retry budget is byte-identical to no\n * retry at all.\n */\n const drainOnce = async (model: string): Promise<{ outcome: AttemptOutcome; empty: boolean }> => {\n attemptIndex += 1\n const controller = new AbortController()\n const abort = (): void => {\n if (!controller.signal.aborted) controller.abort()\n }\n const opening = Promise.resolve().then(() => options.open({\n model,\n attempt: attemptIndex,\n signal: controller.signal,\n }))\n const openDeadline = deadlineAfter(openTimeoutMs)\n let opened: AsyncIterable<unknown> | typeof TIMED_OUT\n try {\n opened = await Promise.race([opening, openDeadline.promise])\n } catch (err) {\n openDeadline.clear()\n abort()\n throw err\n }\n if (opened === TIMED_OUT) {\n openDeadline.clear()\n abort()\n // A Promise cannot be forcibly cancelled. If the abandoned open settles\n // later, close its iterator immediately so it cannot leak a connection.\n void opening.then(\n (lateSource) => {\n try {\n void closeIterator(lateSource[Symbol.asyncIterator](), options.log)\n } catch (err) {\n options.log?.('[chat-routes] closing a late model stream threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n },\n (err) => {\n options.log?.('[chat-routes] abandoned model stream open rejected after timeout', {\n error: err instanceof Error ? err.message : String(err),\n })\n },\n )\n return {\n outcome: {\n committed: false,\n error: `Opening the model event source for ${model} exceeded ${openTimeoutMs}ms`,\n errorCode: 'model_stream_open_timeout',\n timeoutCode: 'model_stream_open_timeout',\n },\n empty: false,\n }\n }\n\n const source = opened\n let iterator: AsyncIterator<unknown>\n try {\n iterator = source[Symbol.asyncIterator]()\n } catch (err) {\n openDeadline.clear()\n abort()\n throw err\n }\n const buffered: unknown[] = []\n // Starting the source includes its first pull. `streamSandboxPrompt` is an\n // async generator, so its cold-start work does not execute until `next()`;\n // counting that time as provider inference would collapse the two phases.\n let next: IteratorResult<unknown> | typeof TIMED_OUT\n try {\n next = await Promise.race([\n Promise.resolve(iterator.next()),\n openDeadline.promise,\n ])\n } catch (err) {\n openDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n openDeadline.clear()\n if (next === TIMED_OUT) {\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: `Starting the model event source for ${model} exceeded ${openTimeoutMs}ms`,\n errorCode: 'model_stream_open_timeout',\n timeoutCode: 'model_stream_open_timeout',\n },\n empty: false,\n }\n }\n if (next.done) {\n abort()\n return { outcome: { committed: true, buffered, iterator: null, abort }, empty: true }\n }\n\n // One deadline for the WHOLE provider pre-response phase. Pulling a\n // lifecycle or processing event races the same promise; liveness cannot\n // reset the clock.\n const firstResponseDeadline = deadlineAfter(firstResponseTimeoutMs)\n\n for (;;) {\n const event = next.value\n let failure: TerminalFailure | null\n let commits = false\n try {\n failure = classifyTerminalFailure(event)\n commits = !failure && committing(event)\n } catch (err) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n if (failure?.outage) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: failure.reason,\n ...(failure.code ? { errorCode: failure.code } : {}),\n },\n empty: false,\n }\n }\n\n if (liveLifecycle && !failure && !commits && isLiveLifecycleEvent(event)) {\n progress.push(event)\n } else {\n buffered.push(event)\n }\n // A terminal NON-outage failure surfaces exactly as it does today.\n if (failure) {\n firstResponseDeadline.clear()\n return { outcome: { committed: true, buffered, iterator, abort }, empty: false }\n }\n if (commits) {\n firstResponseDeadline.clear()\n return { outcome: { committed: true, buffered, iterator, abort }, empty: isEmptyTerminalReceipt(event) }\n }\n\n // A THROWN failure propagates untouched: `runWithModelFailover` classifies\n // it (outage → next model, anything else → re-thrown to the caller).\n try {\n next = await Promise.race([\n Promise.resolve(iterator.next()),\n firstResponseDeadline.promise,\n ])\n } catch (err) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n if (next === TIMED_OUT) {\n firstResponseDeadline.clear()\n // Do not await `return()`: an async generator blocked inside `next()`\n // may not settle its return until that pull completes. Invoking it is\n // the cancellation signal; waiting for it would recreate the hang.\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: `Model ${model} produced no first answer-bearing event within ${firstResponseTimeoutMs}ms`,\n errorCode: 'provider_first_response_timeout',\n timeoutCode: 'provider_first_response_timeout',\n },\n empty: false,\n }\n }\n if (next.done) {\n firstResponseDeadline.clear()\n abort()\n // Clean end with nothing committing. Not an outage — but also not an\n // answer, so it is re-runnable on the same model.\n return { outcome: { committed: true, buffered, iterator: null, abort }, empty: true }\n }\n }\n }\n\n const probe = async (model: string): Promise<AttemptOutcome> => {\n // Only the FINAL attempted model decides the exhaustion code. Clear a\n // prior model's timeout before this model can throw a different outage.\n finalTimeout = undefined\n for (let retry = 0; ; retry += 1) {\n const pass = await drainOnce(model)\n if (!pass.empty || retry >= emptyTurnRetries) {\n if (!pass.outcome.committed && pass.outcome.timeoutCode) {\n finalTimeout = {\n code: pass.outcome.timeoutCode,\n model,\n timeoutMs: pass.outcome.timeoutCode === 'model_stream_open_timeout'\n ? openTimeoutMs\n : firstResponseTimeoutMs,\n }\n } else {\n finalTimeout = undefined\n }\n return pass.outcome\n }\n // Discard the empty pass entirely — including its `step-finish` token\n // receipt, which must not be billed — and re-run the same model.\n const iterator = pass.outcome.committed ? pass.outcome.iterator : null\n if (pass.outcome.committed) pass.outcome.abort()\n if (iterator) void closeIterator(iterator, options.log)\n const info: EmptyTurnRetryInfo = { model, retry: retry + 1, remaining: emptyTurnRetries - retry - 1 }\n options.log?.('[chat-routes] turn completed with no assistant text; re-running the same model', {\n ...info,\n })\n options.onEmptyTurnRetry?.(info)\n }\n }\n\n const events = (async function* (): AsyncGenerator<unknown, void, unknown> {\n let handle: AttemptCommit\n // Progress arrives while the probe is still running. The queue closes only\n // after the verdict settles, so every event pushed before it is delivered\n // in order and ahead of the replayed buffer.\n const settled = runWithModelFailover<AttemptOutcome>({\n models: options.models,\n run: probe,\n ...(options.policy ? { policy: options.policy } : {}),\n // The probe has already classified the raw payload with\n // `isUpstreamUnavailable`; this reads its verdict rather than\n // re-classifying a wrapper object, so the two can never disagree.\n isUnavailableResult: (result) => result.committed === false,\n onFallback: (attempt, nextModel) => {\n const info: ModelFallbackInfo = {\n from: attempt.model,\n to: nextModel,\n reason: attempt.reason ?? 'upstream unavailable',\n }\n options.log?.('[chat-routes] model upstream unavailable; falling over', { ...info })\n options.onFallback?.(info)\n },\n }).finally(() => progress.close())\n // The verdict is read after the drain; mark the rejection handled until then.\n settled.catch(() => undefined)\n for (;;) {\n const item = await progress.pull()\n if (item === PROGRESS_CLOSED) break\n yield item\n }\n try {\n const outcome = await settled\n serving = outcome.model\n trail = outcome.attempts\n fellBack = outcome.usedFallback\n handle = outcome.value as AttemptCommit\n } catch (err) {\n // Exhaustion carries the full trail; keep it so the receipt still names\n // every model tried even though the turn produced nothing.\n const attempts = (err as { attempts?: ModelFailoverAttempt[] })?.attempts\n if (Array.isArray(attempts)) trail = attempts\n if (err instanceof ModelFailoverExhaustedError && finalTimeout) {\n throw new ModelFailoverTimeoutError({\n attempts: trail,\n ...finalTimeout,\n })\n }\n throw err\n }\n\n for (const event of handle.buffered) yield event\n\n const live = handle.iterator\n if (!live) {\n handle.abort()\n return\n }\n try {\n for (;;) {\n const next = await live.next()\n if (next.done) return\n yield next.value\n }\n } finally {\n // The consumer may abandon this generator mid-turn (client drop); close\n // the underlying stream rather than leaking it.\n handle.abort()\n await closeIterator(live, options.log)\n }\n })()\n\n return {\n events,\n servingModel: () => serving,\n attempts: () => trail,\n usedFallback: () => fellBack,\n }\n}\n\nconst PROGRESS_CLOSED: unique symbol = Symbol('progress-closed')\n\n/**\n * FIFO handoff from the probe (a plain promise chain) to the event generator.\n * `close` is idempotent and queues one sentinel behind everything pushed\n * before it, so a consumer never loses a progress event to the verdict.\n */\nfunction createProgressQueue(): {\n push(event: unknown): void\n close(): void\n pull(): Promise<unknown>\n} {\n const items: unknown[] = []\n const waiters: Array<(value: unknown) => void> = []\n let closed = false\n const deliver = (value: unknown): void => {\n const waiter = waiters.shift()\n if (waiter) waiter(value)\n else items.push(value)\n }\n return {\n push(event) {\n if (closed) return\n deliver(event)\n },\n close() {\n if (closed) return\n closed = true\n deliver(PROGRESS_CLOSED)\n },\n pull() {\n if (items.length > 0) return Promise.resolve(items.shift())\n if (closed) return Promise.resolve(PROGRESS_CLOSED)\n return new Promise((resolve) => waiters.push(resolve))\n },\n }\n}\n","/**\n * When each reasoning run of a turn started and ended, so every chat producer\n * persists one reasoning part shape: `{ type: 'reasoning', text, time: { start, end } }`\n * (`ChatReasoningPart` in `/chat-store`).\n *\n * The engine (`./router-producer`) and the sandbox (`./sandbox-producer`) both\n * stream reasoning live as `{ type: 'reasoning', text }` deltas. This clock gives\n * their persisted parts the same timing: a run starts at its first delta and\n * ends when anything else streams (answer text, a tool) or the turn ends. A\n * harness that timed its own part keeps its times; the clock fills only what it\n * left out. `/web-react` reads the result back (`chatReasoningFromParts`), so a\n * reloaded turn shows how long the model thought.\n *\n * Import-free, so the browser-safe chat stream can share the part's type.\n */\n\n/** Start and end, in epoch milliseconds, of one reasoning run. */\nexport interface ReasoningRunTime {\n start?: number\n end?: number\n}\n\n/** The clock one producer keeps for one turn. `K` is how the producer names a run: a part key or the part itself. */\nexport interface ReasoningClock<K> {\n /** A reasoning delta for `run` streamed. Starts its clock on the first; ends any other open run. */\n reasoning(run: K): void\n /** Something other than reasoning streamed, or the turn ended: the open run is over, at `at` (default now). */\n settle(at?: number): void\n /** The run's time, the part's own `start` and `end` winning where it has them. */\n time(run: K, own?: ReasoningRunTime): ReasoningRunTime | undefined\n}\n\n/** A clock for one turn's reasoning runs. */\nexport function createReasoningClock<K>(now: () => number = Date.now): ReasoningClock<K> {\n const runs = new Map<K, ReasoningRunTime>()\n let open: { run: K } | null = null\n const settle = (at?: number) => {\n if (!open) return\n const time = runs.get(open.run)\n if (time && time.end === undefined) time.end = Math.max(time.start ?? 0, at ?? now())\n open = null\n }\n return {\n reasoning(run) {\n if (open && open.run === run) return\n settle()\n const time = runs.get(run)\n // The same run resumed after an interruption: it is open again.\n if (time) delete time.end\n else runs.set(run, { start: now() })\n open = { run }\n },\n settle,\n time(run, own) {\n const time = runs.get(run)\n if (!time) return own\n const start = own?.start ?? time.start\n const end = own?.end ?? time.end\n return { ...(start !== undefined ? { start } : {}), ...(end !== undefined ? { end } : {}) }\n },\n }\n}\n","import { asRecord, type JsonRecord } from '../stream/index'\nimport type { ChatTurnUsage } from './turn-routes'\n\n/** Add one canonical Sandbox `step-finish` receipt to a turn total. */\nexport function addStepFinishUsage(part: JsonRecord, usage: ChatTurnUsage): void {\n const tokens = asRecord(part.tokens)\n if (tokens) {\n const cache = asRecord(tokens.cache)\n const add = (current: number | undefined, value: unknown): number | undefined => {\n const n = Number(value)\n if (!Number.isFinite(n)) return current\n return (current ?? 0) + n\n }\n usage.inputTokens = add(usage.inputTokens, tokens.input)\n usage.outputTokens = add(usage.outputTokens, tokens.output)\n usage.reasoningTokens = add(usage.reasoningTokens, tokens.reasoning)\n if (cache) {\n usage.cacheReadTokens = add(usage.cacheReadTokens, cache.read)\n usage.cacheWriteTokens = add(usage.cacheWriteTokens, cache.write)\n }\n }\n const cost = Number(part.cost)\n if (Number.isFinite(cost)) usage.costUsd = (usage.costUsd ?? 0) + cost\n}\n","/**\n * Sandbox lane: bridge a raw sandbox event stream (`streamSandboxPrompt`) into\n * the `ChatTurnProducer` shape agent-runtime's `handleChatTurn` consumes AND\n * the client vocabulary `/web-react`'s `dispatchChatStreamLine` already parses\n * (`text` / `reasoning` / `tool_call` / `tool_result` / `usage` /\n * `notice` / structured `error` / `interaction`). Legal and tax each hand-rolled\n * this mapping differently;\n * this is that middle, composed from `/stream`'s normalizers — no new loop\n * logic, no SDK import (the event source is an injected `AsyncIterable`).\n *\n * Alongside the live mapping it accumulates the PERSISTED projection — the\n * `message.parts` rows `/chat-store` stores — via `normalizePersistedPart` /\n * `mergePersistedPart` / `finalizeAssistantParts`, plus the usage receipt from\n * `step-finish` parts. `createChatTurnRoutes` reads both after drain.\n */\n\nimport {\n cancelStatusFor,\n interactionPartKey,\n interactionToPersistedPart,\n isRenderableInteractionKind,\n noticePart,\n noticePartKey,\n parseInteractionCancel,\n parseInteractionRequest,\n type InteractionRequestWire,\n} from '../interactions/contract'\nimport {\n parsePlanSubmittedEvent,\n planToPersistedPart,\n} from '../plans/index'\nimport {\n asRecord,\n asString,\n draftAssistantParts,\n finalizeAssistantParts,\n finalizePendingInteractionParts,\n getPartKey,\n mergePersistedPart,\n normalizePersistedPart,\n normalizeToolEvent,\n terminalizeDanglingAssistantToolUpdates,\n type JsonRecord,\n type StreamEvent,\n} from '../stream/index'\nimport {\n buildModelChain,\n ModelFailoverExhaustedError,\n type ModelFailoverAttempt,\n} from '../model-resolution/failover'\nimport type { ModelPolicy } from '../policy-filters/index'\nimport {\n ModelFailoverTimeoutError,\n resolveEmptyTurnRetries,\n streamWithModelFailover,\n summarizeFailoverReason,\n type EmptyTurnRetryInfo,\n type ModelFallbackInfo,\n type ModelFailoverStreamHandle,\n type OpenModelStream,\n} from './model-failover-stream'\nimport { createReasoningClock, type ReasoningRunTime } from './reasoning-clock'\nimport type {\n ChatTurnModelAttribution,\n ChatTurnRouteProducer,\n ChatTurnUsage,\n} from './turn-routes'\nimport { addStepFinishUsage } from './sandbox-turn-usage'\nimport type { ProducerWireEvent } from './wire'\n\n/** Outcome of a `promoteFilePart` attempt. `key`, when given, becomes the\n * persisted part's row key (e.g. `attachment:<path>`) so repeat promotions\n * of the same underlying file fold into one segment instead of appending;\n * omitted, the default `getPartKey` keying applies.\n *\n * On failure, `part` is an OPTIONAL substitute part to persist in place of\n * the raw url-bearing one — this is how a product swaps in a transcript\n * notice (gtm persists a `warning` notice part, never the transient url) for\n * a failed promotion instead of baking a `data:`/sandbox-path url into the\n * durable row. When `part` is present it is persisted (via the same\n * `recordPersistedPart` path as a success, honoring the optional `key`);\n * when absent, the existing raw-part fallback applies unchanged — so a\n * caller that only returns `{ succeeded: false, reason }` keeps today's\n * behavior verbatim. */\nexport type FilePartPromotionOutcome =\n | { succeeded: true; part: Record<string, unknown>; key?: string }\n | { succeeded: false; reason: string; part?: Record<string, unknown>; key?: string }\n\n/** Define options for producing sandbox chat events with rendering and interaction controls */\nexport interface SandboxChatProducerOptions {\n /** The raw sandbox event stream (e.g. `streamSandboxPrompt(...)`).\n *\n * An ALREADY-OPEN stream is bound to one model and cannot be reopened, so\n * this form can never fail over. Prefer {@link openEvents}; exactly one of\n * the two is required. */\n events?: AsyncIterable<unknown>\n /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form\n * of {@link events}. The callback receives the model to run and returns the\n * same `AsyncIterable` `events` would have been (typically\n * `streamSandboxPrompt(shell, box, prompt, { ...opts, model })`).\n *\n * Wiring this turns failover ON with no further flag: whenever the resolved\n * chain (`model` + {@link fallbackModels}) holds more than one entry, a model\n * whose upstream is dead is abandoned BEFORE its first client-visible byte\n * and the next one is tried. A one-entry chain opens exactly once — no extra\n * call, no added latency, byte-identical to today.\n *\n * Requires {@link model}: failover has to know which model it is running. */\n openEvents?: OpenModelStream\n /** Recorded on the persisted assistant message. When failover moves the turn\n * to another model, the producer's `model` reports the model that ACTUALLY\n * served, not this preferred one — see {@link modelFailover}. */\n model?: string\n /** Models to try, in order, when `model`'s upstream is dead. Product config,\n * never a shell default: agent-app cannot know which ids are live, and a\n * baked list would rot into exactly the stale-liveness bug this guards\n * against. Empty/omitted → one attempt, today's behavior.\n *\n * Pick these deliberately. A same-family fallback is NOT automatically safe:\n * `gemini-2.5-flash` produced zero persisted deliverables in 2 of 3 measured\n * runs on a med-legal filing flow where `gemini-2.5-pro` succeeded. That is\n * why every fallback is surfaced (persisted `model`, a transcript notice, and\n * `modelFailover()`) instead of being applied silently. */\n fallbackModels?: readonly string[]\n /** Opt out of failover while still using {@link openEvents}. `false` collapses\n * the chain to `model` alone. */\n modelFailover?: false\n /**\n * Product admission policy applied to every chain member — the RESOLVED\n * preferred model AND every fallback — before its stream is opened. This is\n * the execution-time half of policy enforcement: it covers the model\n * whatever its source (request body, env, product default), and it is what\n * stops a compliant model's mid-turn 502 from re-running the turn's data\n * through a non-compliant fallback. A refused member is skipped with the\n * refusal recorded in `modelFailover().attempts`; an all-refused chain\n * exhausts. Omit → availability only (today's behavior).\n */\n modelPolicy?: ModelPolicy\n /** Maximum time to open/start one model event source through its first event.\n * Default 120 seconds. This is separate from provider first-output latency\n * so cold sandbox startup is not mislabeled as an inference failure. */\n openTimeoutMs?: number\n /** Hard deadline after the source's first lifecycle event for the first\n * answer-bearing event. Default 60 seconds. Processing/lifecycle events do\n * not extend it. */\n firstResponseTimeoutMs?: number\n /** Forward `start`/`status`/`model-processing`/session lifecycle to the\n * client as it arrives instead of after the commit point, so a product can\n * show what a turn is waiting on. Default `false`. An abandoned model's\n * progress then stays on the wire; its answer and receipt never do. */\n liveLifecycleEvents?: boolean\n /** Fired when a model is abandoned mid-chain (telemetry/alerting). The user\n * already sees a transcript notice; this is for the operator. */\n onModelFallback?: (info: ModelFallbackInfo) => void\n /** Re-run the SAME model this many times when a turn completes with no\n * assistant text at all. Default `0` (unchanged behavior). Requires\n * {@link openEvents} — there is nothing to re-open on a fixed stream.\n *\n * Distinct from {@link fallbackModels} on purpose: this never changes which\n * model answers, so it carries none of the attribution risk a downgrade\n * does. Measured on production 2026-07-27 through gtm-agent's profile, a\n * completed-but-blank turn is a transient platform flake — 8 hard cases went\n * 7/8 delivered to 8/8 with one re-run, costing 1 extra turn in 9. */\n emptyTurnRetries?: number\n /** Fired when a blank turn is discarded and the same model re-run. */\n onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void\n /** Which ask kinds the product renders a card for. Anything else is\n * auto-declined (see `declineInteraction`) so the run never hangs in the\n * broker waiting on a card no client will show. Default: question/plan.\n * Products with per-turn plan mode can close over it without another option:\n * `(kind) => kind === 'question' || (kind === 'plan' && planEnabled)`.\n * The parsed request lets a product decline an ask by its content, such\n * as a question that duplicates a decision the product already shows. */\n isRenderableInteraction?: (kind: string, request: InteractionRequestWire) => boolean\n /** The transcript notice for an auto-declined ask; defaults to a policy line naming its kind. */\n declinedNotice?: (request: InteractionRequestWire, declineFailed: boolean) => string\n /** Resolve a non-renderable ask (wire `respondToSessionInteraction` with the\n * session's sidecar connection). Without it, a failure notice is emitted and\n * the run stays blocked until the broker times out. */\n declineInteraction?: (id: string) => Promise<void>\n /** Opt-in eager promotion of harness-emitted `file` parts. Unset, a `file`\n * part persists exactly as the harness sent it — a transient `url` (a\n * `data:` URI or in-sandbox path) baked into the transcript, which is\n * today's behavior and stays byte-identical if this is never wired. Set,\n * EVERY `file` part (never `image`, never any other kind) is routed\n * through this callback instead of `recordPersistedPart`'s default\n * fallback — including a part with NEITHER `id` NOR `url` (gtm always\n * attempts promotion; such a part simply fails \"carries no url\" and\n * resolves through the same failure path as any other rejection, rather\n * than being persisted raw and unpromoted) — so the product can durably\n * write the bytes and swap in a path-bearing part before the raw url ever\n * reaches the persisted transcript. Keyed per source-prefixed `id:<id>` /\n * `url:<url>` (an `id` and a `url` sharing the same text must never collide\n * onto one memo entry) and memoized by PROMISE (not result), so re-emitted\n * snapshot events for the same part —\n * the harness resends the whole part on every update, not just deltas —\n * fold onto the one in-flight or settled attempt rather than promoting\n * twice or racing two concurrent writes; a raw part with neither `id` nor\n * `url` cannot be keyed, so it is invoked UN-memoized (once per event) —\n * each occurrence is its own attempt. A rejecting promise is caught,\n * logged via `log`, and treated as `succeeded: false`. On `succeeded:\n * false` the outcome's optional `part` (a substitute — e.g. a warning\n * notice — see {@link FilePartPromotionOutcome}) persists in its place when\n * given; otherwise the raw part persists exactly as it does today — this\n * seam only decides whether to call the promoter and what to do with its\n * outcomes; the promotion mechanics (vault write, key derivation, notice\n * construction) live in the caller's callback, not here. */\n promoteFilePart?: (raw: JsonRecord) => Promise<FilePartPromotionOutcome>\n /** Append terminal error explanations to assistant text. Default true.\n * Set false when the application renders the structured error separately.\n * Error events, diagnostics, partial answers and tool settlement remain available. */\n includeErrorText?: boolean\n /** Record the terminal error explanation as an `error` notice part (persisted\n * on the assistant row and streamed as a `notice` event) instead of appending\n * it to the assistant text. A reloaded failed turn then carries a typed marker\n * a client renders in the danger tone, rather than prose indistinguishable\n * from the answer. Default false (today's text behavior). When true, the\n * error is never appended to the text, whatever `includeErrorText` says. */\n errorNotice?: boolean\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\ninterface TextTracker {\n /** Full accumulated text per part key, to derive suffix deltas from\n * snapshot-only harness events. */\n seen: Map<string, string>\n}\n\n/** Delta to emit for one text/reasoning part update: prefer the harness's\n * explicit delta; otherwise diff the snapshot against what was already\n * emitted for that part (snapshot-only harnesses re-send the whole text). */\nfunction textDelta(tracker: TextTracker, key: string, part: JsonRecord, rawDelta: unknown): string {\n const explicit = typeof rawDelta === 'string' ? rawDelta : undefined\n const previous = tracker.seen.get(key) ?? ''\n if (explicit !== undefined) {\n tracker.seen.set(key, previous + explicit)\n return explicit\n }\n const snapshot = asString(part.text) ?? asString(part.content) ?? ''\n if (!snapshot) return ''\n if (snapshot.startsWith(previous)) {\n tracker.seen.set(key, snapshot)\n return snapshot.slice(previous.length)\n }\n // The snapshot replaced the text outright — emit it whole; the persisted\n // projection stays correct because finalText is authoritative at finalize.\n tracker.seen.set(key, snapshot)\n return snapshot\n}\n\nfunction parseEffectiveBackend(data: JsonRecord): Omit<\n ChatTurnModelAttribution,\n 'requestedModel' | 'echoReceived'\n> | undefined {\n const backend = asRecord(data.effectiveBackend)\n if (!backend) return undefined\n const provider = asString(backend.provider)\n const model = asString(backend.model)\n const rawSource = asString(backend.source)\n const source =\n rawSource === 'request' || rawSource === 'environment' || rawSource === 'profile'\n ? rawSource\n : undefined\n if (!provider && !model && !source) return undefined\n return {\n ...(model ? { servedModel: model } : {}),\n ...(provider ? { servedProvider: provider } : {}),\n ...(source ? { servedSource: source } : {}),\n }\n}\n\nfunction toFiniteNumber(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\n/** Parse authoritative usage from a terminal sandbox `result` or `done`. */\nfunction extractReportedTurnUsage(data: JsonRecord | null | undefined): ChatTurnUsage | null {\n const tokenUsage = asRecord(data?.tokenUsage)\n if (!tokenUsage) return null\n\n const inputTokens = toFiniteNumber(tokenUsage.inputTokens)\n const outputTokens = toFiniteNumber(tokenUsage.outputTokens)\n if (inputTokens === null || outputTokens === null) return null\n\n const reported: ChatTurnUsage = { inputTokens, outputTokens }\n const reasoningTokens = toFiniteNumber(tokenUsage.reasoningTokens)\n if (reasoningTokens !== null) reported.reasoningTokens = reasoningTokens\n const cacheReadTokens = toFiniteNumber(tokenUsage.cacheReadInputTokens)\n if (cacheReadTokens !== null) reported.cacheReadTokens = cacheReadTokens\n const cacheWriteTokens = toFiniteNumber(tokenUsage.cacheCreationInputTokens)\n if (cacheWriteTokens !== null) reported.cacheWriteTokens = cacheWriteTokens\n const costUsd = toFiniteNumber(data?.totalCostUsd) ?? toFiniteNumber(tokenUsage.cost)\n if (costUsd !== null) reported.costUsd = costUsd\n return reported\n}\n\n/** Native runtime usage events contain cumulative totals, including repeated terminal snapshots. */\nfunction extractUsageSnapshot(value: JsonRecord | null | undefined): ChatTurnUsage | null {\n const inputTokens = toFiniteNumber(value?.promptTokens)\n const outputTokens = toFiniteNumber(value?.completionTokens)\n if (inputTokens === null || outputTokens === null || inputTokens < 0 || outputTokens < 0) return null\n const reported: ChatTurnUsage = { inputTokens, outputTokens }\n const reasoningTokens = toFiniteNumber(value?.reasoningTokens)\n if (reasoningTokens !== null && reasoningTokens >= 0) reported.reasoningTokens = reasoningTokens\n const costUsd = toFiniteNumber(value?.providerCostUsd)\n if (costUsd !== null && costUsd >= 0) reported.costUsd = costUsd\n return reported\n}\n\nfunction applyTerminalUsage(reported: ChatTurnUsage, usage: ChatTurnUsage): void {\n usage.inputTokens = reported.inputTokens\n usage.outputTokens = reported.outputTokens\n if (reported.reasoningTokens !== undefined) usage.reasoningTokens = reported.reasoningTokens\n if (reported.cacheReadTokens !== undefined) usage.cacheReadTokens = reported.cacheReadTokens\n if (reported.cacheWriteTokens !== undefined) usage.cacheWriteTokens = reported.cacheWriteTokens\n if (reported.costUsd !== undefined) usage.costUsd = reported.costUsd\n}\n\nfunction sandboxStreamErrorMessage(data: unknown): string {\n const record = asRecord(data)\n const message = asString(record?.message) ?? asString(record?.error)\n if (message) return message\n try {\n return JSON.stringify(data) || 'Sandbox stream returned an error event without details.'\n } catch {\n return 'Sandbox stream returned an error event without details.'\n }\n}\n\n/** The typed reason a sandbox `error` event carries, or a generic stream-error code. */\nfunction sandboxStreamErrorCode(data: unknown): string {\n return asString(asRecord(data)?.code) ?? 'sandbox.stream_error'\n}\n\nfunction toProducerWireEvent(event: JsonRecord): ProducerWireEvent {\n return event as unknown as ProducerWireEvent\n}\n\nfunction sandboxStreamFailureDiagnostic(error: unknown): {\n userMessage: string\n failureNote: string\n code: string\n} {\n const streamMessage = (error as { streamMessage?: unknown })?.streamMessage\n const message = typeof streamMessage === 'string'\n ? streamMessage\n : error instanceof Error ? error.message : 'Sandbox stream failed'\n const diagnostics = asRecord((error as { diagnostics?: unknown })?.diagnostics)\n let diagnosticText: string | undefined\n if (diagnostics) {\n try {\n diagnosticText = JSON.stringify(diagnostics)\n } catch {\n diagnosticText = '[unserializable diagnostics]'\n }\n }\n const userMessage = [\n 'The sandbox model stream stopped before a clean completion.',\n `Error: ${message}`,\n 'Please retry. If it repeats, send these support details to the team.',\n ].join('\\n\\n')\n const failureNote = diagnosticText\n ? `sandbox-stream: ${message}; ${diagnosticText}`\n : `sandbox-stream: ${message}`\n let code = 'sandbox.stream_failed'\n if (error instanceof ModelFailoverTimeoutError) {\n code = error.code\n } else if (error instanceof ModelFailoverExhaustedError) {\n code = error.upstreamCode ?? code\n }\n return { userMessage, failureNote, code }\n}\n\n/** Create a sandbox chat producer that manages chat turn routing with logging and interaction rendering options */\nexport function createSandboxChatProducer(options: SandboxChatProducerOptions): ChatTurnRouteProducer {\n const log = options.log ?? ((message, meta) => console.error(message, meta ?? ''))\n const renderable = options.isRenderableInteraction ?? isRenderableInteractionKind\n\n // ── model failover ────────────────────────────────────────────────────────\n // ON by default whenever the caller can reopen the stream (`openEvents`) and\n // named more than one model. There is no `enabled` flag on purpose: the class\n // of failure this fixes was a capability that shipped switched off, and a\n // second opt-in on top of supplying the chain would reproduce it exactly.\n if (options.openEvents && !options.model) {\n throw new Error(\n 'createSandboxChatProducer: `openEvents` requires `model` — failover must know which model it is running',\n )\n }\n if (!options.openEvents && !options.events) {\n throw new Error('createSandboxChatProducer: pass `openEvents` (failover-capable) or `events`')\n }\n // A fixed `events` stream cannot be re-opened, so the retry silently does\n // nothing. A config that asks for recovery and receives none is the exact\n // silent-no-op class this seam exists to remove — refuse it at construction\n // rather than let a product believe blank turns are being retried.\n if (!options.openEvents && resolveEmptyTurnRetries(options.emptyTurnRetries) > 0) {\n throw new Error(\n 'createSandboxChatProducer: `emptyTurnRetries` requires `openEvents` — a fixed `events` stream cannot be re-opened',\n )\n }\n const chain = options.model\n ? buildModelChain(options.model, options.modelFailover === false ? [] : (options.fallbackModels ?? []))\n : []\n\n /** Fallback notices waiting to be emitted. `onFallback` fires from inside the\n * first pull of the failover stream, where a generator cannot yield, so the\n * notice is queued and drained at the top of the next loop iteration —\n * landing immediately before the serving model's first event. */\n const pendingModelNotices: Array<{ id: string; text: string }> = []\n let modelNoticeCount = 0\n\n let failover: ModelFailoverStreamHandle | undefined\n let source: AsyncIterable<unknown>\n if (options.openEvents) {\n failover = streamWithModelFailover({\n models: chain,\n open: options.openEvents,\n log,\n ...(options.modelPolicy ? { policy: options.modelPolicy } : {}),\n ...(options.openTimeoutMs !== undefined ? { openTimeoutMs: options.openTimeoutMs } : {}),\n ...(options.firstResponseTimeoutMs !== undefined\n ? { firstResponseTimeoutMs: options.firstResponseTimeoutMs }\n : {}),\n ...(options.emptyTurnRetries !== undefined ? { emptyTurnRetries: options.emptyTurnRetries } : {}),\n ...(options.onEmptyTurnRetry ? { onEmptyTurnRetry: options.onEmptyTurnRetry } : {}),\n ...(options.liveLifecycleEvents !== undefined ? { liveLifecycleEvents: options.liveLifecycleEvents } : {}),\n onFallback: (info) => {\n modelNoticeCount += 1\n pendingModelNotices.push({\n id: `model-fallback-${modelNoticeCount}`,\n // Named models on both sides: a quality regression after a downgrade\n // must be attributable to the model that actually answered. The\n // REASON is condensed — a real edge 5xx arrives as Cloudflare's HTML\n // error page, and the raw text put `<!DOCTYPE html><!--[if lt IE 7]>…`\n // into the customer's transcript. The verbatim text is still on\n // `modelFailover.attempts[].reason` for the operator.\n text: `${info.from} was unavailable (${summarizeFailoverReason(info.reason)}) — answered with ${info.to} instead.`,\n })\n options.onModelFallback?.(info)\n },\n })\n source = failover.events\n } else {\n source = options.events!\n }\n\n /** The model that ACTUALLY served this turn. Read lazily everywhere (the\n * `model` getter, the draft snapshot, the persisted row) so a fallback that\n * happens after the row was drafted still lands on the final write. */\n const servingModel = (): string | undefined => failover?.servingModel() ?? options.model\n let effectiveBackend: ReturnType<typeof parseEffectiveBackend>\n let substitutionNoticeQueued = false\n const servedModel = (): string | undefined => effectiveBackend?.servedModel ?? servingModel()\n\n const captureEffectiveBackend = (data: JsonRecord | undefined): void => {\n if (!data) return\n const parsed = parseEffectiveBackend(data)\n if (!parsed) return\n effectiveBackend = parsed\n const sentModel = servingModel()\n if (\n substitutionNoticeQueued ||\n !options.model ||\n !sentModel ||\n !parsed.servedModel ||\n parsed.servedModel === sentModel\n ) return\n substitutionNoticeQueued = true\n pendingModelNotices.push({\n id: 'model-substitution-1',\n text: `Requested ${sentModel} — the sandbox answered with ${parsed.servedModel} instead.`,\n })\n }\n\n let fullText = ''\n let terminalObserved = false\n const partOrder: string[] = []\n const partMap = new Map<string, JsonRecord>()\n const tracker: TextTracker = { seen: new Map() }\n /** Each reasoning part's start and end where the harness sent none: the same timing the engine's producer persists. */\n const reasoningClock = createReasoningClock<string>()\n const usage: ChatTurnUsage = {}\n /** Whether each announced tool_call carried populated arguments. */\n const announcedToolArgs = new Map<string, boolean>()\n const settledTools = new Set<string>()\n /** Id-less step boundaries: one occurrence per key, never merged. */\n let stepCounter = 0\n let danglingToolsTerminalized = false\n let interactionOutcome: 'answered' | 'expired' = 'answered'\n let warningCount = 0\n /** `promoteFilePart` memo, keyed by `id ?? url` of the raw part. Holds the\n * PROMISE (never the settled result) so concurrent duplicate events for\n * the same part await the one in-flight attempt instead of each starting\n * their own; the promise never rejects (see below), so a later duplicate\n * reading a settled entry reuses that first outcome — success or\n * failure — rather than retrying. */\n const promotedFileParts = new Map<string, Promise<FilePartPromotionOutcome>>()\n\n function recordPersistedPart(part: JsonRecord, delta: string | undefined, keyOverride?: string): void {\n const persisted = normalizePersistedPart(part)\n if (!persisted) return\n const key = keyOverride ?? getPartKey(persisted)\n if (!partMap.has(key)) partOrder.push(key)\n partMap.set(key, mergePersistedPart(partMap.get(key), persisted, delta))\n }\n\n function* emitTerminalizedTools(): Generator<ProducerWireEvent, void, unknown> {\n if (danglingToolsTerminalized) return\n danglingToolsTerminalized = true\n const updates = terminalizeDanglingAssistantToolUpdates(partOrder, partMap, fullText)\n for (const part of updates) {\n const toolId = asString(part.id)\n if (!toolId || settledTools.has(toolId)) continue\n settledTools.add(toolId)\n const state = asRecord(part.state)\n yield {\n type: 'tool_result',\n toolCallId: toolId,\n toolName: asString(part.tool) ?? 'tool',\n outcome: {\n ok: false,\n ...(asString(state?.error) ? { message: asString(state?.error) } : {}),\n },\n }\n }\n }\n\n function* emitFinalUsage(): Generator<ProducerWireEvent, void, unknown> {\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n if (promptTokens || completionTokens) {\n yield {\n type: 'usage',\n usage: {\n promptTokens,\n completionTokens,\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n ...(usage.costUsd !== undefined ? { providerCostUsd: usage.costUsd } : {}),\n },\n }\n }\n }\n\n /** Emit any queued fallback notices, persisting each so the downgrade is in\n * the durable transcript and not only in the live stream. Uses the existing\n * `warning` notice kind deliberately: every shipped client already renders\n * it, whereas a bespoke kind would be dropped by `dispatchChatStreamLine`'s\n * filter — an attribution nobody sees is not attribution. */\n function* drainModelNotices(): Generator<ProducerWireEvent, void, unknown> {\n while (pendingModelNotices.length > 0) {\n const queued = pendingModelNotices.shift()!\n const notice = noticePart('warning', queued.id, queued.text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'warning', text: queued.text }\n }\n }\n\n let errorNoticeCount = 0\n /** `errorNotice` mode: the terminal error as a persisted `error` notice part,\n * so the durable row says \"this turn failed\" in a typed field. */\n function* emitErrorNotice(text: string, code: string): Generator<ProducerWireEvent, void, unknown> {\n errorNoticeCount += 1\n const notice = noticePart('error', `error-${errorNoticeCount}`, text, code)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'error', text, code }\n }\n\n async function* stream(): AsyncGenerator<ProducerWireEvent, void, unknown> {\n try {\n for await (const raw of source) {\n const record = asRecord(raw)\n captureEffectiveBackend(asRecord(record?.data))\n yield* drainModelNotices()\n if (!record || typeof record.type !== 'string') continue\n // Fold bare tool_call/tool_result shapes into the canonical part event;\n // everything else keeps its original record (verbatim forwarding must\n // not strip fields outside `data`).\n const normalized = normalizeToolEvent({ type: record.type, data: asRecord(record.data) })\n const event: StreamEvent = normalized.type === 'message.part.updated'\n ? normalized\n : { type: record.type, data: asRecord(record.data) }\n\n if (event.type === 'message.part.updated') {\n const part = asRecord(event.data?.part)\n if (!part) continue\n const rawDelta = event.data?.delta\n const partType = String(part.type ?? '')\n\n if (partType === 'text' || partType === 'reasoning') {\n const key = getPartKey(part)\n const delta = textDelta(tracker, key, part, rawDelta)\n recordPersistedPart(part, delta || undefined)\n if (delta) {\n if (partType === 'reasoning') reasoningClock.reasoning(key)\n else reasoningClock.settle()\n if (partType === 'text') fullText += delta\n yield { type: partType, text: delta }\n }\n continue\n }\n\n if (partType === 'tool') {\n reasoningClock.settle()\n recordPersistedPart(part, undefined)\n const persisted = partMap.get(getPartKey(part))\n const state = asRecord(persisted?.state)\n const toolId = String(persisted?.id ?? '')\n const toolName = String(persisted?.tool ?? 'tool')\n const args = asRecord(state?.input) ?? {}\n const hasPopulatedArgs = Object.keys(args).length > 0\n const announcedWithPopulatedArgs = announcedToolArgs.get(toolId)\n if (\n toolId &&\n (announcedWithPopulatedArgs === undefined ||\n (!announcedWithPopulatedArgs && hasPopulatedArgs && !settledTools.has(toolId)))\n ) {\n announcedToolArgs.set(toolId, hasPopulatedArgs)\n yield {\n type: 'tool_call',\n call: { toolCallId: toolId, toolName, args },\n }\n }\n const status = String(state?.status ?? '')\n if (toolId && (status === 'completed' || status === 'error') && !settledTools.has(toolId)) {\n settledTools.add(toolId)\n yield {\n type: 'tool_result',\n toolCallId: toolId,\n toolName,\n outcome: {\n ok: status === 'completed',\n ...(state?.output !== undefined ? { result: state.output } : {}),\n ...(asString(state?.error) ? { message: asString(state?.error) } : {}),\n },\n }\n }\n continue\n }\n\n if (partType === 'step-finish') {\n addStepFinishUsage(part, usage)\n // Persist the per-step receipt too (unique key per occurrence: the\n // parts have no id and two receipts must never merge into one).\n recordPersistedPart(part, undefined, `step-finish:#${stepCounter++}`)\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n if (promptTokens || completionTokens) {\n yield {\n type: 'usage',\n usage: {\n promptTokens,\n completionTokens,\n ...(usage.reasoningTokens !== undefined\n ? { reasoningTokens: usage.reasoningTokens }\n : {}),\n ...(usage.costUsd !== undefined ? { providerCostUsd: usage.costUsd } : {}),\n },\n }\n }\n continue\n }\n\n if (partType === 'step-start') {\n recordPersistedPart(part, undefined, `step-start:#${stepCounter}`)\n continue\n }\n\n if (partType === 'file' && options.promoteFilePart) {\n const promote = options.promoteFilePart\n // Prefixed by source (`id:`/`url:`) rather than the bare raw string —\n // an `id` and a `url` that happen to share the same text (e.g. both\n // `\"abc\"`) would otherwise collide onto one memo entry and fold two\n // unrelated parts' promotions together. Internal Map key only; never\n // observable outside this module.\n const rawId = asString(part.id)\n const rawUrl = asString(part.url)\n const memoKey = rawId ? `id:${rawId}` : rawUrl ? `url:${rawUrl}` : undefined\n // Always invoke the callback for a `file` part when one is wired —\n // gtm never skips promotion outright, even for a part with neither\n // `id` nor `url` (it simply fails \"carries no url\" and resolves\n // through the ordinary failure path). Memoization by PROMISE only\n // applies when there is something to key it on; keyless parts are\n // invoked un-memoized, once per occurrence.\n //\n // `Promise.resolve().then(...)` (rather than calling `promote(part)`\n // directly) so a SYNC throw from a non-async `promoteFilePart` is\n // captured into the promise chain instead of escaping this function\n // call outright — `.catch` below only ever sees a rejection, never a\n // thrown exception that unwinds past `attempt()`.\n const attempt = (): Promise<FilePartPromotionOutcome> =>\n Promise.resolve()\n .then(() => promote(part))\n .catch((err) => {\n const reason = err instanceof Error ? err.message : String(err)\n log('[chat-routes] file part promotion threw', { key: memoKey ?? '(keyless)', error: reason })\n return { succeeded: false as const, reason }\n })\n\n let pending: Promise<FilePartPromotionOutcome>\n if (memoKey) {\n pending = promotedFileParts.get(memoKey) ?? attempt()\n promotedFileParts.set(memoKey, pending)\n } else {\n pending = attempt()\n }\n\n const outcome = await pending\n if (outcome.succeeded) {\n recordPersistedPart(outcome.part, undefined, outcome.key)\n } else if (outcome.part) {\n // Substitute part (e.g. a warning notice) takes the raw part's\n // place in the transcript — the transient url never lands.\n recordPersistedPart(outcome.part, undefined, outcome.key)\n } else {\n recordPersistedPart(part, undefined)\n }\n continue\n }\n\n // Remaining storable kinds (file/image/subtask) have no live\n // vocabulary line; they persist so the transcript keeps them.\n recordPersistedPart(part, undefined)\n continue\n }\n\n if (event.type === 'interaction') {\n const parsed = parseInteractionRequest(asRecord(record.data))\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed interaction event', { error: parsed.error })\n continue\n }\n if (renderable(parsed.value.kind, parsed.value)) {\n recordPersistedPart(\n interactionToPersistedPart(parsed.value, 'pending'),\n undefined,\n interactionPartKey(parsed.value.id),\n )\n yield toProducerWireEvent(record)\n continue\n }\n // Non-renderable ask: the run is blocked in the broker until someone\n // answers. Decline it so the turn proceeds instead of hanging.\n let declineFailed = false\n if (options.declineInteraction) {\n try {\n await options.declineInteraction(parsed.value.id)\n } catch (err) {\n declineFailed = true\n log('[chat-routes] failed to auto-decline interaction', {\n id: parsed.value.id,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n } else {\n declineFailed = true\n log('[chat-routes] non-renderable interaction with no declineInteraction wired', {\n id: parsed.value.id,\n kind: parsed.value.kind,\n })\n }\n const text = options.declinedNotice?.(parsed.value, declineFailed) ?? (declineFailed\n ? `The agent requested ${parsed.value.kind} approval; declining it failed — it will expire on its own.`\n : `The agent requested ${parsed.value.kind} approval — auto-declined by policy.`)\n const notice = noticePart('auto-declined', `auto-declined-${parsed.value.id}`, text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'auto-declined', text }\n continue\n }\n\n if (event.type === 'interaction.cancel') {\n const parsed = parseInteractionCancel(asRecord(record.data))\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed interaction.cancel event', { error: parsed.error })\n continue\n }\n const key = interactionPartKey(parsed.value.id)\n const existing = partMap.get(key)\n if (existing?.type === 'interaction' && existing.status === 'pending') {\n recordPersistedPart({\n ...existing,\n status: cancelStatusFor(parsed.value.reason),\n ...(parsed.value.reason ? { cancelReason: parsed.value.reason } : {}),\n }, undefined, key)\n }\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'plan.submitted') {\n const parsed = parsePlanSubmittedEvent(record)\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed plan.submitted event', { error: parsed.error })\n continue\n }\n recordPersistedPart(planToPersistedPart(parsed.value), undefined)\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'warning') {\n const message = asString(event.data?.message)\n if (message) {\n warningCount += 1\n const code = asString(event.data?.code)\n const text = code ? `${code}: ${message}` : message\n const notice = noticePart('warning', `warning-${warningCount}`, text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'warning', text }\n }\n // The flattened notice is additive; existing raw-warning consumers keep\n // receiving the original event too.\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'usage') {\n const reported = extractUsageSnapshot(asRecord(record.usage))\n if (reported) applyTerminalUsage(reported, usage)\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'result') {\n terminalObserved = true\n const finalText = asString(event.data?.finalText)\n if (finalText) fullText = finalText\n const reported = extractReportedTurnUsage(asRecord(event.data))\n if (reported) {\n applyTerminalUsage(reported, usage)\n yield* emitFinalUsage()\n } else {\n // Legacy result shape kept as a secondary fallback.\n const resultUsage = asRecord(event.data?.usage)\n if (resultUsage) {\n const input = Number(resultUsage.inputTokens)\n const output = Number(resultUsage.outputTokens)\n if (Number.isFinite(input)) usage.inputTokens = input\n if (Number.isFinite(output)) usage.outputTokens = output\n }\n }\n yield* emitTerminalizedTools()\n continue\n }\n\n if (event.type === 'done') {\n terminalObserved = true\n const reported = extractReportedTurnUsage(asRecord(event.data))\n if (reported) {\n applyTerminalUsage(reported, usage)\n yield* emitFinalUsage()\n }\n yield* emitTerminalizedTools()\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'error') {\n const message = sandboxStreamErrorMessage(event.data)\n const errorContent = fullText.trim()\n ? `The sandbox model stream stopped before a clean completion.\\n\\nError: ${message}`\n : `The sandbox agent returned an error before producing a visible answer.\\n\\nError: ${message}`\n if (options.errorNotice) {\n yield* emitErrorNotice(errorContent, sandboxStreamErrorCode(event.data))\n } else if (options.includeErrorText !== false) {\n const errorDelta = fullText ? `\\n\\n---\\n${errorContent}` : errorContent\n fullText += errorDelta\n yield { type: 'text', text: errorDelta }\n }\n interactionOutcome = 'expired'\n yield* emitTerminalizedTools()\n yield toProducerWireEvent(record)\n continue\n }\n\n // Remaining lifecycle events forward verbatim; the client parser ignores\n // unknown types.\n yield toProducerWireEvent(record)\n }\n // A model that fell over and then produced nothing never entered the loop\n // body; the downgrade must still be recorded.\n yield* drainModelNotices()\n } catch (streamErr) {\n const diagnostic = sandboxStreamFailureDiagnostic(streamErr)\n log('[chat-routes] sandbox stream failed', {\n failureNote: diagnostic.failureNote,\n error: streamErr instanceof Error ? streamErr.message : String(streamErr),\n })\n if (options.errorNotice) {\n yield* emitErrorNotice(diagnostic.userMessage, diagnostic.code)\n } else if (options.includeErrorText !== false) {\n const errorDelta = fullText\n ? `\\n\\n---\\n${diagnostic.userMessage}`\n : diagnostic.userMessage\n fullText += errorDelta\n yield { type: 'text', text: errorDelta }\n }\n interactionOutcome = 'expired'\n yield* emitTerminalizedTools()\n yield {\n type: 'error',\n data: {\n message: diagnostic.userMessage,\n code: diagnostic.code,\n details: { failureNote: diagnostic.failureNote },\n },\n }\n } finally {\n reasoningClock.settle()\n }\n }\n\n /** The persisted parts with each reasoning part's time: the harness's own, else the clock's. */\n const timedParts = (): Map<string, JsonRecord> => {\n const timed = new Map(partMap)\n for (const [key, part] of partMap) {\n if (part.type !== 'reasoning') continue\n const time = reasoningClock.time(key, asRecord(part.time) as ReasoningRunTime | undefined)\n if (time) timed.set(key, { ...part, time })\n }\n return timed\n }\n\n return {\n stream: stream(),\n finalText: () => fullText,\n terminalObserved: () => terminalObserved,\n assistantParts: () => finalizePendingInteractionParts(\n finalizeAssistantParts(partOrder, timedParts(), fullText),\n interactionOutcome,\n ),\n // Mid-stream snapshot for incremental persistence: the same accumulators,\n // WITHOUT the two completion-time settlements (`terminalizeDanglingTool*`\n // and `finalizePendingInteractionParts`). A running tool and an unanswered\n // ask are the correct live state; settling them early would persist\n // phantom failures the final write then reverses.\n draftParts: () => draftAssistantParts(partOrder, timedParts(), fullText),\n usage: () => usage,\n // A GETTER, not a captured value: `turn-routes` reads `producer.model` at\n // draft-snapshot and persist time, both of which happen after the stream\n // resolved which model serves. A plain property would freeze the PREFERRED\n // model into the row and make a downgrade unattributable — the exact\n // failure mode this work exists to prevent.\n get model(): string | undefined {\n return servedModel()\n },\n modelFailover: () => ({\n model: servingModel(),\n attempts: failover?.attempts() ?? [],\n usedFallback: failover?.usedFallback() ?? false,\n }),\n modelAttribution: () => ({\n ...(options.model ? { requestedModel: options.model } : {}),\n ...(effectiveBackend ?? {}),\n echoReceived: effectiveBackend !== undefined,\n }),\n }\n}\n","/**\n * The chat-route producer for a Router turn: `runRouterTurn` behind the same\n * `produce` seam the sandbox lane uses (`createSandboxChatProducer`). The route\n * persists, buffers, replays and settles the turn exactly as it does for the\n * sandbox; only the producer differs.\n *\n * The engine is pushed, the route pulls: engine events land in a queue that the\n * `stream` generator drains in order, so a slow consumer never drops a frame\n * and the engine never waits on the consumer.\n */\nimport { runRouterTurn, type RouterTurnOptions, type RouterTurnOutcome } from '../runtime/router-turn'\nimport { createReasoningClock } from './reasoning-clock'\nimport type { ChatTurnRouteProducer, ChatTurnUsage } from './turn-routes'\nimport type { ProducerWireEvent } from './wire'\n\nexport type RouterTurnProducerOptions = Omit<RouterTurnOptions, 'onEvent'> & {\n /** Observe the finished turn (placement records, metrics, billing). */\n onOutcome?: (outcome: RouterTurnOutcome) => void\n}\n\nexport function createRouterTurnProducer(options: RouterTurnProducerOptions): ChatTurnRouteProducer {\n const queue: ProducerWireEvent[] = []\n let wake: (() => void) | null = null\n let done = false\n let outcome: RouterTurnOutcome | null = null\n const parts: Array<Record<string, unknown>> = []\n const toolParts = new Map<string, Record<string, unknown>>()\n let textPart: { type: 'text'; text: string } | null = null\n let textSegment = 0\n let reasoningPart: { type: 'reasoning'; text: string } | null = null\n // Each reasoning part's start and end, the same timing the sandbox producer persists.\n const reasoningClock = createReasoningClock<Record<string, unknown>>()\n\n const push = (event: ProducerWireEvent) => {\n queue.push(event)\n wake?.()\n }\n\n const run = runRouterTurn({\n ...options,\n onEvent: event => {\n if (event.type === 'text') {\n if (event.segment !== textSegment || !textPart) {\n textSegment = event.segment\n textPart = { type: 'text', text: '' }\n parts.push(textPart)\n }\n textPart.text += event.delta\n reasoningPart = null\n reasoningClock.settle()\n push({ type: 'text', text: event.delta })\n } else if (event.type === 'reasoning') {\n // A reasoning run is one part until a word or a tool interrupts it.\n if (!reasoningPart) {\n reasoningPart = { type: 'reasoning', text: '' }\n parts.push(reasoningPart)\n }\n reasoningPart.text += event.delta\n reasoningClock.reasoning(reasoningPart)\n push({ type: 'reasoning', text: event.delta })\n } else if (event.type === 'tool') {\n reasoningPart = null\n // The model stopped reasoning when it called the tool.\n reasoningClock.settle(event.startedAt)\n if (event.status === 'running') {\n const part = { type: 'tool', id: event.callId, callID: event.callId, tool: event.toolName,\n state: { status: 'running', input: event.input, time: { start: event.startedAt } } }\n toolParts.set(event.callId, part)\n parts.push(part)\n push({ type: 'tool_call', call: { toolCallId: event.callId, toolName: event.toolName, args: event.input } })\n } else {\n const part = toolParts.get(event.callId)\n const state = event.status === 'completed'\n ? { status: 'completed', output: event.output, time: { start: event.startedAt, end: event.endedAt } }\n : { status: 'error', error: event.error.message, time: { start: event.startedAt, end: event.endedAt } }\n if (part) part.state = { ...(part.state as Record<string, unknown>), ...state }\n push({ type: 'tool_result', toolCallId: event.callId, toolName: event.toolName,\n outcome: event.status === 'completed' ? { ok: true, result: event.output } : { ok: false, message: event.error.message, code: event.error.code } })\n }\n } else if (event.type === 'usage') {\n push({ type: 'usage', usage: { promptTokens: event.promptTokens, completionTokens: event.completionTokens } })\n } else if (event.type === 'tool_event') {\n // Recorded on the tool's part, so a reload shows what the tool did, and streamed as it happens.\n const part = toolParts.get(event.callId)\n if (part) {\n const state = part.state as Record<string, unknown>\n part.state = { ...state, events: [...(state.events as unknown[] ?? []), { kind: event.kind, data: event.data, at: event.at }] }\n }\n push({ type: 'tool.event', data: { toolCallId: event.callId, toolName: event.toolName, kind: event.kind, data: event.data, at: event.at } })\n }\n },\n }).then(result => {\n outcome = result\n options.onOutcome?.(result)\n if (result.failure) {\n push({ type: 'error', data: { message: result.failure.message, code: result.failure.code } })\n }\n }).finally(() => {\n reasoningClock.settle()\n done = true\n wake?.()\n })\n\n async function* stream(): AsyncGenerator<ProducerWireEvent, void, unknown> {\n for (;;) {\n while (queue.length) yield queue.shift()!\n if (done) break\n await new Promise<void>(resolve => { wake = resolve })\n wake = null\n }\n await run\n }\n\n // A reasoning part carries its run's time; a run still open in a mid-turn draft has a start and no end.\n const snapshot = () => parts.map(part => part.type === 'reasoning'\n ? { ...part, time: reasoningClock.time(part) }\n : { ...part })\n\n const usage = (): ChatTurnUsage => ({\n inputTokens: outcome?.usage.promptTokens ?? 0,\n outputTokens: outcome?.usage.completionTokens ?? 0,\n })\n\n return {\n stream: stream() as ChatTurnRouteProducer['stream'],\n finalText: () => outcome?.text ?? '',\n assistantParts: snapshot,\n draftParts: snapshot,\n usage,\n get model() { return outcome?.servedModel ?? options.model },\n modelAttribution: () => ({\n requestedModel: options.model,\n ...(outcome?.servedModel ? { servedModel: outcome.servedModel } : {}),\n echoReceived: Boolean(outcome?.servedModel),\n }),\n }\n}\n","import { profileMaterializationAxes, type AgentProfile } from '@tangle-network/agent-interface'\nimport { runProtectedRouterTurn, type RouterTurnGrant } from '../runtime/protected-router-turn'\nimport type { RouterTurnOptions } from '../runtime/router-turn'\nimport type { McpToolDefinition } from '../tools/mcp-rpc'\nimport { createSandboxChatProducer } from './sandbox-producer'\nimport type { ChatRouteEvent, ChatTurnRouteProducer, ChatTurnUsage } from './turn-routes'\n\nexport interface ProtectedRuntimeChatOptions {\n profile: AgentProfile\n prompt: string\n priorMessages?: ReadonlyArray<{ role: string; content: string }>\n /** Runtime owns reservation, activation, revocation and authoritative settlement. */\n grant: RouterTurnGrant\n /** Only tools authorized for this execution. Each tool validates its arguments and effects. */\n tools: readonly McpToolDefinition<{ signal: AbortSignal }>[]\n maxToolCalls: number\n signal?: AbortSignal\n}\n\n/**\n * The AgentProfile leaves (agent-interface's materialization axes) a protected turn\n * carries. Identity, `harness` (cli-base only) and `metadata` are control fields;\n * `modelMetadata` is accepted only when empty. A leaf outside this set, including one\n * agent-interface adds later, is refused by name before a grant is reserved: running\n * the turn without it would silently drop what the profile asked for.\n */\nconst PROTECTED_TURN_AXES: ReadonlySet<string> = new Set([\n 'name', 'description', 'version', 'tags', 'metadata', 'harness', 'tools',\n 'systemPrompt', 'instructions',\n 'modelDefault', 'modelProvider', 'modelReasoningEffort', 'modelMaxVisibleOutputTokens',\n 'modelMaxTotalOutputTokens', 'modelMetadata',\n])\n\n/**\n * The parts of a profile a protected turn applies: its prompt text, its model and\n * reasoning effort, and its completion ceilings. Anything else would change the\n * execution without the grant pricing it, so it is refused by name.\n */\nfunction protectedTurnSettings(profile: AgentProfile, toolNames: ReadonlySet<string>): Pick<RouterTurnOptions,\n 'model' | 'systemPrompt' | 'reasoningEffort' | 'maxTokens' | 'maxCompletionTokens'> {\n if (profile.harness && profile.harness !== 'cli-base') throw new Error(`Protected chat runs only the cli-base harness, not ${profile.harness}`)\n if (profile.resources) throw new Error('Protected chat applies prompt text only; run profile resources in the Sandbox lane')\n if (profile.model?.metadata && Object.keys(profile.model.metadata).length > 0) {\n throw new Error('Protected chat does not apply AgentProfile.model.metadata')\n }\n if (profile.model?.maxReasoningTokens !== undefined) throw new Error('Protected chat cannot enforce maxReasoningTokens')\n const unsupported = profileMaterializationAxes(profile).filter(axis => !PROTECTED_TURN_AXES.has(axis))\n if (unsupported.length > 0) {\n throw new Error(`Protected chat does not apply AgentProfile ${unsupported.join(', ')}; run this profile in the Sandbox lane`)\n }\n const declared = profile.model?.default?.trim()\n const provider = profile.model?.provider?.trim()\n const model = declared && provider && declared.startsWith(`${provider}/`) ? declared.slice(provider.length + 1) : declared\n if (!model) throw new Error('Protected chat requires AgentProfile.model.default')\n for (const [name, enabled] of Object.entries(profile.tools ?? {})) {\n if (enabled !== toolNames.has(name)) throw new Error(`Protected tool ${name} does not match AgentProfile.tools`)\n }\n for (const name of toolNames) {\n if (profile.tools?.[name] !== true) throw new Error(`Protected tool ${name} is not enabled by AgentProfile.tools`)\n }\n const { visible, total } = { visible: profile.model?.maxVisibleOutputTokens, total: profile.model?.maxTotalOutputTokens }\n return {\n model,\n systemPrompt: [profile.prompt?.systemPrompt, ...(profile.prompt?.instructions ?? [])]\n .filter((part): part is string => typeof part === 'string' && part.trim().length > 0).join('\\n'),\n ...(profile.model?.reasoningEffort ? { reasoningEffort: profile.model.reasoningEffort } : {}),\n ...(visible !== undefined ? { maxTokens: visible } : {}),\n ...(total !== undefined ? { maxCompletionTokens: total } : {}),\n }\n}\n\n/** The text a model reads for one tool result: the JSON of its value, or the tool's own error. */\nfunction protectedToolResultText(outcome: { ok: true; result: unknown } | { ok: false; message: string }): string {\n if (!outcome.ok) return `error: ${outcome.message}`\n return JSON.stringify(outcome.result) ?? 'null'\n}\n\n/**\n * Run a profile as one Router turn under a protected model grant\n * (`runProtectedRouterTurn`), projected through the shared chat projection. The\n * usage is published only from the settled grant.\n */\nexport function createProtectedRuntimeChatProducer(options: ProtectedRuntimeChatOptions): ChatTurnRouteProducer {\n if (!Number.isSafeInteger(options.maxToolCalls) || options.maxToolCalls < 0) {\n throw new Error('maxToolCalls must be a non-negative integer')\n }\n options = { ...options,\n grant: { ...options.grant, resolve: structuredClone(options.grant.resolve), reserve: structuredClone(options.grant.reserve) },\n tools: options.tools.map(tool => ({ ...tool, inputSchema: structuredClone(tool.inputSchema) })),\n // Chat history rows carry storage fields (id, parts, timestamps). The\n // provider receives exactly role and content; Anthropic rejects extra keys.\n priorMessages: options.priorMessages?.map(({ role, content }) => ({ role, content })),\n }\n const tools = new Map(options.tools.map(tool => [tool.name, tool]))\n if (tools.size !== options.tools.length) throw new Error('Duplicate protected tool name')\n const profile = structuredClone(options.profile)\n let toolCalls = 0\n let usage: ChatTurnUsage = {}\n let started = false\n let model: string | undefined\n let budgetEnforced = false\n let toolTokens: number | undefined\n\n async function* events(): AsyncGenerator<unknown> {\n if (started) throw new Error('A protected chat producer can only execute once')\n started = true\n options.signal?.throwIfAborted()\n if (Date.now() >= options.grant.deadlineAtMs) throw new Error('Protected execution deadline expired')\n const settings = protectedTurnSettings(profile, new Set(tools.keys()))\n const controller = new AbortController()\n const signal = AbortSignal.any([\n controller.signal, AbortSignal.timeout(Math.max(1, options.grant.deadlineAtMs - Date.now())),\n ...(options.signal ? [options.signal] : []),\n ])\n const channel = new TransformStream<unknown, unknown>()\n const writer = channel.writable.getWriter()\n const reader = channel.readable.getReader()\n const emit = (event: unknown) => writer.write(event)\n // Each model call's text, by segment. The answer is the last segment the model wrote.\n const segments: string[] = []\n const execution = runProtectedRouterTurn({\n ...settings,\n grant: options.grant,\n userMessage: options.prompt,\n history: [...(options.priorMessages ?? [])],\n signal,\n // The grant's ledger expects one buffered completion per model call.\n stream: false,\n maxToolCalls: options.maxToolCalls,\n // The grant bounds model calls; the loop must not stop the turn earlier.\n maxToolTurns: options.grant.reserve.limits.maxModelCalls,\n tools: options.tools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema,\n run: async (args: Record<string, unknown>) => {\n signal.throwIfAborted()\n toolCalls += 1\n const id = `tool-${toolCalls}`\n await emit({ type: 'tool_call', data: { id, name: tool.name, arguments: args } })\n try {\n const result = await tool.run(args, { signal })\n await emit({ type: 'tool_result', data: { id, name: tool.name, output: result } })\n return result\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Tool failed'\n await emit({ type: 'tool_result', data: { id, name: tool.name, error: message } })\n throw error\n }\n } })),\n toolError: error => ({ code: 'tool_failed', message: error instanceof Error ? error.message : 'Tool failed' }),\n renderToolResult: (_toolName, outcome) => protectedToolResultText(outcome),\n onEvent: event => {\n if (event.type === 'text') segments[event.segment - 1] = (segments[event.segment - 1] ?? '') + event.delta\n },\n }).then(async outcome => {\n if (outcome.settlement) {\n usage = { ...outcome.settlement.usage }\n budgetEnforced = outcome.settlement.budgetEnforced\n model = outcome.settlement.model\n // Tool-result text is billed as input under a grant.\n toolTokens = 0\n }\n // A settlement check fails after the turn answered; the answer stands beside the failure.\n if (!outcome.failure || outcome.failure.code.startsWith('settlement_')) {\n const finalText = segments.filter(Boolean).at(-1) ?? ''\n if (finalText) await emit({ type: 'message.part.updated', data: { part: { id: 'text_delta', type: 'text' }, delta: finalText } })\n await emit({ type: 'result', data: { finalText } })\n }\n if (outcome.failure) throw new Error(outcome.failure.message)\n await writer.close()\n }).catch(error => writer.abort(error))\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) break\n yield next.value\n }\n await execution\n } finally {\n controller.abort()\n await reader.cancel().catch(() => undefined)\n await execution\n reader.releaseLock()\n writer.releaseLock()\n }\n }\n\n const producer = createSandboxChatProducer({ events: events(), model: profile.model?.default })\n return {\n ...producer,\n stream: (async function* () {\n let failure: ChatRouteEvent | undefined\n for await (const event of producer.stream) {\n if (event.type === 'error') failure = event\n else yield event\n }\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined && usage.costUsd !== undefined) {\n yield { type: 'usage' as const, usage: { promptTokens: usage.inputTokens,\n completionTokens: usage.outputTokens, providerCostUsd: usage.costUsd,\n reasoningTokens: usage.reasoningTokens, toolTokens,\n toolCallCount: toolCalls, budgetEnforced } }\n }\n if (failure) yield failure\n })(),\n get model() { return model ?? profile.model?.default },\n usage: () => ({ ...usage }),\n }\n}\n","/**\n * Detached (autonomous) turn → live buffer bridge.\n *\n * The interactive lane (`createChatTurnRoutes`) already streams a user-typed\n * turn to the browser while it runs. An AUTONOMOUS turn — a mission step, a\n * queue job, an inbound-email review — runs detached (`dispatchPrompt`/\n * `streamPrompt` server-side so it survives no one watching) and, historically,\n * only persisted its FINAL message. A browser opening the session mid-run saw a\n * dead screen: the live tokens existed server-side but were never written to\n * the turn-event buffer the client re-attach path (`listRunning` + `/replay`)\n * reads.\n *\n * `runDetachedTurn` is that missing bridge, packaged. It taps the same buffer\n * the interactive lane uses (`createBufferedTurnTap`) with the same producer\n * mapping (`createSandboxChatProducer`), so an autonomous run is watchable\n * token-by-token exactly like an interactive one — while staying durable\n * (a durable driver re-invokes it after a crash; a turn that finished\n * server-side short-circuits instead of re-streaming). Products supply only the\n * domain seams: the raw sandbox event stream, the turn store, and the ids.\n *\n * This is app-shell mechanism (turn durability + live projection), not engine:\n * it owns no loop logic and imports no SDK — the event source is an injected\n * `AsyncIterable`.\n */\n\nimport { toChatMessageParts } from '../chat-store/parts'\nimport type { ModelFailoverAttempt } from '../model-resolution/failover'\nimport {\n coalesceDeltas,\n createBufferedTurnTap,\n type TurnEventStore,\n} from '../stream/index'\nimport {\n assistantRowIdForTurn,\n createAssistantDraftWriter,\n storeSupportsDraftPersistence,\n type AssistantDraftStore,\n type AssistantDraftWriter,\n type AssistantRowValues,\n type DraftStoredMessage,\n type DraftPersistenceTuning,\n} from './draft-persistence'\nimport {\n createSandboxChatProducer,\n type SandboxChatProducerOptions,\n} from './sandbox-producer'\nimport type { ChatTurnUsage } from './turn-routes'\n\n/** The normalized structured message body (tool-call / file / plan / interaction\n * parts) that `/chat-store` persists as the durable assistant row — the same\n * shape `createSandboxChatProducer().assistantParts()` returns. */\nexport type DetachedTurnParts = Array<Record<string, unknown>>\n\n/** Authoritative final receipt for a turn that finished server-side, or whose\n * live stream carried no usage (some harness paths only expose tokens via the\n * completed-turn record, e.g. `box.findCompletedTurn(turnId)`). */\nexport interface DetachedTurnFinal {\n text?: string\n usage?: ChatTurnUsage\n /** The structured parts to persist when this receipt is more complete than\n * the live stream (cached, finished server-side, or a fast stream that\n * delivered scalar text before its message-part events). Omitted when the\n * record only carries a usage receipt. */\n parts?: DetachedTurnParts\n}\n\n/** Define options for managing and projecting a detached turn event stream in a session */\nexport interface DetachedTurnOptions {\n store: TurnEventStore\n turnId: string\n /** Thread/session id — recorded as the buffer scope so a browser opening the\n * session mid-run rediscovers this turn via `listRunning(scopeId)` after it\n * has lost the turnId. */\n scopeId: string\n /** The raw sandbox event stream for this turn (e.g. `streamSandboxPrompt`).\n * Ownership of the box, prompt, tooling, and attachments stays with the\n * caller — this only projects the stream.\n *\n * An already-open stream is bound to one model and cannot fail over. Prefer\n * {@link openEvents}; exactly one of the two is required. */\n events?: AsyncIterable<unknown>\n /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form\n * of {@link events}. Wiring it turns failover on with no further flag\n * whenever {@link fallbackModels} is non-empty. Requires {@link model}.\n *\n * An autonomous run is the case that most needs this: nobody is watching to\n * notice a dead upstream and retry by hand, so without failover the mission\n * step or queue job simply fails. Forwarded to the producer. */\n openEvents?: SandboxChatProducerOptions['openEvents']\n /** Models to try, in order, when `model`'s upstream is dead. Product config —\n * see the producer's note on why a same-family fallback is not automatically\n * safe and why every fallback is surfaced. */\n fallbackModels?: SandboxChatProducerOptions['fallbackModels']\n /** Opt out of failover while still using {@link openEvents}. */\n modelFailover?: false\n /** Maximum time to open/start one model event source through its first event.\n * Forwarded to the producer; default 120 seconds. */\n openTimeoutMs?: SandboxChatProducerOptions['openTimeoutMs']\n /** Hard deadline after the source's first lifecycle event for the first\n * answer-bearing event. Forwarded to the producer; default 60 seconds. */\n firstResponseTimeoutMs?: SandboxChatProducerOptions['firstResponseTimeoutMs']\n /** Fired when a model is abandoned mid-chain (telemetry/alerting). */\n onModelFallback?: SandboxChatProducerOptions['onModelFallback']\n /** The PREFERRED model. Recorded on the persisted assistant message + usage\n * receipt — unless failover moved the turn, in which case the model that\n * actually served is recorded instead and surfaced on the result. */\n model?: string\n /** Per-flush buffer coalescer. Default `coalesceDeltas`. */\n coalesce?: (events: unknown[]) => unknown[]\n /** Which ask kinds the product renders a card for; anything else is\n * auto-declined via {@link declineInteraction}. Forwarded to the producer. */\n isRenderableInteraction?: SandboxChatProducerOptions['isRenderableInteraction']\n /** Resolve a non-renderable ask so the run never hangs in the broker. An\n * autonomous turn has NO human watching to answer an ask, so a caller that\n * omits this risks the run blocking until the broker times out — wire it for\n * any unattended run. Forwarded to the producer. */\n declineInteraction?: SandboxChatProducerOptions['declineInteraction']\n /** Opt-in eager promotion of harness-emitted `file` parts. Forwarded to the\n * producer (see its docs). */\n promoteFilePart?: SandboxChatProducerOptions['promoteFilePart']\n /** Authoritative final receipt, consulted whenever a re-invoke finds a prior\n * buffer: (a) an already-`complete` turn returns it as the cached result,\n * (b) a `running` turn (crash mid-run) uses it to detect a run that finished\n * server-side, and (c) a clean run whose stream carried no usage or only\n * scalar text falls back to it. For Sandbox runs, use\n * `readCompletedSandboxTurn` so the exact completed session message\n * restores tool/file parts as well as text. */\n completedResult?: () => Promise<DetachedTurnFinal | null | undefined>\n /** Own the durable assistant row for this turn instead of returning the body\n * for the caller to insert — and keep it in step with the stream.\n *\n * An autonomous run is exactly the case a late viewer hits: nobody is\n * watching when it starts, so by the time a browser opens the session the\n * streaming gateway's hot event buffer may already have expired it. Keeping\n * that buffer short is what makes it affordable at scale (its Redis\n * footprint is linear in `ttl x concurrent sessions`), so the durable row —\n * written incrementally here — is what serves the late viewer.\n *\n * WIRING THIS TRANSFERS ROW OWNERSHIP: the returned {@link\n * DetachedTurnResult.messageId} names the row this call wrote (draft rows\n * during the stream, authoritative values at the end, retraction when the\n * turn produced nothing). The caller must NOT insert its own assistant row\n * for the turn. Omit the seam and nothing changes — the result is returned\n * and the caller persists it exactly as today.\n *\n * Idempotency reuses the turn's own identity: the row id defaults to\n * `assistant:<turnId>`, so a durable driver re-invoking after a crash\n * patches the same row instead of duplicating parts. */\n persist?: DraftPersistenceTuning & {\n store: AssistantDraftStore\n threadId: string\n /** Deterministic row id. Default `assistant:<turnId>`. */\n messageId?: string\n /** Pre-persist text transform (`/redact`), applied to drafts AND the final\n * write — parity with the interactive lane's `transformFinalText`. */\n transformText?: (text: string) => string | Promise<string>\n }\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** Describe the result of a detached turn including state, text, parts, usage, and optional error or cache flag */\nexport interface DetachedTurnResult {\n /** `completed` — clean drain: persist + bill. `failed` — a terminal error\n * event, including the producer's structured `sandbox.stream_failed` event\n * when the raw sandbox stream throws: skip billing, render an error row. */\n state: 'completed' | 'failed'\n text: string\n /** The structured assistant body to persist (tool calls, file/plan/interaction\n * parts). Empty array when the run produced none. */\n parts: DetachedTurnParts\n usage: ChatTurnUsage\n /** Present when `state === 'failed'`. */\n error?: string\n /** True when a prior buffer meant this call returned a cached/finished result\n * WITHOUT re-streaming (durable-driver retry after a crash). */\n cached: boolean\n /** The durable assistant row this call wrote, when `persist` was wired.\n * `null` when the turn produced nothing and the row was retracted. Absent\n * when the caller owns persistence (today's behavior). */\n messageId?: string | null\n /** The model that SERVED the turn — the fallback's id when failover moved it.\n * A caller that bills or scores per model must read this, not the model it\n * requested. */\n model?: string\n /** The caller's explicit model request, before shell failover. */\n requestedModel?: string\n /** The effective model echoed by the downstream sandbox. */\n servedModel?: string\n /** The effective provider echoed by the downstream sandbox. */\n servedProvider?: string\n /** How the downstream sandbox selected the effective model. */\n servedSource?: 'request' | 'environment' | 'profile'\n /** True when the preferred model did not serve. Makes an autonomous\n * downgrade — which no human watched happen — attributable after the fact. */\n usedModelFallback?: boolean\n /** Every model tried, in order, with the reason each was abandoned. */\n modelAttempts?: ModelFailoverAttempt[]\n}\n\n/** Terminal failure event types a producer may forward verbatim. */\nconst TERMINAL_ERROR_TYPES = new Set(['error', 'session.run.failed'])\n\nfunction errorMessageOf(ev: unknown): string {\n const rec = ev as { data?: { message?: unknown; reason?: unknown }; message?: unknown } | null\n const raw = rec?.data?.message ?? rec?.data?.reason ?? rec?.message\n return typeof raw === 'string' && raw ? raw : 'run failed'\n}\n\nfunction hasUsage(usage: ChatTurnUsage): boolean {\n return typeof usage.inputTokens === 'number' && usage.inputTokens > 0\n}\n\nfunction cachedResultFrom(\n final: DetachedTurnFinal | null,\n persisted?: DraftStoredMessage,\n): DetachedTurnResult {\n if (!final && !persisted) {\n throw new Error('Completed turn has no retained result or assistant row; retry exact recovery')\n }\n // A text-only recovery must not erase usage already stored for this turn.\n // Unknown values stay absent; an explicit measured zero is preserved.\n const usage: ChatTurnUsage = {}\n for (const key of [\n 'inputTokens', 'outputTokens', 'reasoningTokens',\n 'cacheReadTokens', 'cacheWriteTokens', 'costUsd',\n ] as const) {\n const value = final?.usage?.[key] ?? persisted?.[key]\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) usage[key] = value\n }\n return {\n state: 'completed',\n text: final?.text ?? persisted?.content ?? '',\n parts:\n final?.parts ??\n (persisted?.parts as DetachedTurnParts | null | undefined) ??\n [],\n usage,\n cached: true,\n }\n}\n\n/**\n * Stream a detached turn into the live turn-event buffer, durably.\n *\n * - Idempotent: an already-`complete` turn returns the cached result without\n * re-streaming (a second event sequence would collide with the buffered one).\n * - Crash-safe: a `running` turn (a prior attempt crashed mid-tap) consults\n * `completedResult` to detect a run that finished server-side; only a run that\n * genuinely did not complete is re-streamed, and then over a buffer the\n * store's `resetEvents` cleared so seqs don't corrupt. A store without\n * `resetEvents` refuses that re-stream and leaves the existing buffer intact.\n * - Marks the turn `running` under `scopeId` so a mid-run browser finds it.\n * - Settles `complete`/`error` so the client stops tailing and billing/render\n * can branch on `state`.\n */\nexport async function runDetachedTurn(opts: DetachedTurnOptions): Promise<DetachedTurnResult> {\n const { store, turnId, scopeId } = opts\n\n // Hoisted so the draft writer's snapshot can read the producer's live\n // accumulators; assigned once the idempotency branches decide to re-stream.\n let producer: ReturnType<typeof createSandboxChatProducer> | undefined\n\n /** The model that actually served. Read LAZILY (never captured up front): on\n * a cached/finished-server-side path no producer exists and the preferred\n * model is the best available answer, but on a live re-stream failover may\n * have moved the turn after the row was first drafted. */\n const servingModel = (): string | undefined => producer?.model ?? opts.model\n\n const persistedRow = async (): Promise<DraftStoredMessage | undefined> => {\n if (!opts.persist) return undefined\n return (await opts.persist.store.listMessages(opts.persist.threadId)).find(\n (message) =>\n message.id ===\n (opts.persist?.messageId ?? assistantRowIdForTurn(turnId)),\n )\n }\n\n // Durable row ownership (opt-in). Built before the idempotency branches so a\n // cached/finished-server-side re-invoke still converges the row instead of\n // leaving whatever partial state a crashed attempt wrote.\n let draft: AssistantDraftWriter | undefined\n if (opts.persist) {\n const { store: persistStore, threadId, messageId, transformText, ...tuning } = opts.persist\n if (!storeSupportsDraftPersistence(persistStore)) {\n throw new Error(\n 'runDetachedTurn persist requires a store with updateMessage() — `/chat-store`\\'s createChatStore has it',\n )\n }\n draft = createAssistantDraftWriter({\n ...tuning,\n store: persistStore,\n threadId,\n messageId: messageId ?? assistantRowIdForTurn(turnId),\n snapshot: () => (producer\n ? {\n content: producer.finalText?.() ?? '',\n ...(producer.draftParts ? { parts: producer.draftParts() } : {}),\n ...(producer.usage ? { usage: producer.usage() } : {}),\n ...(servingModel() ? { model: servingModel() } : {}),\n }\n : null),\n ...(transformText ? { transformText } : {}),\n ...(opts.log ? { log: opts.log } : {}),\n })\n }\n\n /** Settle the durable row with authoritative values (or retract it when the\n * turn produced nothing), and stamp the id onto the result. */\n const settleRow = async (\n base: DetachedTurnResult,\n cachedPersisted?: DraftStoredMessage,\n ): Promise<DetachedTurnResult> => {\n // A cached/reconciled return has no producer to replay the sidecar echo.\n // Preserve the attribution already written by the prior attempt instead\n // of replacing its served `model` with today's requested-model fallback.\n const persisted =\n cachedPersisted ?? (!producer ? await persistedRow() : undefined)\n const info = producer?.modelFailover?.()\n const attribution = producer?.modelAttribution?.()\n const model = producer?.model ?? persisted?.model ?? opts.model\n const requestedModel = attribution?.requestedModel ?? persisted?.requestedModel ?? opts.model\n const servedModel = attribution?.servedModel ?? persisted?.servedModel\n const servedProvider = attribution?.servedProvider ?? persisted?.servedProvider\n const servedSource = attribution?.servedSource ?? (\n persisted?.servedSource === 'request' ||\n persisted?.servedSource === 'environment' ||\n persisted?.servedSource === 'profile'\n ? persisted.servedSource\n : undefined\n )\n const result: DetachedTurnResult = {\n ...base,\n ...(model ? { model } : {}),\n ...(requestedModel ? { requestedModel } : {}),\n ...(servedModel ? { servedModel } : {}),\n ...(servedProvider ? { servedProvider } : {}),\n ...(servedSource ? { servedSource } : {}),\n ...(info ? { usedModelFallback: info.usedFallback, modelAttempts: info.attempts } : {}),\n }\n if (!draft) return result\n const transform = opts.persist?.transformText\n const content = transform ? await transform(result.text) : result.text\n const rawParts = transform\n ? await Promise.all(\n result.parts.map(async (part) =>\n String((part as { type?: unknown }).type ?? '') === 'text'\n ? { ...part, text: await transform(String((part as { text?: unknown }).text ?? '')) }\n : part,\n ),\n )\n : result.parts\n const parts = toChatMessageParts(rawParts)\n if (!content.trim() && parts.length === 0) {\n await draft.discard()\n return { ...result, messageId: null }\n }\n const values: AssistantRowValues = {\n content,\n ...(parts.length > 0 ? { parts } : {}),\n ...(result.model ? { model: result.model } : {}),\n ...(result.requestedModel ? { requestedModel: result.requestedModel } : {}),\n ...(result.servedModel ? { servedModel: result.servedModel } : {}),\n ...(result.servedProvider ? { servedProvider: result.servedProvider } : {}),\n ...(result.servedSource ? { servedSource: result.servedSource } : {}),\n ...(result.usage.inputTokens !== undefined ? { inputTokens: result.usage.inputTokens } : {}),\n ...(result.usage.outputTokens !== undefined ? { outputTokens: result.usage.outputTokens } : {}),\n ...(result.usage.reasoningTokens !== undefined ? { reasoningTokens: result.usage.reasoningTokens } : {}),\n ...(result.usage.cacheReadTokens !== undefined ? { cacheReadTokens: result.usage.cacheReadTokens } : {}),\n ...(result.usage.cacheWriteTokens !== undefined ? { cacheWriteTokens: result.usage.cacheWriteTokens } : {}),\n ...(result.usage.costUsd !== undefined ? { costUsd: result.usage.costUsd } : {}),\n }\n await draft.finalize(values)\n return { ...result, messageId: draft.rowId() ?? null }\n }\n\n const completed = async (): Promise<DetachedTurnFinal | null> => {\n if (!opts.completedResult) return null\n // An unavailable authoritative read must not become a fresh stream.\n return (await opts.completedResult()) ?? null\n }\n\n const prior = await store.getStatus(turnId)\n\n if (prior === 'complete') {\n const persisted = await persistedRow()\n return await settleRow(\n cachedResultFrom(await completed(), persisted),\n persisted,\n )\n }\n\n if (prior === 'running') {\n // A prior attempt marked the turn running and then this worker crashed. The\n // detached SESSION may have finished server-side while we were gone — the\n // authoritative check is `completedResult` (findCompletedTurn). If it\n // completed, settle the stuck `running` buffer and return it.\n const final = await completed()\n if (final) {\n await store.setStatus(turnId, 'complete', scopeId)\n const persisted = await persistedRow()\n return await settleRow(cachedResultFrom(final, persisted), persisted)\n }\n // Genuine re-run: clear the partial buffer first, or the fresh tap's seq\n // (restarting at 0) interleaves with the orphaned rows.\n if (!store.resetEvents) {\n throw new Error('Detached turn recovery requires a TurnEventStore with resetEvents before replaying an existing stream')\n }\n await store.resetEvents(turnId)\n }\n\n const tap = createBufferedTurnTap({\n store,\n turnId,\n scopeId,\n coalesce: opts.coalesce ?? coalesceDeltas,\n })\n // Leading turn marker: flips the buffer to `running` (so `listRunning` finds\n // it) and is the browser's `/replay` resume handle.\n await tap.onEvent({ type: 'turn', turnId })\n\n producer = createSandboxChatProducer({\n ...(opts.openEvents ? { openEvents: opts.openEvents } : { events: opts.events }),\n model: opts.model,\n ...(opts.fallbackModels ? { fallbackModels: opts.fallbackModels } : {}),\n ...(opts.modelFailover === false ? { modelFailover: false as const } : {}),\n ...(opts.openTimeoutMs !== undefined ? { openTimeoutMs: opts.openTimeoutMs } : {}),\n ...(opts.firstResponseTimeoutMs !== undefined\n ? { firstResponseTimeoutMs: opts.firstResponseTimeoutMs }\n : {}),\n ...(opts.onModelFallback ? { onModelFallback: opts.onModelFallback } : {}),\n isRenderableInteraction: opts.isRenderableInteraction,\n declineInteraction: opts.declineInteraction,\n promoteFilePart: opts.promoteFilePart,\n log: opts.log,\n })\n\n let runError: string | undefined\n try {\n for await (const ev of producer.stream) {\n const type = (ev as { type?: unknown }).type\n if (typeof type === 'string' && TERMINAL_ERROR_TYPES.has(type)) runError = errorMessageOf(ev)\n await tap.onEvent(ev)\n // Same coalesced cadence the interactive lane uses: the durable row\n // tracks the run so a viewer arriving after the hot buffer's short window\n // reads it from storage.\n draft?.notify(ev as { type?: unknown })\n }\n await tap.done(runError ? 'error' : 'complete')\n } catch (err) {\n await tap.done('error').catch(() => {})\n // Settle any in-flight draft before propagating so the partial row a\n // re-invoke will adopt is a complete write, not a half-landed one.\n await draft?.close().catch(() => {})\n throw err\n }\n\n const text = producer.finalText?.() ?? ''\n const parts = producer.assistantParts?.() ?? []\n let usage: ChatTurnUsage = producer.usage?.() ?? {}\n\n // A terminal result can supply scalar text even when the event subscriber\n // missed every structured message part. Reconcile that text-only shape with\n // the durable completion record before final persistence.\n const onlyTextParts = parts.every((part) =>\n String((part as { type?: unknown }).type ?? '') === 'text',\n )\n if (!runError && (!hasUsage(usage) || !text || onlyTextParts)) {\n const final = await completed()\n if (final?.usage) usage = { ...usage, ...final.usage }\n return await settleRow({\n state: 'completed',\n text: final?.text ?? text,\n parts: final?.parts?.length ? final.parts : parts,\n usage,\n cached: false,\n })\n }\n\n if (runError) return await settleRow({ state: 'failed', text, parts, usage, error: runError, cached: false })\n return await settleRow({ state: 'completed', text, parts, usage, cached: false })\n}\n","/**\n * Recover a detached Sandbox turn from its durable completed records.\n *\n * A fast detached run can finish before the live event subscriber receives\n * every message-part event. The Sandbox still retains two exact records: a\n * turn-id keyed result cache and the completed assistant message on the\n * session. This adapter joins them without guessing across turns, then returns\n * the same `DetachedTurnFinal` shape `runDetachedTurn` already consumes.\n */\n\nimport type { SandboxInstance, SessionMessage } from '@tangle-network/sandbox'\nimport {\n asRecord,\n asString,\n collapseRedundantTextParts,\n finalizeAssistantParts,\n getPartKey,\n mergePersistedPart,\n normalizePersistedPart,\n type JsonRecord,\n} from '../stream/index'\nimport { addStepFinishUsage } from './sandbox-turn-usage'\nimport type { DetachedTurnFinal } from './detached-turn'\nimport type { ChatTurnModelAttribution, ChatTurnUsage } from './turn-routes'\n\n/** The official Sandbox methods needed for completed-turn recovery. */\nexport type CompletedSandboxTurnSource = Pick<\n SandboxInstance,\n 'findCompletedTurn' | 'session'\n>\n\n/** Options for resolving one exact detached turn. */\nexport interface ReadCompletedSandboxTurnOptions {\n turnId: string\n sessionId: string\n /** Read only session messages newer than this Unix-ms time. A long session\n * holds more than one page of messages, oldest first, so an unbounded read\n * both grows with the session and can miss the newest turn. */\n since?: number\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined\n if (typeof value !== 'string' || !value.trim()) return undefined\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\nfunction firstNumber(record: JsonRecord | undefined, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = finiteNumber(record?.[key])\n if (value !== undefined) return value\n }\n return undefined\n}\n\n/** @internal Shared terminal-result decoder; callers must establish exact turn identity. */\nexport function usageFromResult(result: JsonRecord | undefined): ChatTurnUsage {\n const raw = asRecord(result?.usage) ?? asRecord(result?.tokenUsage)\n const inputTokens = firstNumber(raw, ['inputTokens', 'promptTokens', 'input'])\n const outputTokens = firstNumber(raw, ['outputTokens', 'completionTokens', 'output'])\n const reasoningTokens = firstNumber(raw, ['reasoningTokens', 'reasoning'])\n const cacheReadTokens = firstNumber(raw, [\n 'cacheReadTokens',\n 'cacheReadInputTokens',\n 'cacheRead',\n ])\n const cacheWriteTokens = firstNumber(raw, [\n 'cacheWriteTokens',\n 'cacheCreationInputTokens',\n 'cacheWrite',\n ])\n const costUsd = firstNumber(result, ['costUsd', 'totalCostUsd', 'cost'])\n ?? firstNumber(raw, ['costUsd', 'cost'])\n\n return {\n ...(inputTokens !== undefined ? { inputTokens } : {}),\n ...(outputTokens !== undefined ? { outputTokens } : {}),\n ...(reasoningTokens !== undefined ? { reasoningTokens } : {}),\n ...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),\n ...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),\n ...(costUsd !== undefined ? { costUsd } : {}),\n }\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\n/** @internal Attribution travels with the same validated result as content and usage. */\nexport function attributionFromResult(result: Record<string, unknown> | undefined): Pick<\n ChatTurnModelAttribution,\n 'servedModel' | 'servedProvider' | 'servedSource'\n> {\n const metadata = asRecord(result?.metadata)\n const modelAttribution = asRecord(result?.modelAttribution) ?? asRecord(metadata?.modelAttribution)\n const backend = asRecord(result?.effectiveBackend) ?? asRecord(metadata?.effectiveBackend)\n const source = nonEmptyString(result?.servedSource)\n ?? nonEmptyString(modelAttribution?.servedSource)\n ?? nonEmptyString(backend?.source)\n const servedSource = source === 'request' || source === 'environment' || source === 'profile'\n ? source\n : undefined\n const servedModel = nonEmptyString(result?.servedModel)\n ?? nonEmptyString(modelAttribution?.servedModel)\n ?? nonEmptyString(backend?.model)\n const servedProvider = nonEmptyString(result?.servedProvider)\n ?? nonEmptyString(modelAttribution?.servedProvider)\n ?? nonEmptyString(backend?.provider)\n return {\n ...(servedModel ? { servedModel } : {}),\n ...(servedProvider ? { servedProvider } : {}),\n ...(servedSource ? { servedSource } : {}),\n }\n}\n\nfunction textFromResult(result: JsonRecord | undefined): string | undefined {\n return asString(result?.response)\n ?? asString(result?.finalText)\n ?? asString(result?.text)\n ?? asString(result?.output)\n}\n\nfunction completedMessagesForTurn(\n messages: SessionMessage[],\n turnId: string,\n): SessionMessage[] {\n return messages.filter((message) =>\n message.role === 'assistant'\n && message.metadata?.turnId === turnId\n && message.metadata?.interrupted !== true\n && (\n message.metadata?.completed === true\n || message.metadata?.status === 'completed'\n ),\n )\n}\n\nfunction normalizedMessageParts(\n message: SessionMessage,\n): { parts: JsonRecord[]; usage: ChatTurnUsage; derivedText: string } {\n const normalized: JsonRecord[] = []\n const usage: ChatTurnUsage = {}\n for (const raw of message.parts) {\n const record = asRecord(raw)\n if (!record) continue\n const part = normalizePersistedPart(record)\n if (!part) continue\n normalized.push(part)\n if (part.type === 'step-finish') addStepFinishUsage(part, usage)\n }\n\n const collapsed = collapseRedundantTextParts(normalized)\n const derivedText = collapsed\n .filter((part) => part.type === 'text')\n .map((part) => String(part.text ?? ''))\n .join('')\n return { parts: normalized, usage, derivedText }\n}\n\nfunction projectMessageParts(parts: JsonRecord[], finalText: string): JsonRecord[] {\n const order: string[] = []\n const map = new Map<string, JsonRecord>()\n for (let index = 0; index < parts.length; index += 1) {\n const part = parts[index]!\n const type = String(part.type ?? '')\n // A session's aggregate message contains every step receipt. Preserve each\n // occurrence just as the live producer does; other kinds merge by their\n // canonical identity.\n const key = type === 'step-start' || type === 'step-finish'\n ? `${type}:#${index}`\n : getPartKey(part)\n if (!map.has(key)) order.push(key)\n map.set(key, mergePersistedPart(map.get(key), part))\n }\n return finalizeAssistantParts(order, map, finalText)\n}\n\n/**\n * Recover the durable content a Sandbox recorder attached to one assistant\n * message. The caller is responsible for proving that the message belongs to\n * the exact terminal turn; this projection deliberately makes no conclusion\n * about whether that terminal state was completed or interrupted.\n */\nexport function recoverSandboxAssistantMessage(message: SessionMessage): DetachedTurnFinal {\n const recovered = normalizedMessageParts(message)\n return {\n ...(recovered.derivedText ? { text: recovered.derivedText } : {}),\n ...(hasUsage(recovered.usage) ? { usage: recovered.usage } : {}),\n parts: projectMessageParts(recovered.parts, recovered.derivedText),\n }\n}\n\nfunction hasUsage(usage: ChatTurnUsage): boolean {\n return Object.values(usage).some((value) =>\n typeof value === 'number' && Number.isFinite(value),\n )\n}\n\n/**\n * Read the exact completed turn from its keyed cache and completed message.\n * Never borrow a session-wide aggregate from a concurrently advancing session.\n * Observation failure is retryable and distinct from successful absent reads.\n * Model attribution comes from that same identity-checked cache record, never\n * from a second unchecked lookup that could belong to a different turn.\n */\nexport async function readCompletedSandboxTurn(\n box: CompletedSandboxTurnSource,\n options: ReadCompletedSandboxTurnOptions,\n): Promise<(DetachedTurnFinal & Pick<ChatTurnModelAttribution, 'servedModel' | 'servedProvider' | 'servedSource'>) | null> {\n const { turnId, sessionId, log } = options\n const session = box.session(sessionId)\n const [cacheOutcome, messagesOutcome] = await Promise.allSettled([\n box.findCompletedTurn(turnId, { sessionId }),\n session.messages({ limit: 1_000, ...(options.since !== undefined ? { since: options.since } : {}) }),\n ])\n\n if (cacheOutcome.status === 'rejected') {\n log?.('[chat-routes] completed Sandbox turn cache lookup failed', {\n turnId,\n sessionId,\n error: String(cacheOutcome.reason),\n })\n }\n if (messagesOutcome.status === 'rejected') {\n log?.('[chat-routes] completed Sandbox session message lookup failed', {\n turnId,\n sessionId,\n error: String(messagesOutcome.reason),\n })\n }\n\n const rawCached = cacheOutcome.status === 'fulfilled' ? cacheOutcome.value : null\n const cached = rawCached\n && rawCached.turnId === turnId\n && rawCached.sessionId === sessionId\n ? rawCached\n : null\n if (rawCached && !cached) {\n log?.('[chat-routes] ignored mismatched completed Sandbox turn cache record', {\n turnId,\n sessionId,\n })\n }\n\n const messages = messagesOutcome.status === 'fulfilled'\n ? messagesOutcome.value\n : []\n const matchingMessages = completedMessagesForTurn(messages, turnId)\n const matchingCount = matchingMessages.length\n const message = matchingCount === 1 ? matchingMessages[0]! : null\n if (matchingCount > 1) {\n log?.('[chat-routes] ignored ambiguous completed Sandbox session messages', {\n turnId,\n sessionId,\n matchingCount,\n })\n }\n\n if (!cached && !message) {\n // A failed read is unknown, not proof that the turn is absent. The durable\n // owner must retry observation rather than admit another execution.\n if (cacheOutcome.status === 'rejected' || messagesOutcome.status === 'rejected') {\n throw new Error('Completed Sandbox turn could not be verified; retry the exact read')\n }\n if (rawCached || matchingCount > 1) {\n throw new Error('Completed Sandbox turn identity is inconsistent; reconciliation is required')\n }\n return null\n }\n\n // Only turn-addressed evidence is safe. A newer turn can complete between\n // reading the message list and reading a session-wide aggregate result.\n const result = cached ? asRecord(cached.result) : undefined\n\n const recovered = message ? normalizedMessageParts(message) : null\n const text = textFromResult(result) ?? recovered?.derivedText\n const resultUsage = usageFromResult(result)\n const usage = { ...(recovered?.usage ?? {}), ...resultUsage }\n const resultParts = Array.isArray(result?.parts)\n ? result.parts\n .map((part) => asRecord(part))\n .filter((part): part is JsonRecord => Boolean(part))\n .map((part) => normalizePersistedPart(part))\n .filter((part): part is JsonRecord => Boolean(part))\n : null\n const sourceParts = recovered?.parts ?? resultParts\n const parts = sourceParts\n ? projectMessageParts(sourceParts, text ?? recovered?.derivedText ?? '')\n : undefined\n\n return {\n ...(text !== undefined ? { text } : {}),\n ...(hasUsage(usage) ? { usage } : {}),\n ...(parts ? { parts } : {}),\n ...attributionFromResult(result),\n }\n}\n","/**\n * Exact terminal observation for a native Sandbox execution that another\n * request already admitted. This module never sends a prompt, calls\n * `driveTurn`, or cancels a run.\n */\n\nimport type { SandboxInstance, SessionInfo, SessionMessage } from '@tangle-network/sandbox'\nimport type { ChatTurnUsage } from './turn-routes'\nimport type { SessionExecutionInfo } from '@tangle-network/sandbox'\nimport {\n attributionFromResult,\n readCompletedSandboxTurn,\n recoverSandboxAssistantMessage,\n usageFromResult,\n} from './completed-sandbox-turn'\n\nexport interface NativeCompletionReceipt {\n state: 'completed' | 'failed'\n text: string\n parts: Array<Record<string, unknown>>\n /** An empty object is an unknown usage receipt, never a measured zero. */\n usage: ChatTurnUsage\n servedModel?: string\n servedProvider?: string\n servedSource?: 'request' | 'environment' | 'profile'\n error?: string\n completedTurnIds: string[]\n}\n\nexport interface NativeCompletionAdmission {\n executionId: string\n state: 'open' | 'closed'\n admittedTurnIds: readonly string[]\n ownerLeaseUntil: Date | number\n updatedAt?: Date | number\n closedAt?: Date | number | null\n}\n\n/** Product persistence for the one admission that authorizes this observer. */\nexport interface NativeCompletionAdmissionStore {\n read(executionId: string): Promise<NativeCompletionAdmission | null>\n renew(executionId: string, now: Date): Promise<NativeCompletionAdmission | null>\n closeExpired(executionId: string, now: Date): Promise<NativeCompletionAdmission | null>\n}\n\n/** The official Sandbox reads needed for exact native completion observation. */\nexport type NativeCompletionSessionSource = Pick<SandboxInstance, 'findCompletedTurn' | 'session'>\n\nexport interface NativeCompletionObservationOptions {\n /** Null when the product cannot currently resolve the admitted Sandbox. */\n source: NativeCompletionSessionSource | null\n admissionStore: NativeCompletionAdmissionStore\n executionId: string\n sessionId: string\n turnId: string\n registeredAt: number\n absentDispatchDeadlineMs?: number\n receiptDeadlineMs?: number\n now?: number\n}\n\nexport type NativeCompletionObservation =\n | { state: 'running' }\n | { state: 'completed'; receipt: NativeCompletionReceipt }\n | { state: 'failed'; receipt: NativeCompletionReceipt }\n\n/** One exact turn receipt before an admission's ordered aggregate. */\nexport interface NativeCompletionTurnReceipt {\n turnId: string\n state: 'completed' | 'failed'\n text: string\n parts: Array<Record<string, unknown>>\n usage: ChatTurnUsage\n servedModel?: string\n servedProvider?: string\n servedSource?: 'request' | 'environment' | 'profile'\n error?: string\n}\n\nconst DEFAULT_ABSENT_DISPATCH_DEADLINE_MS = 10 * 60_000\nconst DEFAULT_RECEIPT_DEADLINE_MS = 10 * 60_000\n/** Session messages are read from this long before registration, so clock\n * skew between the product Worker and the Sandbox cannot hide the turn. */\nconst MESSAGE_READ_SKEW_MS = 10 * 60_000\n\n/**\n * Largest interrupted execution whose event stream is replayed for its\n * terminal result. The replay parses every event and re-compares the growing\n * response text on each token, so its CPU grows faster than the run; a longer\n * run takes its receipt from the recorded message and failure reason alone.\n */\nconst INTERRUPTED_RESULT_REPLAY_MAX_EVENTS = 2_000\n/**\n * The fixed id of the terminal the sidecar appends when it fails an execution\n * whose runtime restarted before the run ended.\n */\nconst RUNTIME_RESTART_TERMINAL_EVENT_ID = 'interrupted-done'\n/** The sidecar's reason for that terminal, used when the replay is not read. */\nexport const RUNTIME_RESTARTED_ERROR = 'Execution interrupted: the agent runtime restarted before the run produced a terminal event. The run cannot be resumed; retry it.'\n\nfunction replayableExecution(run: Pick<SessionExecutionInfo, 'eventCount'>): boolean {\n return typeof run.eventCount === 'number' && run.eventCount <= INTERRUPTED_RESULT_REPLAY_MAX_EVENTS\n}\n\n/**\n * Serialized UTF-8 budget for one terminal receipt. A Cloudflare Workflow\n * refuses a step result over 1 MiB and D1 refuses a row over 2 MB, and a\n * receipt approaches both as tool payloads accumulate over a long turn.\n */\nexport const NATIVE_COMPLETION_RECEIPT_MAX_BYTES = 900 * 1024\n\n/** Per-string caps tried in order until a receipt fits its budget. */\n// Status, ids and paths stay well under the smallest cap.\nconst RECEIPT_STRING_CAPS = [16_384, 4_096, 1_024, 256] as const\n\nconst encoder = new TextEncoder()\n\nfunction serializedBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value)).length\n}\n\nfunction capString(value: string, cap: number): string {\n if (value.length <= cap) return value\n return `${value.slice(0, cap)}…[truncated ${value.length - cap} characters; the Sandbox session keeps the full value]`\n}\n\nfunction capStrings(value: unknown, cap: number): unknown {\n if (typeof value === 'string') return capString(value, cap)\n if (Array.isArray(value)) return value.map((item) => capStrings(item, cap))\n const object = record(value)\n if (!object) return value\n return Object.fromEntries(Object.entries(object).map(([key, item]) => [key, capStrings(item, cap)]))\n}\n\n/** Tool payloads and reasoning shrink; the answer text, files and interactions do not. */\nfunction capReceiptPart(part: Record<string, unknown>, cap: number): Record<string, unknown> {\n const type = String(part.type ?? '')\n if (type === 'tool') {\n const state = record(part.state)\n if (!state) return part\n const capped = capStrings(state, cap) as Record<string, unknown>\n if (serializedBytes(capped) === serializedBytes(state)) return part\n return { ...part, state: { ...capped, metadata: { ...record(capped.metadata), receiptTruncated: true } } }\n }\n if (type === 'reasoning' && typeof part.text === 'string' && part.text.length > cap) {\n return { ...part, text: capString(part.text, cap), receiptTruncated: true }\n }\n return part\n}\n\n/** Last resort for a receipt whose part count alone exceeds the budget. */\nfunction skeletonPart(part: Record<string, unknown>): Record<string, unknown> | null {\n const type = String(part.type ?? '')\n if (type === 'reasoning') return null\n if (type !== 'tool') return part\n const state = record(part.state)\n return {\n type: 'tool',\n ...(part.id !== undefined ? { id: part.id } : {}),\n ...(part.tool !== undefined ? { tool: part.tool } : {}),\n state: { status: state?.status ?? 'completed', metadata: { receiptTruncated: true } },\n }\n}\n\n/**\n * Fit a terminal receipt inside {@link NATIVE_COMPLETION_RECEIPT_MAX_BYTES}.\n * A receipt under the budget is returned unchanged. Over it, the longest\n * strings in tool payloads and reasoning are clipped with a marker, at\n * successively smaller caps, so a long turn's answer is always persistable.\n */\nexport function boundNativeCompletionReceipt(\n receipt: NativeCompletionReceipt,\n maxBytes: number = NATIVE_COMPLETION_RECEIPT_MAX_BYTES,\n): NativeCompletionReceipt {\n if (serializedBytes(receipt) <= maxBytes) return receipt\n for (const cap of RECEIPT_STRING_CAPS) {\n const bounded = { ...receipt, parts: receipt.parts.map((part) => capReceiptPart(part, cap)) }\n if (serializedBytes(bounded) <= maxBytes) return bounded\n }\n const skeleton = {\n ...receipt,\n parts: receipt.parts.map(skeletonPart).filter((part): part is Record<string, unknown> => part !== null),\n }\n if (serializedBytes(skeleton) <= maxBytes) return skeleton\n // Only the answer text is left to shrink.\n const textBudget = Math.max(0, Math.floor(maxBytes / 4))\n return {\n ...skeleton,\n text: capString(skeleton.text, textBudget),\n parts: skeleton.parts.map((part) => (part.type === 'text' && typeof part.text === 'string'\n ? { ...part, text: capString(part.text, textBudget) }\n : part)),\n }\n}\n\nfunction terminal(receipt: NativeCompletionReceipt): NativeCompletionObservation {\n const bounded = boundNativeCompletionReceipt(receipt)\n return bounded.state === 'completed'\n ? { state: 'completed', receipt: bounded }\n : { state: 'failed', receipt: bounded }\n}\n\nfunction timestamp(value: Date | number | null | undefined): number | undefined {\n if (value instanceof Date) return value.getTime()\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n}\n\nfunction isSandboxNotFoundError(value: unknown): boolean {\n const error = record(value)\n return error?.name === 'NotFoundError' || error?.code === 'NOT_FOUND' || error?.status === 404\n}\n\nfunction interruptedMessages(messages: SessionMessage[], turnId: string): SessionMessage[] {\n return messages.filter((message) =>\n message.role === 'assistant'\n && message.metadata?.turnId === turnId\n && (message.metadata.interrupted === true || message.metadata.status === 'interrupted'),\n )\n}\n\nfunction turnAssistantMessages(messages: SessionMessage[], turnId: string): SessionMessage[] {\n return messages.filter((message) => message.role === 'assistant' && message.metadata?.turnId === turnId)\n}\n\nfunction consistent<T>(values: Array<T | undefined>): T | undefined {\n if (values.length === 0 || values.some((value) => value === undefined)) return undefined\n const first = values[0] as T\n return values.every((value) => value === first) ? first : undefined\n}\n\nfunction sum(values: Array<number | undefined>): number | undefined {\n if (values.some((value) => value === undefined)) return undefined\n return (values as number[]).reduce((total, value) => total + value, 0)\n}\n\n/** Assemble admitted exact receipts in dispatch order. Missing usage stays unknown. */\nexport function aggregateNativeCompletionReceipts(turns: readonly NativeCompletionTurnReceipt[]): NativeCompletionReceipt {\n const failed = turns.some((turn) => turn.state === 'failed')\n const usage: ChatTurnUsage = {}\n for (const key of ['inputTokens', 'outputTokens', 'reasoningTokens', 'cacheReadTokens', 'cacheWriteTokens', 'costUsd'] as const) {\n const total = sum(turns.map((turn) => turn.usage[key]))\n if (total !== undefined) usage[key] = total\n }\n // A missing exact receipt is the terminal observer failure even when an\n // earlier execution also failed. Preserve that earlier partial transcript,\n // but surface why this aggregate could not be fully reconciled.\n const error = turns.find((turn) =>\n turn.error?.startsWith('Admitted native execution did not produce')\n || turn.error?.startsWith('Native Sandbox execution ledger is missing'),\n )?.error ?? turns.find((turn) => turn.error)?.error\n return {\n state: failed ? 'failed' : 'completed',\n text: turns.map((turn) => turn.text).join(''),\n parts: turns.flatMap((turn) => turn.parts),\n usage,\n ...(consistent(turns.map((turn) => turn.servedModel)) ? { servedModel: consistent(turns.map((turn) => turn.servedModel)) } : {}),\n ...(consistent(turns.map((turn) => turn.servedProvider)) ? { servedProvider: consistent(turns.map((turn) => turn.servedProvider)) } : {}),\n ...(consistent(turns.map((turn) => turn.servedSource)) ? { servedSource: consistent(turns.map((turn) => turn.servedSource)) } : {}),\n ...(error ? { error } : {}),\n completedTurnIds: turns.map((turn) => turn.turnId),\n }\n}\n\n/** The failure a completed run with an aborted tool call settles with. */\nexport const TOOL_ABORTED_ERROR = 'The Sandbox aborted a tool call before the turn finished, as when its runtime restarts mid-turn'\n\n/**\n * The name of a tool whose call the Sandbox aborted, or undefined. OpenCode\n * marks a tool interrupted by a runtime restart with \"Tool execution aborted\";\n * an ordinary tool error (a missing file, an HTTP 403) does not end the turn.\n */\nfunction abortedToolCall(parts: ReadonlyArray<Record<string, unknown>>): string | undefined {\n for (const part of parts) {\n if (part.type !== 'tool') continue\n const state = part.state as { status?: unknown; error?: unknown } | undefined\n if (state?.status === 'error' && /tool execution aborted/i.test(String(state.error ?? ''))) {\n return typeof part.tool === 'string' ? part.tool : 'tool'\n }\n }\n return undefined\n}\n\n/**\n * Whether the session still runs this admission's work, so its lease should be\n * renewed. The session's own status is not enough: it keeps a persisted\n * `running` status when the runtime restarts under it (the sidecar may report\n * that as recovery state `stale`, or not at all), while its execution ledger\n * has already failed the interrupted run. Renewing on status alone observed\n * GTM b81b's restarted completions forever on 2026-10-10. Live means an\n * admitted turn's execution is active, or not yet recorded while its dispatch\n * can still be on the way.\n */\nfunction admittedWorkIsLive(\n status: SessionInfo,\n runsByTurnId: ReadonlyMap<string, SessionExecutionInfo>,\n admittedTurnIds: readonly string[],\n dispatchMayBePending: boolean,\n): boolean {\n if (status.status !== 'queued' && status.status !== 'running') return false\n if (status.raw?.state === 'stale') return false\n return admittedTurnIds.some((turnId) => {\n const run = runsByTurnId.get(turnId)\n return run ? run.status === 'active' : dispatchMayBePending\n })\n}\n\nfunction missingTurnReceipt(turnId: string, error: string): NativeCompletionTurnReceipt {\n // An execution ledger entry proves this turn reached a terminal state, but\n // not that its terminal transcript was retained. Keep any earlier exact\n // evidence when the bounded wait expires; `{}` means aggregate usage is\n // unknown because this turn has no usage receipt.\n return { turnId, state: 'failed', text: '', parts: [], usage: {}, error }\n}\n\n/**\n * Observe only the turns admitted under `executionId`. Transport failures throw\n * so the durable owner retries. A missing or partial terminal record remains\n * `running` until the configured deadline; it never starts another run.\n */\nexport async function observeNativeCompletion(\n options: NativeCompletionObservationOptions,\n): Promise<NativeCompletionObservation> {\n const now = options.now ?? Date.now()\n let admission = await options.admissionStore.read(options.executionId)\n if (!admission) throw new Error('Native completion admission is missing')\n if (admission.executionId !== options.executionId) throw new Error('Native completion admission identity conflict')\n const leaseUntil = timestamp(admission.ownerLeaseUntil) ?? 0\n const source = options.source\n const session = source?.session(options.sessionId)\n let status: SessionInfo | null\n if (!session) {\n status = null\n } else {\n try {\n status = await session.status()\n } catch (error) {\n if (!isSandboxNotFoundError(error)) throw error\n status = null\n }\n }\n\n if (!status || !session || !source) {\n if (admission.state === 'open' && leaseUntil <= now) {\n admission = await options.admissionStore.closeExpired(options.executionId, new Date(now)) ?? admission\n }\n if (admission.state === 'open' || now - options.registeredAt < (options.absentDispatchDeadlineMs ?? DEFAULT_ABSENT_DISPATCH_DEADLINE_MS)) {\n return { state: 'running' }\n }\n return terminal(aggregateNativeCompletionReceipts([\n missingTurnReceipt(options.turnId, 'Native Sandbox dispatch was not observed before the admission deadline'),\n ]))\n }\n const exactSession = session\n const dispatchMayBePending = now - options.registeredAt < (options.absentDispatchDeadlineMs ?? DEFAULT_ABSENT_DISPATCH_DEADLINE_MS)\n let runsByTurnId: Map<string, SessionExecutionInfo> | undefined\n const readRuns = async () => runsByTurnId ??= new Map((await exactSession.runs()).map((run) => [run.executionId, run]))\n\n if (admission.state === 'open') {\n if (admittedWorkIsLive(status, await readRuns(), admission.admittedTurnIds, dispatchMayBePending)) {\n admission = await options.admissionStore.renew(options.executionId, new Date(now)) ?? admission\n } else if (leaseUntil <= now) {\n admission = await options.admissionStore.closeExpired(options.executionId, new Date(now)) ?? admission\n }\n if (admission.state === 'open') return { state: 'running' }\n }\n // `closeExpired` may return the current row after another owner appended a\n // continuation. Read its full ordered ledger rather than observing the\n // earlier snapshot from before the close attempt.\n const admittedTurnIds = [...admission.admittedTurnIds]\n if (admittedTurnIds.length === 0 || admittedTurnIds[0] !== options.turnId) {\n throw new Error('Native completion admission has an invalid turn sequence')\n }\n\n const ledger = await readRuns()\n const missingExecution = admittedTurnIds.find((turnId) => !ledger.has(turnId))\n if (missingExecution && dispatchMayBePending) return { state: 'running' }\n if (admittedTurnIds.some((turnId) => ledger.get(turnId)?.status === 'active')) return { state: 'running' }\n\n // Completed recovery owns its message read. Load interrupted history only\n // when it is needed, and share that one read across failed continuations.\n // Both reads start shortly before registration, so their size follows this\n // turn rather than the whole session.\n const since = Math.max(0, options.registeredAt - MESSAGE_READ_SKEW_MS)\n let messages: SessionMessage[] | undefined\n const recovered: NativeCompletionTurnReceipt[] = []\n for (const turnId of admittedTurnIds) {\n const run = ledger.get(turnId)\n if (!run) {\n recovered.push(missingTurnReceipt(turnId, 'Native Sandbox execution ledger is missing the admitted turn'))\n continue\n }\n const completed = run.status === 'completed'\n ? await readCompletedSandboxTurn(source, { turnId, sessionId: options.sessionId, since })\n : null\n if (run.status === 'completed' && completed) {\n const aborted = abortedToolCall(completed.parts ?? [])\n recovered.push({\n ...completed,\n turnId,\n // A run the runtime ended while a tool was still running reports\n // completed, but its reply stops mid-work. It is a failed turn.\n state: aborted ? 'failed' : 'completed',\n text: completed.text ?? '',\n parts: completed.parts ?? [],\n usage: completed.usage ?? {},\n ...(aborted ? { error: `${TOOL_ABORTED_ERROR} (${aborted})` } : {}),\n })\n continue\n }\n // The process that would stamp a turn interrupted is gone when its runtime\n // restarts; the sidecar fails the execution on restart, and the turn's own\n // assistant message holds whatever reply it had streamed.\n const restarted = run.status === 'failed' && run.lastEventId === RUNTIME_RESTART_TERMINAL_EVENT_ID\n let interrupted = run.status === 'failed' || run.status === 'cancelled'\n ? interruptedMessages(messages ??= await exactSession.messages({ limit: 1_000, since }), turnId)\n : []\n if (restarted && interrupted.length === 0) interrupted = turnAssistantMessages(messages ?? [], turnId)\n if (restarted && interrupted.length !== 1) {\n recovered.push({ turnId, state: 'failed', text: '', parts: [], usage: {}, error: RUNTIME_RESTARTED_ERROR })\n continue\n }\n if ((run.status === 'failed' || run.status === 'cancelled') && interrupted.length === 1) {\n const message = recoverSandboxAssistantMessage(interrupted[0]!)\n const result = replayableExecution(run)\n ? await exactSession.result({ executionId: turnId }) as unknown as Record<string, unknown>\n : undefined\n recovered.push({\n turnId,\n state: 'failed',\n text: message.text ?? (typeof result?.response === 'string' ? result.response : undefined) ?? '',\n parts: message.parts ?? [],\n usage: { ...message.usage, ...usageFromResult(result) },\n ...attributionFromResult(result),\n error: (typeof result?.error === 'string' ? result.error : undefined)\n ?? interrupted[0]!.metadata?.interruptReason\n ?? (restarted ? RUNTIME_RESTARTED_ERROR : undefined)\n ?? status.failureReason?.message,\n })\n continue\n }\n const closedAt = timestamp(admission.closedAt)\n ?? timestamp(admission.updatedAt)\n ?? options.registeredAt\n if (now - closedAt < (options.receiptDeadlineMs ?? DEFAULT_RECEIPT_DEADLINE_MS)) return { state: 'running' }\n recovered.push(missingTurnReceipt(\n turnId,\n `Admitted native execution did not produce an exact completion: ${turnId}`,\n ))\n }\n return terminal(aggregateNativeCompletionReceipts(recovered))\n}\n","/**\n * The chat turn that attaches to a decided plan's follow-up execution.\n *\n * Deciding a plan does not run anything in the product. The Sandbox platform\n * enqueues the approved (or revision) execution itself under the turn id\n * `planFollowUpTurnId(planId, revision, outcome)`, and the Sidecar names that\n * execution `plan-followup-<sha256(sessionId \\0 turnId)>`. The browser card then\n * re-enters the chat route with a `planFollowUp` descriptor. That attach is a\n * real turn: it streams, persists an assistant row, and bills. It therefore\n * needs the same single-runner protection as a fresh turn.\n *\n * This module owns that attach: the descriptor check against the persisted\n * plan, the execution identity, the exclusive D1 claim on the `turn_status`\n * row, and the replay-then-follow event stream. Products keep their own\n * authorization, billing, and persistence around it.\n */\nimport { createHash, randomUUID } from 'node:crypto'\nimport type { SandboxEvent, SandboxInstance } from '@tangle-network/sandbox'\nimport { persistedPartToPlan, planFollowUpTurnId } from '../plans'\n\n/** The decision a follow-up continues. */\nexport type PlanFollowUpOutcome = 'approved' | 'rejected'\n\n/** The descriptor a decided plan card sends back to the chat route. */\nexport interface PlanFollowUpAttach {\n planId: string\n revision: number\n outcome: PlanFollowUpOutcome\n turnId: string\n}\n\n/** Result of checking a follow-up descriptor against the session's persisted plans. */\nexport type PlanFollowUpResolution =\n | { ok: true; attach: PlanFollowUpAttach; executionId: string }\n | { ok: false; status: 400 | 409; code: string; error: string }\n\n/** Result of one exclusive follow-up claim. */\nexport type PlanFollowUpAdmission =\n | { admitted: true; lease: string }\n | { admitted: false; reason: 'in_flight' | 'completed' }\n\n/** Result of settling a claim. `lease_lost` means a stale reclaim superseded it. */\nexport type PlanFollowUpSettlement =\n | { settled: true }\n | { settled: false; reason: 'lease_lost' }\n\n/** A bound D1 statement used by the follow-up gate. */\nexport interface D1BoundForPlanFollowUps {\n run(): Promise<unknown>\n}\n\n/**\n * Structural D1 contract for the follow-up gate. `batch` is required: it is\n * D1's atomic boundary, and the gate confirms ownership from the row read in\n * the same batch instead of from a driver-specific affected-row count.\n */\nexport interface D1LikeForPlanFollowUps {\n prepare(sql: string): { bind(...values: unknown[]): D1BoundForPlanFollowUps }\n batch(statements: D1BoundForPlanFollowUps[]): Promise<unknown[]>\n}\n\n/** Configure the follow-up gate. Clock and lease are injectable for tests. */\nexport interface PlanFollowUpGateOptions {\n now?: () => Date\n createLease?: () => string\n /** How long a `running` claim may sit before another isolate may reclaim it. Default one hour. */\n staleAfterMs?: number\n}\n\n/** Exclusive claim over one follow-up execution's `turn_status` row. */\nexport interface PlanFollowUpGate {\n admit(executionId: string, scopeId: string): Promise<PlanFollowUpAdmission>\n /** Record a completed attach. Only the current lease may settle. */\n settle(executionId: string, scopeId: string, lease: string): Promise<PlanFollowUpSettlement>\n /** Record a failed attach so a retry is admitted immediately. */\n abandon(executionId: string, scopeId: string, lease: string): Promise<PlanFollowUpSettlement>\n}\n\nconst PLAN_FOLLOW_UP_STALE_AFTER_MS = 60 * 60 * 1000\nconst PLAN_FOLLOW_UP_ATTACH_TIMEOUT_MS = 120_000\nconst TERMINAL_SESSION_EVENT_TYPES = new Set(['done', 'error', 'result'])\n\n/** The Sidecar's execution id for a follow-up turn. Keep it byte-identical to the Sidecar. */\nexport function planFollowUpExecutionId(sessionId: string, turnId: string): string {\n return `plan-followup-${createHash('sha256').update(`${sessionId}\\0${turnId}`).digest('hex')}`\n}\n\n/** Parse a follow-up descriptor. The turn id is derived, never trusted. */\nexport function parsePlanFollowUpAttach(value: unknown): PlanFollowUpAttach | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n const planId = typeof record.planId === 'string' ? record.planId.trim() : ''\n const turnId = typeof record.turnId === 'string' ? record.turnId.trim() : ''\n const revision = record.revision\n const outcome = record.outcome\n if (!planId || !turnId || (outcome !== 'approved' && outcome !== 'rejected')) return null\n if (typeof revision !== 'number' || !Number.isInteger(revision) || revision < 1) return null\n if (turnId !== planFollowUpTurnId(planId, revision, outcome)) return null\n return { planId, revision, outcome, turnId }\n}\n\n/**\n * Validate a follow-up request against what the session actually persisted:\n * the plan must be in this session at this revision and must already carry the\n * claimed decision.\n */\nexport function resolvePlanFollowUpRequest(input: {\n sessionId: string\n planFollowUp: unknown\n messages: ReadonlyArray<{ role: string; parts?: unknown }>\n}): PlanFollowUpResolution {\n const attach = parsePlanFollowUpAttach(input.planFollowUp)\n if (!attach) {\n return { ok: false, status: 400, code: 'invalid_plan_follow_up', error: 'Invalid planFollowUp descriptor' }\n }\n const plan = input.messages\n .filter((message) => message.role === 'assistant' && Array.isArray(message.parts))\n .flatMap((message) => message.parts as unknown[])\n .map((part) => (part && typeof part === 'object' && !Array.isArray(part)\n ? persistedPartToPlan(part as Record<string, unknown>)\n : null))\n .find((candidate) => candidate?.planId === attach.planId && candidate.revision === attach.revision)\n if (!plan) {\n return {\n ok: false,\n status: 409,\n code: 'plan_follow_up_missing',\n error: `Plan ${attach.planId} revision ${attach.revision} is not in this session`,\n }\n }\n if (plan.status !== attach.outcome) {\n return {\n ok: false,\n status: 409,\n code: 'plan_follow_up_not_decided',\n error: `Plan ${attach.planId} is ${plan.status}, not ${attach.outcome}`,\n }\n }\n return { ok: true, attach, executionId: planFollowUpExecutionId(input.sessionId, attach.turnId) }\n}\n\n/**\n * Session events carry two payload shapes: replayed events hold the stream\n * event under `data`, while live events nest it under `data.properties`.\n * Downstream reads `data.<field>`, so a live event is unwrapped. A replayed\n * payload that still carries its own `type` drops it, so `data` holds only the\n * event's fields.\n */\nexport function unwrapSessionEventPayload(event: SandboxEvent): SandboxEvent {\n const data = event.data && typeof event.data === 'object' && !Array.isArray(event.data)\n ? event.data as Record<string, unknown>\n : undefined\n if (!data) return event\n const properties = data.properties && typeof data.properties === 'object' && !Array.isArray(data.properties)\n ? data.properties as Record<string, unknown>\n : undefined\n if (properties) return { ...event, data: properties } as SandboxEvent\n if (typeof data.type === 'string') {\n const { type: _type, ...rest } = data\n return { ...event, data: rest } as SandboxEvent\n }\n return event\n}\n\n/**\n * Attach to the already-enqueued follow-up execution: replay its buffered\n * events from the start, then follow it live to completion. It never sends a\n * new prompt. The session-events iterator has no reconnect, so a stream that\n * ends without a terminal event fails instead of persisting a partial turn.\n */\nexport async function* streamPlanFollowUpEvents(\n box: SandboxInstance,\n sessionId: string,\n executionId: string,\n options: { timeoutMs?: number } = {},\n): AsyncGenerator<SandboxEvent> {\n const timeoutMs = options.timeoutMs ?? PLAN_FOLLOW_UP_ATTACH_TIMEOUT_MS\n const abort = new AbortController()\n const iterator = box.session(sessionId).events({\n executionId,\n since: '0',\n signal: abort.signal,\n })[Symbol.asyncIterator]()\n let timeout: ReturnType<typeof setTimeout> | undefined\n let sawTerminalEvent = false\n const track = (event: SandboxEvent): SandboxEvent => {\n if (TERMINAL_SESSION_EVENT_TYPES.has(String(event.type ?? ''))) sawTerminalEvent = true\n return unwrapSessionEventPayload(event)\n }\n try {\n const first = await Promise.race([\n iterator.next(),\n new Promise<never>((_, reject) => {\n timeout = setTimeout(\n () => reject(new Error(`Plan follow-up ${executionId} did not start within ${Math.round(timeoutMs / 1000)} seconds`)),\n timeoutMs,\n )\n }),\n ])\n if (timeout) clearTimeout(timeout)\n if (first.done) throw new Error(`Plan follow-up ${executionId} ended before emitting an event`)\n yield track(first.value)\n while (true) {\n const next = await iterator.next()\n if (next.done) {\n if (!sawTerminalEvent) {\n throw new Error(`Plan follow-up ${executionId} stream ended without a terminal event`)\n }\n return\n }\n yield track(next.value)\n }\n } finally {\n if (timeout) clearTimeout(timeout)\n abort.abort()\n await iterator.return?.(undefined)\n }\n}\n\ninterface PlanFollowUpStatusRow {\n status: string\n leaseToken: string | null\n}\n\n/**\n * Exclusive claim over a follow-up execution in the shared `turn_status`\n * table. Every claim writes a random lease and confirms ownership from the\n * row returned by the same D1 batch. A running claim older than\n * `staleAfterMs` may be reclaimed; the superseded holder then settles into\n * `lease_lost` instead of overwriting the newer claim. Apply\n * `TURN_STATUS_LEASE_MIGRATION_SQL` (from `/stream`) once after the turn\n * tables: it adds the `leaseToken` column the claim writes.\n */\nexport function createD1PlanFollowUpGate(\n db: D1LikeForPlanFollowUps,\n options: PlanFollowUpGateOptions = {},\n): PlanFollowUpGate {\n const now = options.now ?? (() => new Date())\n const createLease = options.createLease ?? randomUUID\n const staleAfterMs = options.staleAfterMs ?? PLAN_FOLLOW_UP_STALE_AFTER_MS\n const readStatus = (executionId: string) => db.prepare(`\n SELECT status, leaseToken\n FROM turn_status\n WHERE turnId = ?\n LIMIT 1\n `).bind(executionId)\n\n async function readAfterBatch(\n write: D1BoundForPlanFollowUps,\n executionId: string,\n operation: string,\n ): Promise<PlanFollowUpStatusRow> {\n const results = await db.batch([write, readStatus(executionId)])\n const rows = (results[1] as { results?: unknown[] } | undefined)?.results\n const row = rows?.[0] as Partial<PlanFollowUpStatusRow> | undefined\n if (!row || typeof row.status !== 'string') {\n throw new Error(`Plan follow-up ${executionId} has no status after ${operation}`)\n }\n if (row.leaseToken !== null && row.leaseToken !== undefined && typeof row.leaseToken !== 'string') {\n throw new Error(`Plan follow-up ${executionId} has an invalid lease after ${operation}`)\n }\n return { status: row.status, leaseToken: row.leaseToken ?? null }\n }\n\n async function settle(\n executionId: string,\n scopeId: string,\n lease: string,\n status: 'complete' | 'error',\n ): Promise<PlanFollowUpSettlement> {\n const row = await readAfterBatch(\n db.prepare(`\n UPDATE turn_status\n SET status = ?, scopeId = ?, updatedAt = ?\n WHERE turnId = ? AND status = 'running' AND leaseToken = ?\n `).bind(status, scopeId, now().toISOString(), executionId, lease),\n executionId,\n `settling ${status}`,\n )\n if (row.leaseToken !== lease) return { settled: false, reason: 'lease_lost' }\n if (row.status !== status) {\n throw new Error(`Plan follow-up ${executionId} remained ${row.status} while settling ${status}`)\n }\n return { settled: true }\n }\n\n return {\n async admit(executionId, scopeId) {\n const claimedAt = now()\n const lease = createLease()\n const staleBefore = new Date(claimedAt.getTime() - staleAfterMs).toISOString()\n const row = await readAfterBatch(\n db.prepare(`\n INSERT INTO turn_status (turnId, status, scopeId, updatedAt, leaseToken)\n VALUES (?, 'running', ?, ?, ?)\n ON CONFLICT(turnId) DO UPDATE SET\n status = 'running',\n scopeId = excluded.scopeId,\n updatedAt = excluded.updatedAt,\n leaseToken = excluded.leaseToken\n WHERE turn_status.status = 'error'\n OR (turn_status.status = 'running' AND turn_status.updatedAt < ?)\n `).bind(executionId, scopeId, claimedAt.toISOString(), lease, staleBefore),\n executionId,\n 'admission',\n )\n if (row.status === 'running' && row.leaseToken === lease) return { admitted: true, lease }\n if (row.status === 'running') return { admitted: false, reason: 'in_flight' }\n if (row.status === 'complete') return { admitted: false, reason: 'completed' }\n throw new Error(`Plan follow-up ${executionId} has unexpected admission status: ${row.status}`)\n },\n settle: (executionId, scopeId, lease) => settle(executionId, scopeId, lease, 'complete'),\n abandon: (executionId, scopeId, lease) => settle(executionId, scopeId, lease, 'error'),\n }\n}\n","import { getPartKey, mergePersistedPart, type StreamEvent } from '../stream/index'\nimport type { ChatTurnRouteProducer } from './turn-routes'\n\n/** Resolve chat route events and materialize their durable state records */\nexport interface ChatRouteDurableProjection {\n observe(event: unknown): void | Promise<void>\n materialize(): Array<Record<string, unknown>> | Promise<Array<Record<string, unknown>>>\n}\n\n/** Log chat route projection messages with optional metadata for durable processing */\nexport type ChatRouteDurableProjectionLogger =\n (message: string, meta?: Record<string, unknown>) => void\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Adds durable lifecycle projection to any producer lane without moving its\n * transport into agent-app. Purely STRUCTURAL: agent-app ships no implementer\n * of {@link ChatRouteDurableProjection} and deliberately does not — the one it\n * used to ship (`/durable-chat`) was removed in 0.44.0 with zero fleet imports.\n * Pass any `{ observe, materialize }` object backed by your own store. The projection is observed inline and its\n * materialized parts replace same-key pending snapshots after the stream\n * drains. Projection persistence is best-effort for the live lane: a store\n * outage must not terminate an otherwise healthy sandbox stream. Failures are\n * reported through the optional logger so products can retain diagnostics. */\nexport function withDurableChatProjection(\n producer: ChatTurnRouteProducer,\n projection: ChatRouteDurableProjection,\n log: ChatRouteDurableProjectionLogger = (message, meta) => console.error(message, meta ?? ''),\n): ChatTurnRouteProducer {\n let projected: Array<Record<string, unknown>> = []\n async function* stream(): AsyncGenerator<StreamEvent, void, unknown> {\n for await (const event of producer.stream) {\n try {\n await projection.observe(event)\n } catch (error) {\n log('[chat-routes] durable projection observe failed', {\n eventType: event.type,\n error: errorMessage(error),\n })\n }\n yield event\n }\n try {\n projected = await projection.materialize()\n } catch (error) {\n log('[chat-routes] durable projection materialize failed', {\n error: errorMessage(error),\n })\n }\n }\n return {\n ...producer,\n stream: stream(),\n assistantParts: () => {\n const parts = producer.assistantParts?.() ?? []\n const order: string[] = []\n const byKey = new Map<string, Record<string, unknown>>()\n for (const part of [...parts, ...projected]) {\n const key = getPartKey(part)\n if (!byKey.has(key)) order.push(key)\n byKey.set(key, mergePersistedPart(byKey.get(key), part))\n }\n return order.map((key) => byKey.get(key)!)\n },\n }\n}\n","/**\n * `createUploadRoute` — the multimodal middle. Accepts multipart file uploads\n * and returns `PromptInputPart`-shaped descriptors the client echoes back on\n * send (`ChatTurnRequestPayload.parts`):\n *\n * ≤ inlineMaxBytes (700 KiB default) → inline `data:` URI part — rides the\n * turn body directly, no sandbox round trip.\n * > inlineMaxBytes → written into the sandbox workspace (base64 through the\n * structural `write` seam — `box.fs` satisfies it) and referenced by\n * `path`. Mandatory two-step: the gateway caps request bodies at ~1 MiB,\n * so a large file can never ride the prompt POST.\n *\n * The sink is structural (no sandbox-SDK import); products pass `box.fs`.\n *\n * @remarks Sole consumer today is the `--chat` scaffold (`create-agent-app\n * --chat` → `template-chat/src/chat.ts`), the reference multimodal path — its\n * inline-`data:`-or-ephemeral-sandbox-workspace split stays the scaffold's\n * proven upload pattern, not a fleet primitive; keep that distinction in mind\n * before widening its surface. Fleet apps with a durable store of their own\n * (KV, or AES-GCM-encrypted R2) no longer need to hand-roll a vault upload\n * route: `createAttachmentUploadRoute` (`./attachment-upload`, agent-app#234)\n * is the shared hardened path for that persistence model — a content-sniffed\n * type gate, two-phase atomic batch writes, and per-kind/aggregate size caps,\n * all seamed through an injected `WriteAttachmentFn`. Point readers there\n * instead of widening this route to cover both models.\n */\n\nimport type { ChatTurnFilePartInput } from './wire'\n\n/** 700 KiB: base64 inflates ~4/3, so an inline part stays comfortably under\n * the ~1 MiB gateway body cap alongside the JSON envelope. */\nexport const UPLOAD_INLINE_MAX_BYTES = 700 * 1024\n\n/** 8 MiB default ceiling per file — one base64 `write` call handles it. Raise\n * it only with a sink that can take the bigger single write. */\nexport const UPLOAD_MAX_FILE_BYTES = 8 * 1024 * 1024\n\nconst DEFAULT_UPLOAD_DIRECTORY = '/workspace/uploads'\n\nfunction normalizeUploadDirectory(value: string): string | null {\n const withoutTrailingSlash = value.trim().replace(/\\/+$/, '')\n if (!withoutTrailingSlash.startsWith('/') || withoutTrailingSlash.length === 0) return null\n const segments = withoutTrailingSlash.slice(1).split('/')\n if (segments.some((segment) => segment.length === 0 || segment === '.' || segment === '..')) return null\n return `/${segments.join('/')}`\n}\n\n/** Structural match of the sandbox SDK's `box.fs` write surface (v0.10.5+:\n * `encoding: 'base64'` is the worker-safe binary path). */\nexport interface SandboxUploadSink {\n write(path: string, content: string, options?: { encoding?: 'utf8' | 'base64' }): Promise<unknown>\n}\n\n/** Resolve upload authorization status and provide upload destination or error response */\nexport type UploadAuthorization =\n | {\n ok: true\n /** Where large files land. Absent/null: only inline uploads are\n * accepted and an over-inline-cap file is rejected with 413. */\n sink?: SandboxUploadSink | null\n /** Per-request override of the workspace directory large files go to. */\n uploadDir?: string\n }\n | { ok: false; response: Response }\n\n/** Define options to authorize uploads and configure file size limits and upload directory */\nexport interface CreateUploadRouteOptions {\n /** Authenticate the caller and resolve the sandbox file sink (usually\n * `ensureWorkspaceSandbox(...)` → `box.fs`). */\n authorize(args: { request: Request }): Promise<UploadAuthorization>\n /** Inline-vs-sandbox threshold. Default {@link UPLOAD_INLINE_MAX_BYTES}. */\n inlineMaxBytes?: number\n /** Hard per-file cap. Default {@link UPLOAD_MAX_FILE_BYTES}. */\n maxFileBytes?: number\n /** Absolute workspace directory for path-ref files.\n * Default `'/workspace/uploads'`. */\n uploadDir?: string\n}\n\n/** One uploaded file, ready for the composer chip and the turn body. */\nexport interface UploadedChatFile {\n id: string\n name: string\n size: number\n mediaType: string\n /** True when the part carries the bytes inline (`data:` URI). */\n inline: boolean\n /** Echo this back verbatim in `ChatTurnRequestPayload.parts`. */\n part: ChatTurnFilePartInput\n}\n\n/** Path-safe file name: basename only, conservative charset, length-capped. */\nexport function sanitizeUploadFilename(name: string): string {\n const base = name.split(/[\\\\/]/).pop() ?? 'file'\n const safe = base.replace(/[^A-Za-z0-9._-]+/g, '_').replace(/^\\.+/, '_')\n return (safe || 'file').slice(0, 120)\n}\n\nconst BASE64_CHUNK = 0x8000\n\n/** Convert a Uint8Array of bytes into a base64-encoded string */\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = ''\n for (let offset = 0; offset < bytes.length; offset += BASE64_CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + BASE64_CHUNK))\n }\n return btoa(binary)\n}\n\nfunction uploadError(status: number, code: string, error: string): Response {\n return Response.json({ code, error }, { status })\n}\n\n/** Create an upload route handler that authorizes requests and processes file uploads with size limits */\nexport function createUploadRoute(options: CreateUploadRouteOptions): (request: Request) => Promise<Response> {\n const inlineMaxBytes = options.inlineMaxBytes ?? UPLOAD_INLINE_MAX_BYTES\n const maxFileBytes = options.maxFileBytes ?? UPLOAD_MAX_FILE_BYTES\n\n return async function upload(request: Request): Promise<Response> {\n const auth = await options.authorize({ request })\n if (!auth.ok) return auth.response\n const sink = auth.sink ?? null\n const configuredUploadDir = auth.uploadDir ?? options.uploadDir ?? DEFAULT_UPLOAD_DIRECTORY\n\n let form: FormData\n try {\n form = await request.formData()\n } catch {\n return uploadError(400, 'INVALID_UPLOAD', 'Expected a multipart/form-data body with file fields')\n }\n const files: File[] = []\n form.forEach((value) => {\n if (value instanceof File) files.push(value)\n })\n if (files.length === 0) {\n return uploadError(400, 'INVALID_UPLOAD', 'No files in the upload body')\n }\n\n const uploaded: UploadedChatFile[] = []\n for (const file of files) {\n const name = sanitizeUploadFilename(file.name)\n const mediaType = file.type || 'application/octet-stream'\n const partType: ChatTurnFilePartInput['type'] = mediaType.startsWith('image/') ? 'image' : 'file'\n\n if (file.size > maxFileBytes) {\n return uploadError(\n 413,\n 'FILE_TOO_LARGE',\n `${name} is ${file.size}B, over the ${maxFileBytes}B per-file cap`,\n )\n }\n\n const id = crypto.randomUUID()\n if (file.size <= inlineMaxBytes) {\n const base64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()))\n uploaded.push({\n id,\n name,\n size: file.size,\n mediaType,\n inline: true,\n part: {\n type: partType,\n filename: name,\n mediaType,\n url: `data:${mediaType};base64,${base64}`,\n },\n })\n continue\n }\n\n if (!sink) {\n return uploadError(\n 413,\n 'SANDBOX_REQUIRED',\n `${name} is ${file.size}B, over the ${inlineMaxBytes}B inline cap, and no sandbox is available to hold it`,\n )\n }\n const uploadDir = normalizeUploadDirectory(configuredUploadDir)\n if (!uploadDir) {\n return uploadError(\n 500,\n 'INVALID_UPLOAD_DIRECTORY',\n 'uploadDir must be an absolute path without empty, \".\" or \"..\" segments',\n )\n }\n const path = `${uploadDir}/${id}-${name}`\n const base64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()))\n await sink.write(path, base64, { encoding: 'base64' })\n uploaded.push({\n id,\n name,\n size: file.size,\n mediaType,\n inline: false,\n part: { type: partType, filename: name, mediaType, path },\n })\n }\n\n return Response.json({ files: uploaded })\n }\n}\n","/**\n * Storage seams for the chat-attachment vertical (`resolveChatAttachments`,\n * `buildDispatchParts`, `promoteAgentFilePart`). Structural function ports in\n * the same style as `upload.ts`'s `SandboxUploadSink`: REQUIRED injection, no\n * default implementation — agent-app owns the size/budget/idempotency\n * mechanism, the product owns where the bytes actually live.\n *\n * One reader, two callers: `resolveChatAttachments` reads only the\n * authoritative `size` (never trusting the client-reported size), while\n * `buildDispatchParts` reads the inline `content` (base64 or raw bytes) to\n * build a `data:` URI. Both fit behind ONE `ReadAttachmentFn` so a product\n * wires a single vault/object-store adapter, not two. gtm's KV vault (stores\n * base64 bodies) and `/object-store`'s `ObjectStore` (hands back raw bytes)\n * both satisfy this shape.\n */\n\n/**\n * The result of reading one stored attachment. `ok:true` MUST carry the\n * authoritative decoded byte `size` (the cap is meaningless against a\n * client-controlled number) and, when the caller needs to inline the file,\n * its content as `base64` and/or raw `bytes`. `mediaType` is the stored\n * content type when the store knows it (used as the fallback when the wire\n * part carried none). `ok:false` carries a human `reason` that SHOULD name the\n * offending path — it is surfaced verbatim in the caller's typed outcome.\n */\nexport type AttachmentReadResult =\n | {\n ok: true\n /** Authoritative decoded byte length of the stored content. */\n size: number\n /** Inline content as base64 — reused verbatim for a `data:` URI, so a\n * store holding already-base64 bodies never decodes-and-re-encodes. */\n base64?: string\n /** Inline content as raw bytes — base64-encoded by the caller when a\n * `data:` URI is needed. Ignored when `base64` is present. */\n bytes?: Uint8Array\n /** Stored content type, when the store tracks one. */\n mediaType?: string\n }\n | { ok: false; reason: string }\n\n/**\n * Read one stored attachment for `scopeId` (the product's workspace/tenant\n * key) at its store-relative `path`. Missing, deleted, or unreadable content\n * MUST resolve to `{ ok: false, reason }`, never throw — a store failure is a\n * per-attachment outcome the caller folds into its own typed result, not a\n * turn-level exception.\n */\nexport type ReadAttachmentFn = (scopeId: string, path: string) => Promise<AttachmentReadResult>\n\n/** The ownership identity for one attempted write. The upload and promotion\n * routes create a unique path for each attempt, and the product adapter stores\n * this id with it. */\nexport interface AttachmentWriteOwnership {\n readonly id: string\n readonly path: string\n}\n\n/** Public-safe outcome for a storage outage. Backend details belong in logs. */\nexport const ATTACHMENT_STORAGE_FAILURE_MESSAGE = 'Attachment storage is temporarily unavailable. Please try again.'\n\n/** Stable client error code when compensating cleanup did not complete. */\nexport const ATTACHMENT_ROLLBACK_FAILURE_CODE = 'rollback_failed'\n\n/** Public-safe outcome when compensating cleanup did not complete. */\nexport const ATTACHMENT_ROLLBACK_FAILURE_MESSAGE = 'Attachment cleanup failed. Please try again.'\n\n/** Add a path-safe ownership id to a logical path and return an immutable store key. */\nexport function immutableAttachmentPath(logicalPath: string, ownershipId: string): string {\n if (!/^[A-Za-z0-9_-]{1,128}$/.test(ownershipId)) {\n throw new Error('attachment ownership id must be a path-safe identifier')\n }\n return `${logicalPath}--${ownershipId}`\n}\n\n/** Compensation for one attachment write. The adapter MUST compare the stored\n * ownership id before deleting, so an older rollback cannot delete a newer\n * overwrite. */\nexport interface AttachmentWriteReceipt {\n rollback(): void | Promise<void>\n ownership: AttachmentWriteOwnership\n}\n\n/** Clean up a write whose function threw after the store may have committed.\n * The adapter must delete only the supplied ownership, never the logical path\n * unconditionally. */\nexport type AbortAttachmentWriteFn = (\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n) => void | Promise<void>\n\n/**\n * The stable writer result from the original attachment-store contract.\n * Existing products may return this shape and keep using the legacy writer\n * lane. New products should use {@link AtomicAttachmentWriteResult}.\n */\nexport type AttachmentWriteResult = { ok: true } | { ok: false; reason: string }\n\n/** Options from the stable attachment-store contract. */\nexport interface AttachmentWriteOptions {\n mediaType?: string\n name?: string\n originalName?: string\n size?: number\n}\n\n/**\n * The stable writer port. It is intentionally unchanged so products released\n * before the ownership receipt contract continue to compile.\n *\n * The legacy lane does not promise batch rollback. Use\n * {@link createAtomicAttachmentWriter} for ownership-safe writes.\n */\nexport type WriteAttachmentFn = (\n scopeId: string,\n path: string,\n content: Uint8Array | string,\n opts: AttachmentWriteOptions,\n) => Promise<AttachmentWriteResult>\n\n/** Options for a streamed write. `size` is the exact byte count `content`\n * yields. `mediaType` is decided from the file name and its first bytes,\n * before the route has read the rest: an archive's central directory sits at\n * its end, so the route's response, not this value, carries the final type. */\nexport interface AttachmentStreamWriteOptions extends AttachmentWriteOptions {\n size: number\n mediaType: string\n}\n\n/**\n * Streamed writer port for an upload too large to hold in memory. `content`\n * yields exactly `opts.size` bytes. The route checks the file while it\n * streams and withholds the final bytes until the whole file has passed, so\n * on a refusal `content` errors instead of ending. The adapter must make the\n * bytes readable at `path` only when `content` ends cleanly, and must leave\n * whatever was stored at `path` before untouched when it errors. A store put\n * of a fixed-length stream (R2 `put` behind `FixedLengthStream`) does both.\n */\nexport type WriteAttachmentStreamFn = (\n scopeId: string,\n path: string,\n content: ReadableStream<Uint8Array>,\n opts: AttachmentStreamWriteOptions,\n) => Promise<AttachmentWriteResult>\n\n/** Options for an ownership-safe writer. */\nexport interface AtomicAttachmentWriteOptions extends AttachmentWriteOptions {\n ownership: AttachmentWriteOwnership\n}\n\n/** Result for an ownership-safe writer. A receipt is required on both paths. */\nexport type AtomicAttachmentWriteResult =\n | { ok: true; receipt: AttachmentWriteReceipt }\n | { ok: false; reason: string; receipt: AttachmentWriteReceipt }\n\n/** Ownership-safe writer port used by the atomic upload and promotion lanes. */\nexport type AtomicWriteAttachmentFn = (\n scopeId: string,\n path: string,\n content: Uint8Array | string,\n opts: AtomicAttachmentWriteOptions,\n) => Promise<AtomicAttachmentWriteResult>\n\n/** A complete ownership-safe attachment store adapter. */\nexport interface AtomicAttachmentWriter {\n write: AtomicWriteAttachmentFn\n abort: AbortAttachmentWriteFn\n}\n\n/**\n * Build the explicit atomic adapter used by new routes.\n *\n * Keeping the write and abort functions together prevents a caller from\n * enabling ownership paths while forgetting the ambiguous-write cleanup.\n */\nexport function createAtomicAttachmentWriter(input: AtomicAttachmentWriter): AtomicAttachmentWriter {\n return {\n write: input.write.bind(input),\n abort: input.abort.bind(input),\n }\n}\n","/**\n * `resolveChatAttachments` — validate a turn body's `attachments` field into\n * persistable {@link ChatAttachmentPart}s. Every path is re-validated (a path\n * off the wire is never trusted to stay inside the store root) and every size\n * is re-derived from the STORED body via the injected {@link ReadAttachmentFn},\n * never the client-reported `size` — the upload path lets a caller rewrite its\n * own frontmatter, so a stored size cannot bound anything and the wire size can\n * be anything. Both the aggregate cap and the size carried on the returned part\n * come from the authoritative read.\n *\n * Storage-parameterized: the frontmatter parsing / base64 sizing that derives\n * the authoritative size lives BEHIND `readAttachment` (a product's vault or\n * object-store adapter), so this module is a pure validator + budget gate with\n * no store knowledge. Lifted from gtm-agent's `resolve-attachments.ts`\n * (workspaceId → scopeId, the vault read → the injected reader) and kept\n * behavior-identical for gtm-agent#618 adoption.\n */\n\nimport { attachmentInputToPart, type ChatAttachmentPart } from '../chat-store/parts'\nimport type { ReadAttachmentFn } from './attachment-store'\nimport {\n MAX_ATTACHMENT_TOTAL_BYTES,\n attachmentTotalSizeErrorMessage,\n parseChatAttachmentInputs,\n type AttachmentPathCheck,\n} from './attachment-validation'\n\n/** Resolve the result of chat attachment processing with success status and corresponding data or error */\nexport type ResolveChatAttachmentsResult =\n | { succeeded: true; value: ChatAttachmentPart[] }\n | { succeeded: false; error: string }\n\n/** Define options to resolve and validate chat attachments with size, count, and path constraints */\nexport interface ResolveChatAttachmentsOptions {\n /** The product's workspace/tenant key, passed to `readAttachment`. */\n scopeId: string\n /** Authoritative size + content reader — see {@link ReadAttachmentFn}. */\n readAttachment: ReadAttachmentFn\n /** Most attachments one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */\n maxCount?: number\n /** Aggregate raw-byte ceiling. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */\n maxTotalBytes?: number\n /** Path validator override. Default {@link defaultValidateAttachmentPath}. */\n validatePath?: (path: string) => AttachmentPathCheck\n}\n\n/**\n * Validate and resolve a turn body's `attachments` field into persistable\n * parts. Every path is confirmed present (and not deleted) in the caller's own\n * store by `readAttachment` before it is trusted, and size is derived from the\n * authoritative read for both the aggregate cap and the returned part's size.\n */\nexport async function resolveChatAttachments(\n value: unknown,\n options: ResolveChatAttachmentsOptions,\n): Promise<ResolveChatAttachmentsResult> {\n const maxTotalBytes = options.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const parsed = parseChatAttachmentInputs(value, options)\n if (!parsed.succeeded) return parsed\n const inputs = parsed.value\n\n // Advisory only: client-controlled, so a dishonest caller can slip past it —\n // but an honest oversized request should fail before it costs a single store\n // read. The authoritative check runs per-attachment below, against the\n // body-derived size.\n const advisoryTotal = inputs.reduce((sum, input) => sum + input.size, 0)\n if (advisoryTotal > maxTotalBytes) {\n return { succeeded: false, error: attachmentTotalSizeErrorMessage(advisoryTotal, maxTotalBytes) }\n }\n\n // One attachment is read, sized, and dropped at a time rather than via\n // Promise.all — that would materialize every attachment's body in memory\n // simultaneously (ten truthful 10 MiB references is ~134 MiB of base64, over\n // a Workers heap) before the cap ever gets a chance to reject the request.\n // Sequential reads bound resident bytes to one attachment at a time and bail\n // the moment the running total exceeds the cap, at the cost of up to\n // `maxCount` serial reads instead of one parallel batch.\n let totalStoredBytes = 0\n for (const input of inputs) {\n const read = await options.readAttachment(options.scopeId, input.path)\n if (!read.ok) return { succeeded: false, error: read.reason }\n totalStoredBytes += read.size\n if (totalStoredBytes > maxTotalBytes) {\n return { succeeded: false, error: attachmentTotalSizeErrorMessage(totalStoredBytes, maxTotalBytes) }\n }\n input.size = read.size\n }\n\n return { succeeded: true, value: inputs.map(attachmentInputToPart) }\n}\n","import type {\n AtomicAttachmentWriteResult,\n AttachmentWriteOwnership,\n AttachmentWriteReceipt,\n AttachmentWriteResult,\n} from './attachment-store'\n\ntype ObjectLike = Record<PropertyKey, unknown>\n\nfunction objectLike(value: unknown): value is ObjectLike {\n return value !== null && (typeof value === 'object' || typeof value === 'function')\n}\n\n/** Read an injected writer result without allowing hostile getters to escape. */\nexport function inspectLegacyAttachmentWriteResult(\n value: unknown,\n): AttachmentWriteResult | undefined {\n try {\n if (!objectLike(value)) return undefined\n const ok = value.ok\n if (ok === true) return { ok: true }\n if (ok === false && typeof value.reason === 'string') {\n return { ok: false, reason: value.reason }\n }\n } catch {\n // A malformed or hostile adapter result is a storage failure.\n }\n return undefined\n}\n\n/**\n * Read an ownership-aware writer result with strict runtime checks.\n *\n * The public types protect TypeScript callers only. The product adapter is an\n * injection boundary, so a proxy, malformed value, or truthy non-boolean must\n * fail closed before the route invokes cleanup or publishes a path.\n */\nexport function inspectAtomicAttachmentWriteResult(\n value: unknown,\n ownership: AttachmentWriteOwnership,\n): AtomicAttachmentWriteResult | undefined {\n try {\n if (!objectLike(value)) return undefined\n const receiptValue = value.receipt\n if (!objectLike(receiptValue)) return undefined\n const receiptOwnership = receiptValue.ownership\n if (!objectLike(receiptOwnership)) return undefined\n if (\n receiptOwnership.id !== ownership.id ||\n receiptOwnership.path !== ownership.path ||\n typeof receiptOwnership.id !== 'string' ||\n typeof receiptOwnership.path !== 'string' ||\n typeof receiptValue.rollback !== 'function'\n ) {\n return undefined\n }\n if (value.ok === true) {\n return { ok: true, receipt: receiptValue as unknown as AttachmentWriteReceipt }\n }\n if (value.ok === false && typeof value.reason === 'string') {\n return {\n ok: false,\n reason: value.reason,\n receipt: receiptValue as unknown as AttachmentWriteReceipt,\n }\n }\n } catch {\n // A malformed or hostile adapter result is a storage failure.\n }\n return undefined\n}\n","/**\n * `promoteAgentFilePart` — turn a harness-emitted `type:\"file\"` stream part\n * into a store-backed {@link ChatAttachmentPart}. The harness hands back a URL\n * pointing at bytes it produced (a `data:` URI, or a path inside the sandbox);\n * nothing durable survives past the turn unless it is written into the\n * product's store, the same way a user upload is. Typed outcomes throughout:\n * every failure mode (unsupported scheme, no sandbox, oversize, store-write\n * failure, malformed part) resolves to `{ succeeded: false, filename, reason }`\n * rather than throwing past this boundary, so the caller folds a visible notice\n * instead of losing the file silently.\n *\n * Storage-parameterized port of gtm-agent's `promote-file-parts.ts` with the\n * refactor gtm never made: persistence goes through an injected stable writer\n * or ownership-safe adapter, the path strategy is the injected\n * `buildAttachmentPath` (neutral `uploads/agent/<date>/` default, no domain\n * bucket taxonomy baked), the MIME map is an injectable hook, and the date\n * segment reads an injectable clock. The logical `hash8(id ?? url ?? filename)`\n * naming is preserved. Atomic callers also receive a fresh ownership suffix so\n * an ambiguous write cannot overwrite an older object.\n */\n\nimport {\n statSandboxFileSize,\n readSandboxBinaryBytes,\n type SandboxExecChannel,\n} from '../sandbox/binary-read'\nimport { attachmentKindForMime, type ChatAttachmentKind, type ChatAttachmentPart } from '../chat-store/parts'\nimport { redactErrorMessage } from '../redact'\nimport {\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n immutableAttachmentPath,\n type AtomicAttachmentWriter,\n type AttachmentWriteOwnership,\n type AttachmentWriteReceipt,\n type WriteAttachmentFn,\n} from './attachment-store'\nimport {\n inspectAtomicAttachmentWriteResult,\n inspectLegacyAttachmentWriteResult,\n} from './attachment-write-safety'\nimport { sanitizeAttachmentFileName } from './attachment-validation'\nimport { formatBytes } from './wire'\n\n/** Default ceiling on a promoted file's raw (pre-encoding) byte size. */\nexport const PROMOTE_MAX_FILE_BYTES = 10 * 1024 * 1024\n\n/** Define the structure for a raw file part with optional metadata and media type information */\nexport interface RawAgentFilePart {\n type: 'file'\n id?: string\n filename?: string\n /** AI-SDK-shaped parts carry the MIME type here… */\n mediaType?: string\n /** …but OpenCode's native FilePart calls the same field `mime`. */\n mime?: string\n url?: string\n}\n\n/** Resolve the result of promoting a file part with success status and relevant data or error details */\nexport type PromoteFilePartResult =\n | { succeeded: true; part: ChatAttachmentPart }\n | { succeeded: false; filename: string; reason: string }\n\ntype ByteResolution =\n | { succeeded: true; bytes: Uint8Array }\n | { succeeded: false; reason: string }\n\n/** Arguments handed to a {@link PromoteAgentFilePartOptions.buildAttachmentPath}\n * override — everything needed to place the file deterministically. */\nexport interface AttachmentPathArgs {\n /** Sanitized display filename (basename, safe charset). */\n filename: string\n /** First 8 hex chars of the SHA-256 idempotency digest. */\n hash8: string\n /** `YYYY-MM-DD` from the injected clock. */\n date: string\n /** Resolved media type. */\n mediaType: string\n /** `image`/`file` split of the media type. */\n kind: ChatAttachmentKind\n}\n\n/** Minimal extension→mime map — the last-resort media type when the part\n * carries none. Generic file typing, NOT a product accept-list (which is a\n * domain value the product supplies): an unknown extension falls to\n * `text/plain`, it never rejects. */\nconst EXT_TO_MIME: Record<string, string> = {\n md: 'text/markdown',\n markdown: 'text/markdown',\n txt: 'text/plain',\n log: 'text/plain',\n csv: 'text/csv',\n tsv: 'text/tab-separated-values',\n json: 'application/json',\n yaml: 'text/yaml',\n yml: 'text/yaml',\n xml: 'application/xml',\n html: 'text/html',\n htm: 'text/html',\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n heic: 'image/heic',\n heif: 'image/heif',\n svg: 'image/svg+xml',\n mp4: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n aac: 'audio/aac',\n}\n\n/** Default MIME hook: extension → mime, or `text/plain` for the unknown. */\nexport function sniffMimeFromName(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase()\n if (!ext) return 'text/plain'\n return EXT_TO_MIME[ext] ?? 'text/plain'\n}\n\n/** Decode base64 with `atob` (not `Buffer.from`, which SKIPS out-of-alphabet\n * characters and would decode a corrupt payload to something plausible). */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)\n return bytes\n}\n\nfunction parseDataUrl(url: string): { base64: boolean; data: string } | null {\n const match = /^data:[^,]*,([\\s\\S]*)$/i.exec(url)\n if (!match) return null\n return { base64: /;base64,/i.test(url), data: match[1] ?? '' }\n}\n\n/** The MIME type embedded in a `data:` URI's header, if any — the last-resort\n * signal when the part itself carries no mediaType/mime field. */\nfunction dataUrlMime(url: string | undefined): string | undefined {\n if (!url) return undefined\n const match = /^data:([^;,]+)[;,]/i.exec(url)\n return match ? match[1] : undefined\n}\n\nfunction basenameFromUrl(url: string | undefined): string | undefined {\n if (!url || /^data:/i.test(url)) return undefined\n const withoutQuery = url.split(/[?#]/)[0] ?? url\n const segments = withoutQuery.split('/').filter(Boolean)\n return segments[segments.length - 1] || undefined\n}\n\n/** `file://<path>` strips to `<path>`; a bare absolute path passes through\n * unchanged. The remainder is percent-decoded — sidecar file URLs encode\n * spaces and other reserved characters. */\nfunction resolveFileUrlPath(url: string): { succeeded: true; path: string } | { succeeded: false; reason: string } {\n const withoutScheme = /^file:\\/\\//i.test(url) ? url.slice('file://'.length) : url\n try {\n return { succeeded: true, path: decodeURIComponent(withoutScheme) }\n } catch (err) {\n return { succeeded: false, reason: `malformed file path: ${redactErrorMessage(err)}` }\n }\n}\n\n/** Matches gtm's `attachmentSizeErrorMessage` (attachment-limits.ts:87-89)\n * verbatim, via the shared {@link formatBytes} — e.g. \"report.pdf is 10MB;\n * attachments are limited to 10MB\" — so an oversize promotion notice reads\n * identically whether gtm's original composed it or agent-app's promoter did. */\nfunction oversizeReason(filename: string, actual: number, limit: number): string {\n return `${filename} is ${formatBytes(actual)}; attachments are limited to ${formatBytes(limit)}`\n}\n\nfunction resolveDataUrlBytes(url: string, filename: string, maxBytes: number): ByteResolution {\n const parsed = parseDataUrl(url)\n if (!parsed) return { succeeded: false, reason: 'malformed data URI' }\n let bytes: Uint8Array\n try {\n bytes = parsed.base64 ? base64ToBytes(parsed.data) : new TextEncoder().encode(decodeURIComponent(parsed.data))\n } catch (err) {\n return { succeeded: false, reason: `failed to decode data URI: ${redactErrorMessage(err)}` }\n }\n if (bytes.byteLength > maxBytes) {\n return { succeeded: false, reason: oversizeReason(filename, bytes.byteLength, maxBytes) }\n }\n return { succeeded: true, bytes }\n}\n\nasync function resolveSandboxFileBytes(input: {\n path: string\n box: SandboxExecChannel\n sessionId: string\n filename: string\n maxBytes: number\n}): Promise<ByteResolution> {\n // exec can reject outright (box teardown, timeout, transport failure) — that\n // is a per-file failure, not a turn failure, so it must resolve to a typed\n // outcome like a nonzero exit code does.\n const stat = await statSandboxFileSize(input.box, input.path, { sessionId: input.sessionId })\n if (!stat.succeeded) {\n return { succeeded: false, reason: `could not stat agent file: ${redactErrorMessage(stat.error)}` }\n }\n // Rejected before the bytes are ever pulled — a base64 exec of an oversize\n // file would waste a full sandbox round trip only to be discarded.\n if (stat.value > input.maxBytes) {\n return { succeeded: false, reason: oversizeReason(input.filename, stat.value, input.maxBytes) }\n }\n\n const read = await readSandboxBinaryBytes(input.box, input.path, stat.value, { sessionId: input.sessionId })\n if (!read.succeeded) {\n return { succeeded: false, reason: `could not read agent file: ${redactErrorMessage(read.error)}` }\n }\n return { succeeded: true, bytes: read.value.bytes }\n}\n\nasync function resolveBytes(input: {\n raw: RawAgentFilePart\n box: SandboxExecChannel | undefined\n sessionId: string\n filename: string\n maxBytes: number\n}): Promise<ByteResolution> {\n const url = input.raw.url\n if (!url) return { succeeded: false, reason: 'the file part carries no url' }\n\n if (/^data:/i.test(url)) return resolveDataUrlBytes(url, input.filename, input.maxBytes)\n\n const isSandboxPath = /^file:\\/\\//i.test(url) || url.startsWith('/')\n if (!isSandboxPath) {\n const scheme = /^([A-Za-z][A-Za-z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase() ?? 'unknown'\n return { succeeded: false, reason: `unsupported file URL scheme: ${scheme}` }\n }\n if (!input.box) return { succeeded: false, reason: 'no sandbox to read agent file' }\n\n const resolvedPath = resolveFileUrlPath(url)\n if (!resolvedPath.succeeded) return resolvedPath\n return resolveSandboxFileBytes({\n path: resolvedPath.path,\n box: input.box,\n sessionId: input.sessionId,\n filename: input.filename,\n maxBytes: input.maxBytes,\n })\n}\n\n/** First 8 hex chars of the SHA-256 of `seed` — deterministic (no\n * `Math.random`) so promoting the same source part twice, even across\n * requests, resolves to the same store path and overwrites in place. */\nasync function hash8(seed: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(seed))\n return Array.from(new Uint8Array(digest).slice(0, 4))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Neutral default placement: everything under `uploads/agent/<date>/`, named\n * `<base>-<hash8><ext>`. No domain bucket taxonomy (assets/audio/videos…) —\n * a product that wants one supplies `buildAttachmentPath`. */\nfunction defaultBuildAttachmentPath(args: AttachmentPathArgs): string {\n const extensionMatch = /\\.[A-Za-z0-9]+$/.exec(args.filename)\n const extension = extensionMatch ? extensionMatch[0] : ''\n const base = extension ? args.filename.slice(0, -extension.length) : args.filename\n return `uploads/agent/${args.date}/${base}-${args.hash8}${extension}`\n}\n\ntype PromoteFilePartLogger = Pick<Console, 'error'>\n\nfunction logPromotionStorageError(\n logger: PromoteFilePartLogger,\n message: string,\n fields: Record<string, unknown>,\n): void {\n try {\n const safeFields = Object.fromEntries(\n Object.entries(fields).map(([key, value]) => [\n key,\n typeof value === 'string' ? redactErrorMessage(value) : value,\n ]),\n )\n logger.error(message, safeFields)\n } catch {\n // Logging must not turn a typed storage failure into a thrown response.\n }\n}\n\nasync function compensatePromotionWrite(\n writer: AtomicAttachmentWriter,\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n receipt: AttachmentWriteReceipt,\n): Promise<{ rollbackError?: unknown; abortError?: unknown }> {\n try {\n await receipt.rollback()\n return {}\n } catch (rollbackError) {\n try {\n await writer.abort(scopeId, ownership)\n return { rollbackError }\n } catch (abortError) {\n return { rollbackError, abortError }\n }\n }\n}\n\ninterface PromoteFilePartCommonOptions {\n raw: RawAgentFilePart\n /** The turn's box — required only to promote a sandbox-path part; a `data:`\n * URI needs none. */\n box?: SandboxExecChannel\n /** The product's workspace/tenant key, passed to `writeAttachment`. */\n scopeId: string\n /** The turn's session id, used for the sandbox stat/read exec calls. */\n sessionId: string\n /** Store-path strategy. Default {@link defaultBuildAttachmentPath}. */\n buildAttachmentPath?: (args: AttachmentPathArgs) => string\n /** Raw-byte ceiling. Default {@link PROMOTE_MAX_FILE_BYTES}. */\n maxBytes?: number\n /** Last-resort media-type hook. Default {@link sniffMimeFromName}. */\n sniffMime?: (filename: string) => string\n /** Clock for the date path segment. Default `() => new Date()`. */\n now?: () => Date\n /** Unique id source for tests or a product's id service. */\n createWriteId?: () => string\n /** Server-side sink for redacted storage details. */\n logger?: PromoteFilePartLogger\n}\n\n/** Stable promotion options from the original writer contract. */\nexport interface PromoteAgentFilePartOptions extends PromoteFilePartCommonOptions {\n /** REQUIRED store writer — no default (the product owns its store). */\n writeAttachment: WriteAttachmentFn\n}\n\n/** Ownership-safe promotion options for new products. */\nexport interface AtomicPromoteAgentFilePartOptions extends PromoteFilePartCommonOptions {\n /** Complete writer + ambiguous-write cleanup adapter. */\n attachmentWriter: AtomicAttachmentWriter\n}\n\nfunction isAtomicPromotionOptions(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): options is AtomicPromoteAgentFilePartOptions {\n return 'attachmentWriter' in options\n}\n\n/** Promote a part using the stable writer contract. */\nexport function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>\n\n/** Promote a part using an ownership-safe writer and cleanup adapter. */\nexport function promoteAgentFilePart(options: AtomicPromoteAgentFilePartOptions): Promise<PromoteFilePartResult>\n\n/** Promote a part when the caller selects the writer contract at runtime. */\nexport function promoteAgentFilePart(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): Promise<PromoteFilePartResult>\n\n/** Promote a part of an agent file with optional byte limits and MIME type detection. */\nexport async function promoteAgentFilePart(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): Promise<PromoteFilePartResult> {\n const atomic = isAtomicPromotionOptions(options)\n const maxBytes = options.maxBytes ?? PROMOTE_MAX_FILE_BYTES\n const sniffMime = options.sniffMime ?? sniffMimeFromName\n const buildAttachmentPath = options.buildAttachmentPath ?? defaultBuildAttachmentPath\n const now = options.now ?? (() => new Date())\n const logger = options.logger ?? console\n const createWriteId = atomic\n ? options.createWriteId ?? (() => {\n if (typeof crypto === 'undefined' || typeof crypto.randomUUID !== 'function') {\n throw new Error('attachment promotion requires crypto.randomUUID')\n }\n return crypto.randomUUID()\n })\n : undefined\n\n const filename = sanitizeAttachmentFileName(\n options.raw.filename ?? basenameFromUrl(options.raw.url) ?? 'agent-file',\n )\n\n const resolved = await resolveBytes({\n raw: options.raw,\n box: options.box,\n sessionId: options.sessionId,\n filename,\n maxBytes,\n })\n if (!resolved.succeeded) return { succeeded: false, filename, reason: resolved.reason }\n\n let mediaType: string\n let kind: ChatAttachmentKind\n let logicalPath: string\n try {\n mediaType = (options.raw.mediaType ?? options.raw.mime ?? dataUrlMime(options.raw.url) ?? sniffMime(filename)).toLowerCase()\n kind = attachmentKindForMime(mediaType)\n const digest = await hash8(options.raw.id ?? options.raw.url ?? filename)\n const date = now().toISOString().split('T')[0] ?? ''\n logicalPath = buildAttachmentPath({ filename, hash8: digest, date, mediaType, kind })\n } catch (error) {\n logPromotionStorageError(logger, '[promote-file-part] path planning failed', {\n error: redactErrorMessage(error),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n\n if (!atomic) {\n let written: unknown\n try {\n written = await options.writeAttachment(options.scopeId, logicalPath, resolved.bytes, {\n mediaType,\n name: filename,\n originalName: options.raw.filename ?? filename,\n size: resolved.bytes.byteLength,\n })\n } catch (error) {\n const reason = redactErrorMessage(error)\n logPromotionStorageError(logger, '[promote-file-part] legacy writer failed', {\n path: logicalPath,\n error: reason,\n })\n return { succeeded: false, filename, reason }\n }\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result) {\n logPromotionStorageError(logger, '[promote-file-part] legacy writer returned an invalid result', {\n path: logicalPath,\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logPromotionStorageError(logger, '[promote-file-part] legacy write rejected', {\n path: logicalPath,\n error: reason,\n })\n return { succeeded: false, filename, reason }\n }\n\n return {\n succeeded: true,\n part: {\n type: kind,\n path: logicalPath,\n name: filename,\n size: resolved.bytes.byteLength,\n mediaType,\n },\n }\n }\n\n let ownershipId: string\n let path: string\n try {\n ownershipId = createWriteId!()\n path = immutableAttachmentPath(logicalPath, ownershipId)\n } catch (error) {\n logPromotionStorageError(logger, '[promote-file-part] could not allocate ownership key', {\n path: logicalPath,\n error: redactErrorMessage(error),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n const ownership: AttachmentWriteOwnership = Object.freeze({ id: ownershipId, path })\n\n // `name` is the sanitized filename already computed above; `originalName`\n // is the pre-sanitization source name (gtm's frontmatter `originalName`) —\n // the one field sanitization would otherwise destroy with no way back.\n let written: Awaited<ReturnType<AtomicAttachmentWriter['write']>>\n try {\n written = await options.attachmentWriter.write(options.scopeId, path, resolved.bytes, {\n mediaType,\n name: filename,\n originalName: options.raw.filename ?? filename,\n size: resolved.bytes.byteLength,\n ownership,\n })\n } catch (err) {\n let abortError: unknown\n try {\n await options.attachmentWriter.abort(options.scopeId, ownership)\n } catch (error) {\n abortError = error\n }\n logPromotionStorageError(logger, '[promote-file-part] write failed', {\n path,\n ownershipId: ownership.id,\n error: redactErrorMessage(err),\n ...(abortError === undefined ? {} : { abortError: redactErrorMessage(abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n const result = inspectAtomicAttachmentWriteResult(written, ownership)\n if (!result) {\n let abortError: unknown\n try {\n await options.attachmentWriter.abort(options.scopeId, ownership)\n } catch (error) {\n abortError = error\n }\n logPromotionStorageError(logger, '[promote-file-part] writer returned a mismatched ownership receipt', {\n path,\n ownershipId: ownership.id,\n ...(abortError === undefined ? {} : { abortError: redactErrorMessage(abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n if (!result.ok) {\n const compensation = await compensatePromotionWrite(\n options.attachmentWriter,\n options.scopeId,\n ownership,\n result.receipt,\n )\n logPromotionStorageError(logger, '[promote-file-part] write rejected', {\n path,\n ownershipId: ownership.id,\n error: redactErrorMessage(result.reason),\n ...(compensation.rollbackError === undefined\n ? {}\n : { rollbackError: redactErrorMessage(compensation.rollbackError) }),\n ...(compensation.abortError === undefined\n ? {}\n : { abortError: redactErrorMessage(compensation.abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n\n return {\n succeeded: true,\n part: {\n type: kind,\n path,\n name: filename,\n size: resolved.bytes.byteLength,\n mediaType,\n },\n }\n}\n","/**\n * `createAttachmentUploadRoute` — the fleet-primitive durable-store upload\n * route: a two-phase batch (every file is validated before any file is written),\n * with a stable legacy lane and an ownership-safe receipt lane for new callers,\n * a content-sniffed type gate\n * (`checkAttachmentType` over `sniffBinary`'s magic-byte read, not the\n * extension or the browser-reported MIME), binary/text caps with optional\n * per-sniffed-mime overrides, an aggregate byte cap, and sanitized filenames.\n * Storage is fully seamed through the injected\n * `WriteAttachmentFn` or `AtomicAttachmentWriter` (`./attachment-store`) — no\n * default store, the product owns where bytes actually live (vault, object\n * store, …), and auth/rate limiting is entirely the injected `authorize`\n * seam's job: this factory\n * never invents a 401 or 429 response, it only returns `auth.response`\n * verbatim on failure.\n *\n * Two body shapes: a `multipart/form-data` batch, parsed whole, and one raw\n * file body named by the `filename` query parameter. A raw body larger than\n * the sniff head streams to `writeAttachmentStream` when the product supplies\n * one: the Worker never holds the file whole, which is what lets a product\n * admit data files of tens of MB inside a 128 MB isolate.\n *\n * Lifted from gtm-agent's `src/routes/api.vault.upload.ts` (the hardening\n * lineage other lifted modules in this vertical cite: gtm#584 binary\n * corruption, gtm#592 sniff gate/caps, gtm#593 batch-atomic writes) and\n * generalized the way `resolve-attachments.ts` generalized gtm's read path —\n * the vault-specific pieces (KV vault paths, frontmatter, per-user rate\n * limiting) are all injected seams here, while the validate-then-write phase\n * split and the type/size gate ordering survive byte-for-byte.\n *\n * @remarks Sibling to, NOT an extension of, `./upload.ts`'s\n * `createUploadRoute` — a different persistence model (durable product store\n * vs. inline-`data:`-or-ephemeral-sandbox-workspace). See that module's doc\n * comment for the up-to-date framing between the two.\n */\n\nimport { formatBytes, type ChatAttachmentInput, type ChatAttachmentKind } from './wire'\nimport { attachmentKindForMime } from '../chat-store/parts'\nimport { redactErrorMessage } from '../redact'\nimport {\n ATTACHMENT_ROLLBACK_FAILURE_CODE,\n ATTACHMENT_ROLLBACK_FAILURE_MESSAGE,\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n type AtomicAttachmentWriter,\n immutableAttachmentPath,\n type AttachmentWriteOwnership,\n type AttachmentWriteReceipt,\n type WriteAttachmentFn,\n type WriteAttachmentStreamFn,\n} from './attachment-store'\nimport {\n inspectAtomicAttachmentWriteResult,\n inspectLegacyAttachmentWriteResult,\n} from './attachment-write-safety'\nimport {\n ALLOWED_ATTACHMENT_SNIFFED_MIMES,\n ATTACHMENT_MAX_COUNT,\n MAX_ATTACHMENT_TOTAL_BYTES,\n MAX_BINARY_ATTACHMENT_BYTES,\n MAX_TEXT_ATTACHMENT_BYTES,\n attachmentSizeErrorMessage,\n attachmentTotalSizeErrorMessage,\n checkAttachmentType,\n defaultValidateAttachmentPath,\n sanitizeAttachmentFileName,\n sniffedMimeImpliedByName,\n type AttachmentPathCheck,\n} from './attachment-validation'\nimport { createStreamingSniffer, sniffBinary, STREAM_SNIFF_HEAD_BYTES, type SniffResult } from './binary-sniff'\nimport { sniffMimeFromName } from './promote-file-part'\n\n/** Stable authorization result for the original writer contract. */\nexport type AttachmentUploadAuthorization =\n | {\n ok: true\n scopeId: string\n writeAttachment?: WriteAttachmentFn\n writeAttachmentStream?: WriteAttachmentStreamFn\n }\n | { ok: false; response: Response }\n\n/** Authorization result for an ownership-safe attachment writer. */\nexport type AtomicAttachmentUploadAuthorization =\n | {\n ok: true\n scopeId: string\n attachmentWriter?: AtomicAttachmentWriter\n }\n | { ok: false; response: Response }\n\n/** The logger receives only sanitized backend details. The client receives the\n * opaque message below, regardless of the store's failure text. */\nexport type AttachmentUploadLogger = Pick<Console, 'error'>\n\nfunction logAttachmentUploadError(\n logger: AttachmentUploadLogger,\n message: string,\n fields: Record<string, unknown>,\n): void {\n try {\n const safeFields = Object.fromEntries(\n Object.entries(fields).map(([key, value]) => [\n key,\n typeof value === 'string' ? redactErrorMessage(value) : value,\n ]),\n )\n logger.error(message, safeFields)\n } catch {\n // Logging must not turn a typed storage failure into a thrown response.\n }\n}\n\ninterface AttachmentUploadRouteCommonOptions {\n /** Overridable caps. Defaults come from `./attachment-validation`. */\n limits?: {\n /** Most files one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */\n maxCount?: number\n /** Ceiling on a binary file's raw size. Default {@link MAX_BINARY_ATTACHMENT_BYTES}. */\n maxBinaryBytes?: number\n /** Optional binary-file ceilings keyed by the content-sniffed mime. */\n maxBytesBySniffedMime?: ReadonlyMap<string, number>\n /** Ceiling on a text file's raw size. Default {@link MAX_TEXT_ATTACHMENT_BYTES}. */\n maxTextBytes?: number\n /** Aggregate raw-byte ceiling. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */\n maxTotalBytes?: number\n /** Largest `multipart/form-data` body this route parses. A form body is\n * held whole while it parses, at about three copies at peak, so a\n * product that admits files too large for that sets this and takes them\n * as raw bodies (see {@link createAttachmentUploadRoute}). Default: no\n * limit beyond the per-file and aggregate caps. */\n maxFormBytes?: number\n }\n /** Attachment kinds this route accepts. Default `['image', 'file']`. */\n allowedKinds?: ChatAttachmentKind[]\n /** Sniffed-mime allowlist fed to `checkAttachmentType`. */\n allowedSniffedMimes?: ReadonlySet<string>\n /** Sanitized-name → logical store path. */\n pathFor?: (name: string) => string\n /** Store-path validator. Default {@link defaultValidateAttachmentPath}. */\n validatePath?: (path: string) => AttachmentPathCheck\n /** Last-resort media-type hook for text content the sniffer cannot type. */\n sniffMime?: (name: string) => string\n /** Unique id source for atomic ownership keys. */\n createWriteId?: () => string\n /** Server-side error sink. Backend text is sanitized before it is logged. */\n logger?: AttachmentUploadLogger\n}\n\n/** Stable route options for products using the original writer contract. */\nexport interface CreateLegacyAttachmentUploadRouteOptions extends AttachmentUploadRouteCommonOptions {\n /** Authenticate the caller, rate-limit, and resolve the store scope\n * (workspace/tenant id) — never a query param. */\n authorize(args: { request: Request }): Promise<AttachmentUploadAuthorization>\n /** Stable writer. `authorize` may override it per-request. */\n writeAttachment: WriteAttachmentFn\n /** Writer for a raw-body upload larger than {@link STREAM_SNIFF_HEAD_BYTES}:\n * the route checks the file while it streams to this writer, so the file\n * is never held whole. Without it, a raw body is read whole and goes\n * through `writeAttachment` under the same caps. */\n writeAttachmentStream?: WriteAttachmentStreamFn\n}\n\n/**\n * The original public route-options interface remains available for consumers\n * that extend it. Ownership-safe callers use the atomic interface below.\n */\nexport interface CreateAttachmentUploadRouteOptions extends CreateLegacyAttachmentUploadRouteOptions {}\n\n/** Ownership-safe route options for new products. */\nexport interface CreateAtomicAttachmentUploadRouteOptions extends AttachmentUploadRouteCommonOptions {\n /** Authenticate the caller, rate-limit, and resolve the store scope. */\n authorize(args: { request: Request }): Promise<AtomicAttachmentUploadAuthorization>\n /** Complete writer + ambiguous-write cleanup adapter. */\n attachmentWriter: AtomicAttachmentWriter\n}\n\nfunction attachmentUploadError(status: number, code: string, message: string, path?: string): Response {\n return Response.json(\n { error: path === undefined ? { code, message } : { code, message, path } },\n { status },\n )\n}\n\nfunction atomicAttachmentFailure(path: string, cleanupFailures = 0): Response {\n return attachmentUploadError(\n 503,\n cleanupFailures > 0 ? ATTACHMENT_ROLLBACK_FAILURE_CODE : 'attachment_store_unavailable',\n cleanupFailures > 0 ? ATTACHMENT_ROLLBACK_FAILURE_MESSAGE : ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n path,\n )\n}\n\n/** Longest raw-upload `filename` accepted. */\nconst MAX_RAW_FILENAME_LENGTH = 255\n\ntype RawUploadTarget =\n | { succeeded: true; filename: string; declaredBytes: number }\n | { succeeded: false; response: Response }\n\n/**\n * A raw upload is one file as the request body, named by the `filename`\n * query parameter, with its size in `Content-Length` or `X-Upload-Length`\n * (the browser's `File.size`), as in `/object-store`'s upload route. Knowing\n * the size up front is what lets a store receive a fixed-length stream.\n */\nfunction rawUploadTarget(request: Request): RawUploadTarget {\n const filename = new URL(request.url).searchParams.get('filename')\n if (!filename || filename.length > MAX_RAW_FILENAME_LENGTH) {\n return {\n succeeded: false,\n response: attachmentUploadError(\n 400,\n 'invalid_upload',\n 'Expected a multipart/form-data body with file fields, or one raw file body named by the filename query parameter',\n ),\n }\n }\n const uploadLength = request.headers.get('X-Upload-Length')\n const transportLength = request.headers.get('Content-Length')\n if (uploadLength !== null && transportLength !== null && uploadLength !== transportLength) {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_length', 'Conflicting upload lengths') }\n }\n const declared = uploadLength ?? transportLength\n if (declared === null) {\n return {\n succeeded: false,\n response: attachmentUploadError(411, 'length_required', 'A raw upload declares its size in Content-Length or X-Upload-Length'),\n }\n }\n const declaredBytes = Number(declared)\n if (!/^(0|[1-9]\\d*)$/.test(declared) || !Number.isSafeInteger(declaredBytes)) {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_length', 'Invalid upload length') }\n }\n return { succeeded: true, filename, declaredBytes }\n}\n\nconst LENGTH_MISMATCH_MESSAGE = 'Upload length does not match the file body'\n\n/** Read a small raw body whole, refusing one longer or shorter than declared. */\nasync function readRawBody(\n request: Request,\n declaredBytes: number,\n): Promise<{ succeeded: true; value: Uint8Array<ArrayBuffer> } | { succeeded: false; response: Response }> {\n const mismatch = { succeeded: false as const, response: attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE) }\n const bytes = new Uint8Array(declaredBytes)\n if (!request.body) return declaredBytes === 0 ? { succeeded: true, value: bytes } : mismatch\n const reader = request.body.getReader()\n let offset = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (offset + value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n return mismatch\n }\n bytes.set(value, offset)\n offset += value.length\n }\n } catch {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_upload', 'The upload body ended early') }\n }\n return offset === declaredBytes ? { succeeded: true, value: bytes } : mismatch\n}\n\n/** Read a body of at most `maxBytes`, refusing a longer one as soon as it\n * passes the cap. A declared `Content-Length` over the cap is refused before\n * any byte is read. */\nasync function readBodyUpTo(\n request: Request,\n maxBytes: number,\n): Promise<{ succeeded: true; value: Uint8Array<ArrayBuffer> } | { succeeded: false; tooLarge: boolean }> {\n const declared = Number(request.headers.get('Content-Length') ?? Number.NaN)\n if (Number.isFinite(declared) && declared > maxBytes) return { succeeded: false, tooLarge: true }\n if (!request.body) return { succeeded: true, value: new Uint8Array(0) }\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let length = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n length += value.length\n if (length > maxBytes) {\n await reader.cancel().catch(() => undefined)\n return { succeeded: false, tooLarge: true }\n }\n chunks.push(value)\n }\n } catch {\n return { succeeded: false, tooLarge: false }\n }\n const bytes = new Uint8Array(length)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.length\n }\n return { succeeded: true, value: bytes }\n}\n\n/** The media type a streamed write records before the whole file is read.\n * The head decides every family except a zip container, whose Office\n * subtype lives in the central directory at the end; for that, the\n * extension's implied type stands in, and the route's response carries the\n * final one. */\nfunction provisionalMediaType(name: string, head: SniffResult, sniffMime: (name: string) => string): string {\n if (!head.binary) return sniffMime(name)\n if (head.mime === 'application/zip') return sniffedMimeImpliedByName(name) ?? head.mime\n return head.mime ?? 'application/octet-stream'\n}\n\ninterface SuccessfulAttachmentWrite {\n path: string\n ownership: AttachmentWriteOwnership\n receipt: AttachmentWriteReceipt\n}\n\n/** Run every available compensation, from the newest write to the oldest. The\n * receipt owns the compare-and-delete operation; this route never deletes a\n * logical path itself. */\nasync function rollbackSuccessfulWrites(\n writes: readonly SuccessfulAttachmentWrite[],\n scopeId: string,\n writer: AtomicAttachmentWriter,\n logger: AttachmentUploadLogger,\n): Promise<number> {\n let failures = 0\n for (let index = writes.length - 1; index >= 0; index -= 1) {\n const write = writes[index]!\n try {\n await write.receipt.rollback()\n } catch (error) {\n failures += 1\n logAttachmentUploadError(logger, '[attachment-upload] cleanup failed', {\n path: write.path,\n ownershipId: write.ownership.id,\n error: redactErrorMessage(error),\n })\n failures += await abortOwnership(writer, scopeId, write.ownership, logger)\n }\n }\n return failures\n}\n\nasync function abortOwnership(\n writer: AtomicAttachmentWriter,\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n logger: AttachmentUploadLogger,\n): Promise<number> {\n try {\n await writer.abort(scopeId, ownership)\n return 0\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] ambiguous write cleanup failed', {\n path: ownership.path,\n ownershipId: ownership.id,\n error: redactErrorMessage(error),\n })\n return 1\n }\n}\n\nfunction isAtomicRouteOptions(\n options: CreateAttachmentUploadRouteOptions | CreateAtomicAttachmentUploadRouteOptions,\n): options is CreateAtomicAttachmentUploadRouteOptions {\n return 'attachmentWriter' in options\n}\n\n/** Resolve an attachment upload route handler with customizable limits and validation options. */\nexport function createAttachmentUploadRoute(\n options: CreateAttachmentUploadRouteOptions | CreateAtomicAttachmentUploadRouteOptions,\n): (request: Request) => Promise<Response> {\n const atomic = isAtomicRouteOptions(options)\n const maxCount = options.limits?.maxCount ?? ATTACHMENT_MAX_COUNT\n const maxBinaryBytes = options.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES\n const maxBytesBySniffedMime = options.limits?.maxBytesBySniffedMime\n const maxTextBytes = options.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES\n const maxTotalBytes = options.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const allowedKinds: ChatAttachmentKind[] = options.allowedKinds ?? ['image', 'file']\n const allowedSniffedMimes = options.allowedSniffedMimes ?? ALLOWED_ATTACHMENT_SNIFFED_MIMES\n const pathFor = options.pathFor ?? ((name: string) => name)\n const validatePath = options.validatePath ?? defaultValidateAttachmentPath\n const sniffMime = options.sniffMime ?? sniffMimeFromName\n const createWriteId = atomic\n ? options.createWriteId ?? (() => {\n if (typeof crypto === 'undefined' || typeof crypto.randomUUID !== 'function') {\n throw new Error('attachment upload requires crypto.randomUUID')\n }\n return crypto.randomUUID()\n })\n : undefined\n const logger = options.logger ?? console\n const maxFormBytes = options.limits?.maxFormBytes\n const streamWriter = atomic ? undefined : options.writeAttachmentStream\n const limitFor = (sniff: SniffResult): number => {\n if (!sniff.binary) return maxTextBytes\n return sniff.mime === null ? maxBinaryBytes : maxBytesBySniffedMime?.get(sniff.mime) ?? maxBinaryBytes\n }\n // The most any admitted type may weigh, checked against a raw upload's\n // declared size before a byte is read; the type's own cap is checked once\n // the content is known.\n const largestFileBytes = Math.max(maxTextBytes, maxBinaryBytes, ...(maxBytesBySniffedMime?.values() ?? []))\n\n /**\n * The streamed lane: one raw body larger than the sniff head, written as it\n * arrives. The sniffer keeps only the head and tail, and the last chunk is\n * withheld until the whole file passes the type, kind and size gates, so a\n * refused file never reaches the store in full and its write fails.\n */\n async function streamRawUpload(input: {\n request: Request\n scopeId: string\n writer: WriteAttachmentStreamFn\n filename: string\n name: string\n path: string\n declaredBytes: number\n }): Promise<Response> {\n const { request, declaredBytes, name, path } = input\n if (!request.body) return attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE)\n const reader = request.body.getReader()\n const sniffer = createStreamingSniffer()\n const head: Uint8Array[] = []\n let refusal: Response | undefined\n const tooLong = () => attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE)\n\n try {\n while (sniffer.size < STREAM_SNIFF_HEAD_BYTES) {\n const { done, value } = await reader.read()\n if (done) break\n if (sniffer.size + value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n return tooLong()\n }\n sniffer.push(value)\n head.push(value)\n }\n } catch {\n return attachmentUploadError(400, 'invalid_upload', 'The upload body ended early')\n }\n const mediaTypeBeforeEnd = provisionalMediaType(name, sniffBinary(sniffer.head()), sniffMime)\n\n let held = head.pop()\n let finalSniff: SniffResult | undefined\n const refuse = (controller: ReadableStreamDefaultController<Uint8Array>, response: Response) => {\n refusal = response\n controller.error(new Error('attachment upload refused'))\n }\n const content = new ReadableStream<Uint8Array>({\n start(controller) {\n for (const chunk of head) controller.enqueue(chunk)\n },\n async pull(controller) {\n let next: ReadableStreamReadResult<Uint8Array>\n try {\n next = await reader.read()\n } catch {\n refuse(controller, attachmentUploadError(400, 'invalid_upload', 'The upload body ended early'))\n return\n }\n if (!next.done) {\n if (sniffer.size + next.value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n refuse(controller, tooLong())\n return\n }\n sniffer.push(next.value)\n if (held) controller.enqueue(held)\n held = next.value\n return\n }\n if (sniffer.size !== declaredBytes) {\n refuse(controller, tooLong())\n return\n }\n const sniff = sniffer.finish()\n const typeCheck = checkAttachmentType(name, sniff, allowedSniffedMimes)\n if (!typeCheck.succeeded) {\n refuse(controller, attachmentUploadError(\n typeCheck.code === 'attachment_type_mismatch' ? 400 : 415,\n typeCheck.code,\n typeCheck.message,\n ))\n return\n }\n const kind = attachmentKindForMime(sniff.mime ?? '')\n if (!allowedKinds.includes(kind)) {\n refuse(controller, attachmentUploadError(\n 415,\n 'attachment_kind_not_allowed',\n `${name} is a \"${kind}\" attachment, which this upload route does not accept`,\n ))\n return\n }\n const limit = limitFor(sniff)\n if (declaredBytes > limit) {\n refuse(controller, attachmentUploadError(413, 'attachment_too_large', attachmentSizeErrorMessage(name, declaredBytes, limit)))\n return\n }\n finalSniff = sniff\n if (held) controller.enqueue(held)\n held = undefined\n controller.close()\n },\n cancel(reason) {\n return reader.cancel(reason).catch(() => undefined)\n },\n }, { highWaterMark: 0 })\n\n let written: unknown\n try {\n written = await input.writer(input.scopeId, path, content, {\n mediaType: mediaTypeBeforeEnd,\n name,\n originalName: input.filename,\n size: declaredBytes,\n })\n } catch (error) {\n if (!finalSniff) await reader.cancel().catch(() => undefined)\n if (refusal) return refusal\n logAttachmentUploadError(logger, '[attachment-upload] stream writer failed', { path, error: redactErrorMessage(error) })\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, path)\n }\n if (!finalSniff) await reader.cancel().catch(() => undefined)\n if (refusal) return refusal\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result || (result.ok && !finalSniff)) {\n // A writer that reports success without reading the file to its end\n // stored something the route never checked.\n logAttachmentUploadError(logger, '[attachment-upload] stream writer returned without a checked file', { path })\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, path)\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logAttachmentUploadError(logger, '[attachment-upload] stream write rejected', { path, error: reason })\n return attachmentUploadError(413, 'attachment_write_failed', reason, path)\n }\n const sniff = finalSniff!\n const uploaded: ChatAttachmentInput = {\n path,\n name,\n size: declaredBytes,\n mediaType: sniff.mime ?? sniffMime(name),\n kind: attachmentKindForMime(sniff.mime ?? ''),\n }\n return Response.json({ files: [uploaded] })\n }\n\n return async function attachmentUpload(request: Request): Promise<Response> {\n const auth = await options.authorize({ request })\n if (!auth.ok) return auth.response\n const atomicWriter = atomic\n ? (auth as Extract<AtomicAttachmentUploadAuthorization, { ok: true }>).attachmentWriter ?? options.attachmentWriter\n : undefined\n const legacyWriter = atomic\n ? undefined\n : (auth as Extract<AttachmentUploadAuthorization, { ok: true }>).writeAttachment ?? options.writeAttachment\n if (atomic && !atomicWriter) {\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE)\n }\n\n const files: File[] = []\n const contentType = request.headers.get('Content-Type')?.toLowerCase() ?? ''\n if (!contentType.startsWith('multipart/form-data')) {\n const target = rawUploadTarget(request)\n if (!target.succeeded) return target.response\n const { filename, declaredBytes } = target\n const name = sanitizeAttachmentFileName(filename)\n if (declaredBytes > largestFileBytes) {\n return attachmentUploadError(413, 'attachment_too_large', attachmentSizeErrorMessage(name, declaredBytes, largestFileBytes))\n }\n if (declaredBytes > maxTotalBytes) {\n return attachmentUploadError(413, 'attachments_total_too_large', attachmentTotalSizeErrorMessage(declaredBytes, maxTotalBytes))\n }\n if (streamWriter && declaredBytes > STREAM_SNIFF_HEAD_BYTES) {\n const path = pathFor(name)\n const pathCheck = validatePath(path)\n if (!pathCheck.succeeded) return attachmentUploadError(400, 'invalid_attachment_path', pathCheck.error, path)\n const writer = (auth as Extract<AttachmentUploadAuthorization, { ok: true }>).writeAttachmentStream ?? streamWriter\n return streamRawUpload({ request, scopeId: auth.scopeId, writer, filename, name, path, declaredBytes })\n }\n // A small raw body takes the batch path below as a one-file batch.\n const body = await readRawBody(request, declaredBytes)\n if (!body.succeeded) return body.response\n files.push(new File([body.value], filename))\n } else {\n let formSource: Request | Response = request\n if (maxFormBytes !== undefined) {\n // Read at most the cap before parsing, whether or not the client sent\n // a Content-Length, so an oversized form is refused while small.\n const bounded = await readBodyUpTo(request, maxFormBytes)\n if (!bounded.succeeded && !bounded.tooLarge) {\n return attachmentUploadError(400, 'invalid_upload', 'The upload body ended early')\n }\n if (!bounded.succeeded) {\n return attachmentUploadError(\n 413,\n 'form_upload_too_large',\n `Form uploads are limited to ${formatBytes(maxFormBytes)}; send a larger file as a raw request body named by the filename query parameter`,\n )\n }\n formSource = new Response(bounded.value, { headers: { 'Content-Type': request.headers.get('Content-Type') ?? '' } })\n }\n let form: FormData\n try {\n form = await formSource.formData()\n } catch {\n return attachmentUploadError(400, 'invalid_upload', 'Expected a multipart/form-data body with file fields')\n }\n // Collect every File value regardless of field name — the client may\n // send one field per file or a single repeated field.\n form.forEach((value) => {\n if (value instanceof File) files.push(value)\n })\n }\n if (files.length === 0) {\n return attachmentUploadError(400, 'invalid_upload', 'No files in the upload body')\n }\n\n if (files.length > maxCount) {\n return attachmentUploadError(\n 400,\n 'attachment_count_exceeded',\n `Too many files — the ${maxCount}-file limit was exceeded`,\n )\n }\n\n // Advisory only: client-controlled `file.size`, so a dishonest caller can\n // slip past it — but an honest oversized batch should fail before a\n // single byte is read. The authoritative aggregate check (against the\n // sniffed/decoded byte length) runs per-file in phase 1, below.\n const advisoryTotal = files.reduce((sum, file) => sum + file.size, 0)\n if (advisoryTotal > maxTotalBytes) {\n return attachmentUploadError(\n 413,\n 'attachments_total_too_large',\n attachmentTotalSizeErrorMessage(advisoryTotal, maxTotalBytes),\n )\n }\n\n interface PreparedWrite {\n path: string\n ownershipId?: string\n name: string\n bytes: Uint8Array\n originalName: string\n size: number\n mediaType: string\n kind: ChatAttachmentKind\n }\n\n // Phase 1: validate every file and prepare its write input WITHOUT\n // writing anything. A batch fails atomically — any file's validation\n // error must reject the whole request before an earlier file in the\n // same batch is persisted.\n const prepared: PreparedWrite[] = []\n const seenLogicalPaths = new Set<string>()\n let totalBytes = 0\n\n for (const file of files) {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const sniff = sniffBinary(bytes)\n // Attachment paths double as store keys, so the stored name must fit\n // the store charset; the as-uploaded name survives as `originalName`.\n const name = sanitizeAttachmentFileName(file.name)\n\n const typeCheck = checkAttachmentType(name, sniff, allowedSniffedMimes)\n if (!typeCheck.succeeded) {\n return attachmentUploadError(\n typeCheck.code === 'attachment_type_mismatch' ? 400 : 415,\n typeCheck.code,\n typeCheck.message,\n )\n }\n\n const kind = attachmentKindForMime(sniff.mime ?? '')\n if (!allowedKinds.includes(kind)) {\n return attachmentUploadError(\n 415,\n 'attachment_kind_not_allowed',\n `${name} is a \"${kind}\" attachment, which this upload route does not accept`,\n )\n }\n\n const limit = limitFor(sniff)\n if (bytes.length > limit) {\n return attachmentUploadError(\n 413,\n 'attachment_too_large',\n attachmentSizeErrorMessage(name, bytes.length, limit),\n )\n }\n\n const logicalPath = pathFor(name)\n let ownershipId: string | undefined\n let path = logicalPath\n if (atomic) {\n try {\n ownershipId = createWriteId!()\n path = immutableAttachmentPath(logicalPath, ownershipId)\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] could not allocate ownership key', {\n path: logicalPath,\n error: redactErrorMessage(error),\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n logicalPath,\n )\n }\n }\n const pathCheck = validatePath(path)\n if (!pathCheck.succeeded) {\n return attachmentUploadError(400, 'invalid_attachment_path', pathCheck.error, path)\n }\n // Small hardening over gtm: a batch whose sanitized names collide onto\n // the same store path (e.g. two variously-cased \"Report.PDF\" uploads)\n // would otherwise silently overwrite one with the other in phase 2.\n const duplicatePath = atomic ? logicalPath : path\n if (seenLogicalPaths.has(duplicatePath)) {\n return attachmentUploadError(\n 400,\n 'attachment_duplicate_path',\n `attachments must not repeat a path within one upload: ${duplicatePath}`,\n path,\n )\n }\n seenLogicalPaths.add(duplicatePath)\n\n // Authoritative aggregate check, against the actual decoded byte\n // length rather than the client-reported `file.size` checked above.\n totalBytes += bytes.length\n if (totalBytes > maxTotalBytes) {\n return attachmentUploadError(\n 413,\n 'attachments_total_too_large',\n attachmentTotalSizeErrorMessage(totalBytes, maxTotalBytes),\n )\n }\n\n const mediaType = sniff.mime ?? sniffMime(name)\n prepared.push({ path, ownershipId, name, bytes, originalName: file.name, size: bytes.length, mediaType, kind })\n }\n\n // Phase 2: every file in the batch passed validation — write them all.\n const uploaded: ChatAttachmentInput[] = []\n const successfulWrites: SuccessfulAttachmentWrite[] = []\n for (const input of prepared) {\n if (!atomic) {\n let written: unknown\n try {\n written = await legacyWriter!(auth.scopeId, input.path, input.bytes, {\n mediaType: input.mediaType,\n name: input.name,\n originalName: input.originalName,\n size: input.size,\n })\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] legacy writer failed', {\n path: input.path,\n error: redactErrorMessage(error),\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n input.path,\n )\n }\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result) {\n logAttachmentUploadError(logger, '[attachment-upload] legacy writer returned an invalid result', {\n path: input.path,\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n input.path,\n )\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logAttachmentUploadError(logger, '[attachment-upload] legacy write rejected', {\n path: input.path,\n error: reason,\n })\n return attachmentUploadError(413, 'attachment_write_failed', reason, input.path)\n }\n uploaded.push({\n path: input.path,\n name: input.name,\n size: input.size,\n mediaType: input.mediaType,\n kind: input.kind,\n })\n continue\n }\n\n const ownershipId = input.ownershipId\n if (!ownershipId) {\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, input.path)\n }\n const ownership: AttachmentWriteOwnership = Object.freeze({ id: ownershipId, path: input.path })\n let written: Awaited<ReturnType<AtomicAttachmentWriter['write']>>\n try {\n written = await atomicWriter!.write(auth.scopeId, input.path, input.bytes, {\n mediaType: input.mediaType,\n name: input.name,\n originalName: input.originalName,\n size: input.size,\n ownership,\n })\n } catch (error) {\n const cleanupFailures = (await abortOwnership(atomicWriter!, auth.scopeId, ownership, logger)) +\n await rollbackSuccessfulWrites(successfulWrites, auth.scopeId, atomicWriter!, logger)\n logAttachmentUploadError(logger, '[attachment-upload] write failed', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n error: redactErrorMessage(error),\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n const result = inspectAtomicAttachmentWriteResult(written, ownership)\n if (!result) {\n const cleanupFailures = (await abortOwnership(atomicWriter!, auth.scopeId, ownership, logger)) +\n await rollbackSuccessfulWrites(successfulWrites, auth.scopeId, atomicWriter!, logger)\n logAttachmentUploadError(logger, '[attachment-upload] writer returned a mismatched ownership receipt', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n if (!result.ok) {\n const cleanupFailures = (await rollbackSuccessfulWrites([\n { path: input.path, ownership, receipt: result.receipt },\n ], auth.scopeId, atomicWriter!, logger)) + await rollbackSuccessfulWrites(\n successfulWrites,\n auth.scopeId,\n atomicWriter!,\n logger,\n )\n logAttachmentUploadError(logger, '[attachment-upload] write rejected', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n error: redactErrorMessage(result.reason),\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n successfulWrites.push({ path: input.path, ownership, receipt: result.receipt })\n uploaded.push({\n path: input.path,\n name: input.name,\n size: input.size,\n mediaType: input.mediaType,\n kind: input.kind,\n })\n }\n\n return Response.json({ files: uploaded })\n }\n}\n","/**\n * `buildDispatchParts` — assemble the `PromptInputPart[]` a turn carrying\n * attachments and/or `@`-mentions dispatches to the sandbox. `parts[0]` is\n * always the full prompt text (typed text plus the attachment + mention pointer\n * blocks); each attachment or mention becomes one media part. An attachment\n * (read from the product store via the injected reader) draws the inline byte\n * budget first; a mention (read from the LIVE box) takes what is left. A file\n * inlines as a `data:` URI when it fits the remaining budget, otherwise uses\n * the in-box file reference supported by the current sandbox prompt contract.\n * Every media part is deduped by its resolved absolute path. This module only\n * produces the parts array; the caller decides when a turn dispatches parts\n * instead of a plain string.\n *\n * Storage-parameterized port of gtm-agent's `dispatch-parts.ts`: the vault\n * default reader is dropped (`readAttachment` is REQUIRED — the product supplies\n * its store adapter), the `GTM_SANDBOX_VAULT_DIR` prefixing becomes the required\n * `resolveAttachmentPath` seam, the `GTM_MULTIMODAL_FORCE_PATH` env fallback\n * becomes an explicit `forcePath` flag, and every budget cap reads an overridable\n * `./wire` constant. Kept behavior-identical for gtm-agent#618 adoption (the\n * demotion math and emitted part shapes reproduce its dispatched prompt bytes).\n */\n\nimport { isWorkspaceFileExportable } from '../web/file-export'\nimport { flattenHistory, type PromptInputPart } from '../sandbox'\nimport {\n statSandboxFileSize,\n readSandboxBinaryBytes,\n type SandboxExecChannel,\n} from '../sandbox/binary-read'\nimport {\n ChatTurnInputError,\n mediaTypeForMentionPath,\n base64WireLen,\n DISPATCH_REQUEST_MAX_BYTES,\n DISPATCH_STRUCTURAL_RESERVE_BYTES,\n DISPATCH_MAX_PARTS,\n type ChatTurnPartInput,\n} from './wire'\nimport type { ChatAttachmentPart, ChatMentionPart } from '../chat-store/parts'\nimport { bytesToBase64 } from './upload'\nimport type { ReadAttachmentFn } from './attachment-store'\nimport { pathToFileURL } from 'node:url'\n\nexport type { PromptInputPart }\n\n/** Resolve the outcome of dispatching parts with success status and corresponding value or error message */\nexport type DispatchPartsOutcome =\n | { succeeded: true; value: PromptInputPart[] }\n | { succeeded: false; error: string }\n\n/** One mention file's size (always) and inline bytes (only when the caller\n * asked for them — a path-only mention never reads its bytes). */\ntype SandboxMentionReadOutcome =\n | { succeeded: true; value: { size: number; base64?: string } }\n | { succeeded: false; error: string }\n\n/** Resolve sandbox mention details by reading from a specified path with optional byte reading */\nexport type ReadSandboxMentionFn = (\n box: SandboxExecChannel,\n absolutePath: string,\n options: { readBytes: boolean },\n) => Promise<SandboxMentionReadOutcome>\n\nfunction byteLen(value: string): number {\n return new TextEncoder().encode(value).length\n}\n\n/**\n * Default mention reader: stats the in-box file (which also proves it still\n * exists — a since-deleted mention fails loud here), then reads its bytes only\n * when the caller wants to inline it. Both hops cross the sandbox exec channel\n * via the substrate's binary-read helpers.\n */\nasync function readSandboxMention(\n box: SandboxExecChannel,\n absolutePath: string,\n options: { readBytes: boolean },\n): Promise<SandboxMentionReadOutcome> {\n const stat = await statSandboxFileSize(box, absolutePath)\n if (!stat.succeeded) {\n return { succeeded: false, error: `mentioned sandbox file missing or unreadable: ${absolutePath} — ${stat.error}` }\n }\n if (!options.readBytes) return { succeeded: true, value: { size: stat.value } }\n\n const read = await readSandboxBinaryBytes(box, absolutePath, stat.value)\n if (!read.succeeded) {\n return { succeeded: false, error: `mentioned sandbox file read failed: ${absolutePath} — ${read.error}` }\n }\n return { succeeded: true, value: { size: stat.value, base64: bytesToBase64(read.value.bytes) } }\n}\n\n/** HARD INVARIANT: an image carries exactly one URL or absolute path, while a\n * file carries the URL required by the current sandbox prompt contract. */\nfunction violatesUrlPathXor(part: PromptInputPart): boolean {\n if (part.type === 'text') return false\n const hasUrl = typeof part.url === 'string' && (\n part.url.startsWith('data:')\n || part.url.startsWith('file://')\n )\n const hasPath = 'path' in part && typeof part.path === 'string' && part.path.startsWith('/')\n if (part.type === 'file') return !hasUrl || hasPath\n return hasUrl === hasPath\n}\n\nfunction sandboxFileUrl(absolutePath: string): string | undefined {\n if (!absolutePath.startsWith('/')) return undefined\n try {\n return pathToFileURL(absolutePath).href\n } catch {\n return undefined\n }\n}\n\n/**\n * Convert the browser-safe chat attachment contract into the current sandbox\n * prompt contract. Images carry exactly one URL or path; generic files carry a\n * filename and URL.\n */\nexport function normalizeChatPromptForSandbox(\n prompt: string | readonly ChatTurnPartInput[],\n): string | PromptInputPart[] {\n if (typeof prompt === 'string') return prompt\n return prompt.map((part): PromptInputPart => {\n if (part.type === 'text') return part\n if ('content' in part) {\n throw new ChatTurnInputError('Sandbox prompt parts do not accept inline content; provide a URL or path')\n }\n\n const url = typeof part.url === 'string' && part.url.length > 0 ? part.url : undefined\n const path = typeof part.path === 'string' && part.path.length > 0 ? part.path : undefined\n if (Boolean(url) === Boolean(path)) {\n throw new ChatTurnInputError(`Sandbox ${part.type} parts require exactly one URL or path`)\n }\n if (path && !path.startsWith('/')) {\n throw new ChatTurnInputError(`Sandbox ${part.type} paths must be absolute: ${path}`)\n }\n\n if (part.type === 'image') {\n return {\n type: 'image',\n ...(part.filename ? { filename: part.filename } : {}),\n ...(part.mediaType ? { mediaType: part.mediaType } : {}),\n ...(url ? { url } : { path: path! }),\n }\n }\n\n const filename = part.filename?.trim()\n if (!filename) {\n throw new ChatTurnInputError('Sandbox file parts require a filename')\n }\n const fileUrl = url ?? sandboxFileUrl(path!)\n if (!fileUrl) {\n throw new ChatTurnInputError(`Sandbox file paths must be absolute: ${path}`)\n }\n return {\n type: 'file',\n filename,\n ...(part.mediaType ? { mediaType: part.mediaType } : {}),\n url: fileUrl,\n }\n })\n}\n\n/** Build input parameters for dispatching chat message parts including text, attachments, mentions, and history */\nexport interface BuildDispatchPartsInput {\n text: string\n attachments: ChatAttachmentPart[]\n mentions?: ChatMentionPart[]\n history: Array<{ role: 'user' | 'assistant'; content: string }>\n systemPrompt: string\n /** Serialized size of the backend profile the SDK inlines into the same\n * prompt request body — a large, non-negotiable rider that must come out of\n * the inline budget or near-cap attachments 413 at the proxy instead of\n * demoting to path parts. */\n profileWireBytes: number\n /** The product's workspace/tenant key, passed to `readAttachment`. */\n scopeId: string\n /** Maps an attachment's store-relative path to the in-box absolute path a\n * local media reference uses (same style as `fileMentionsToParts`'s\n * `resolvePath`). */\n resolveAttachmentPath: (path: string) => string\n /** Maps a mention's workspace-relative path to its in-box absolute path.\n * Default: {@link BuildDispatchPartsInput.resolveAttachmentPath} — in gtm the\n * vault mount roots both; a product that mounts them apart overrides this. */\n resolveMentionPath?: (path: string) => string\n /** The turn's already-ensured box — required when `mentions` is non-empty\n * (mention bytes are read from the live box, not the store). */\n box?: SandboxExecChannel\n /** Force every media part to a local file reference, skipping all inlining. */\n forcePath?: boolean\n /** REQUIRED store reader for attachment content — no default (the product\n * owns its store; see {@link ReadAttachmentFn}). */\n readAttachment: ReadAttachmentFn\n readSandboxMention?: ReadSandboxMentionFn\n /** Whole-request proxy cap. Default {@link DISPATCH_REQUEST_MAX_BYTES}. */\n requestMaxBytes?: number\n /** JSON-envelope reserve off the top of the request cap. Default\n * {@link DISPATCH_STRUCTURAL_RESERVE_BYTES}. */\n structuralReserveBytes?: number\n /** Sidecar per-request parts-array cap. Default {@link DISPATCH_MAX_PARTS}. */\n maxParts?: number\n}\n\n/** Content of one attachment read, normalized to the base64 a `data:` URI\n * needs — `base64` reused verbatim, else `bytes` encoded once. */\nfunction readResultToBase64(read: { base64?: string; bytes?: Uint8Array }): string | undefined {\n if (typeof read.base64 === 'string') return read.base64\n if (read.bytes) return bytesToBase64(read.bytes)\n return undefined\n}\n\n/** Build dispatch parts from input by resolving mentions, paths, and applying size constraints asynchronously */\nexport async function buildDispatchParts(input: BuildDispatchPartsInput): Promise<DispatchPartsOutcome> {\n const readMention = input.readSandboxMention ?? readSandboxMention\n const resolveMentionPath = input.resolveMentionPath ?? input.resolveAttachmentPath\n const forcePath = input.forcePath ?? false\n const mentions = input.mentions ?? []\n const requestMaxBytes = input.requestMaxBytes ?? DISPATCH_REQUEST_MAX_BYTES\n const structuralReserveBytes = input.structuralReserveBytes ?? DISPATCH_STRUCTURAL_RESERVE_BYTES\n const maxParts = input.maxParts ?? DISPATCH_MAX_PARTS\n\n const parts: PromptInputPart[] = [{ type: 'text', text: input.text }]\n // Absolute in-box paths already emitted — a file both attached and mentioned\n // (or mentioned twice) rides as a single media part.\n const emittedAbsPaths = new Set<string>()\n\n const flattenedForSizing = flattenHistory(input.text, input.history)\n // May go negative when history/systemPrompt alone are large — every\n // attachment then fails the `runningInline + cost <= inlineBudget` check\n // below and uses a local file reference, which does not draw on this inline\n // budget.\n const inlineBudget =\n requestMaxBytes\n - base64WireLen(byteLen(flattenedForSizing))\n - byteLen(JSON.stringify(input.systemPrompt))\n - input.profileWireBytes\n - structuralReserveBytes\n\n let runningInline = 0\n\n // Stable input order: attachments dispatch in the order the user attached\n // them, and the pointer block in `input.text` already names them in that\n // same order.\n for (const attachment of input.attachments) {\n if (!attachment.path) {\n return { succeeded: false, error: `attachment path must be non-empty: ${attachment.name}` }\n }\n\n // The reader crosses an external boundary (store/coordinator) — a rejection\n // there must land in the typed outcome, not escape as a thrown error the\n // caller would misattribute to stream init.\n let read: Awaited<ReturnType<ReadAttachmentFn>>\n try {\n read = await input.readAttachment(input.scopeId, attachment.path)\n } catch (err) {\n return {\n succeeded: false,\n error: `attachment store read failed: ${attachment.path} — ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n if (!read.ok) return { succeeded: false, error: read.reason }\n\n const base64 = readResultToBase64(read)\n if (base64 === undefined) {\n return { succeeded: false, error: `attachment store read produced no content: ${attachment.path}` }\n }\n\n const mediaType = attachment.mediaType ?? read.mediaType\n if (attachment.type === 'image' && !mediaType) {\n return { succeeded: false, error: `attachment is missing a mediaType required for an image data URI: ${attachment.path}` }\n }\n\n const absPath = input.resolveAttachmentPath(attachment.path)\n emittedAbsPaths.add(absPath)\n\n if (attachment.type === 'image') {\n const inlinePart: PromptInputPart = {\n type: 'image',\n filename: attachment.name,\n mediaType,\n url: `data:${mediaType};base64,${base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n if (!forcePath && runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n } else {\n parts.push({ type: 'image', filename: attachment.name, mediaType, path: absPath })\n }\n continue\n }\n\n // File parts use the current filename + URL contract. A file that is too\n // large to inline keeps its existing in-box file through a file:// URL.\n const fileMediaType = mediaType ?? 'application/octet-stream'\n const inlinePart: PromptInputPart = {\n type: 'file',\n filename: attachment.name,\n mediaType: fileMediaType,\n url: `data:${fileMediaType};base64,${base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n if (!forcePath && runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n } else {\n const url = sandboxFileUrl(absPath)\n if (!url) {\n return { succeeded: false, error: `resolved attachment path must be absolute: ${absPath}` }\n }\n parts.push({ type: 'file', filename: attachment.name, mediaType: fileMediaType, url })\n }\n }\n\n if (mentions.length > 0 && !input.box) {\n return { succeeded: false, error: 'internal error: sandbox mentions require a box to read from' }\n }\n for (const mention of mentions) {\n if (!mention.path) {\n return { succeeded: false, error: `mention path must be non-empty: ${mention.name}` }\n }\n if (!isWorkspaceFileExportable(mention.path)) {\n return { succeeded: false, error: `mention file is not exportable: ${mention.path}` }\n }\n const absPath = resolveMentionPath(mention.path)\n if (emittedAbsPaths.has(absPath)) continue\n emittedAbsPaths.add(absPath)\n\n const isImage = mention.mentionKind === 'image'\n const mediaType = isImage ? mediaTypeForMentionPath(mention.path) : undefined\n\n // Every mention is stat'd first: it proves the file still exists (a deleted\n // mention fails the turn loud) and gives the size for the inline budget\n // decision without pulling bytes across the exec channel.\n let stat: SandboxMentionReadOutcome\n try {\n stat = await readMention(input.box!, absPath, { readBytes: false })\n } catch (err) {\n return { succeeded: false, error: `mention read failed: ${absPath} — ${err instanceof Error ? err.message : String(err)}` }\n }\n if (!stat.succeeded) return { succeeded: false, error: stat.error }\n\n // Only an image that projects within the remaining budget reads its bytes\n // to inline; every other mention (and a budget-exceeding image) ships\n // by local reference, so a large file is never base64'd unnecessarily.\n const projectedInlineCost = base64WireLen(stat.value.size)\n + byteLen(JSON.stringify({ type: 'image', filename: mention.name, mediaType: mediaType ?? '', url: '' }))\n if (isImage && mediaType && !forcePath && runningInline + projectedInlineCost <= inlineBudget) {\n let read: SandboxMentionReadOutcome\n try {\n read = await readMention(input.box!, absPath, { readBytes: true })\n } catch (err) {\n return { succeeded: false, error: `mention read failed: ${absPath} — ${err instanceof Error ? err.message : String(err)}` }\n }\n if (!read.succeeded) return { succeeded: false, error: read.error }\n if (!read.value.base64) return { succeeded: false, error: `mentioned image produced no bytes: ${absPath}` }\n const inlinePart: PromptInputPart = {\n type: 'image',\n filename: mention.name,\n mediaType,\n url: `data:${mediaType};base64,${read.value.base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n // Re-check against the actual serialized cost — the projection can\n // undershoot; a real overshoot demotes to a path part rather than 413ing.\n if (runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n continue\n }\n }\n\n if (isImage && mediaType) {\n parts.push({ type: 'image', filename: mention.name, mediaType, path: absPath })\n } else {\n const url = sandboxFileUrl(absPath)\n if (!url) {\n return { succeeded: false, error: `resolved mention path must be absolute: ${absPath}` }\n }\n parts.push({ type: 'file', filename: mention.name, url })\n }\n }\n\n for (const part of parts) {\n if (violatesUrlPathXor(part)) {\n return { succeeded: false, error: 'internal error: emitted media part violates the url/path exclusivity invariant' }\n }\n }\n\n // Final whole-request check sized against what actually crosses the wire: the\n // history-merged text part (the substrate folds `history` into `parts[0]`\n // before dispatch), the media parts, the system prompt, and the inlined\n // backend profile riding the same body — the same terms `inlineBudget` was\n // derived from.\n const textPartSize = base64WireLen(byteLen(flattenedForSizing))\n const mediaPartsSize = parts.slice(1).reduce((total, part) => total + byteLen(JSON.stringify(part)), 0)\n const systemPromptSize = byteLen(JSON.stringify(input.systemPrompt))\n if (textPartSize + mediaPartsSize + systemPromptSize + input.profileWireBytes + structuralReserveBytes > requestMaxBytes) {\n return { succeeded: false, error: 'dispatch parts exceed the sandbox proxy request cap even after path demotion' }\n }\n\n // The sidecar rejects the whole request past its parts-array cap; the caller\n // selects which media ride natively, so overflow here is a caller bug\n // surfaced loudly rather than a truncation.\n if (parts.length > maxParts) {\n return { succeeded: false, error: `dispatch parts exceed the sidecar per-request cap of ${maxParts}` }\n }\n\n return { succeeded: true, value: parts }\n}\n","import type { IntelligenceClient } from '@tangle-network/agent-runtime/intelligence'\nimport type { ChatTurnRouteProducer } from './turn-routes'\n\n/** Host-resolved metadata only. Neither input nor output content is exported. */\nexport interface ChatTurnIntelligenceOptions {\n client: () => Pick<IntelligenceClient, 'traceRun'>\n sessionId: string\n userId?: string\n workspaceId?: string\n /** Exact execution identity, when retained by the execution owner. */\n runId?: string\n traceId?: string\n /** Requested model. This is not a receipt for the model that served. */\n model?: string\n /** Existing transport cancellation evidence; grants no new abort authority. */\n signal?: AbortSignal\n /** Receives no exception text, which may contain private customer material. */\n onObservationError?: () => void\n}\n\n/**\n * Observe the source iterator without an execution loop, relay, queue or eager\n * pull. The source still owns cancellation and serializes its own operations.\n * Stream termination is not task correctness, reviewer acceptance or delivery.\n */\nexport function observeChatTurnStream<TEvent>(\n source: AsyncGenerator<TEvent, void, unknown>,\n options: ChatTurnIntelligenceOptions,\n): AsyncGenerator<TEvent, void, unknown> {\n const { client, sessionId, userId, workspaceId, runId, traceId, model, signal, onObservationError } = options\n let state: 'idle' | 'running' | 'terminal' = 'idle'\n let startedAt = 0\n let interrupted = false\n let observation: Promise<void> | undefined\n\n const finish = (termination: 'exhausted' | 'interrupted' | 'failed'): Promise<void> => {\n if (state !== 'running') return observation ?? Promise.resolve()\n state = 'terminal'\n const completedAt = Date.now()\n const observedTermination = signal?.aborted ? 'interrupted' : termination\n observation = Promise.resolve().then(async () => {\n await client().traceRun({\n ...(runId !== undefined ? { runId } : {}),\n ...(traceId !== undefined ? { traceId } : {}),\n ...(model !== undefined ? { model } : {}),\n labels: {\n 'tangle.sessionId': sessionId,\n ...(userId !== undefined ? { 'tangle.userId': userId } : {}),\n ...(workspaceId !== undefined ? { 'tangle.workspaceId': workspaceId } : {}),\n 'tangle.observation.kind': 'stream-lifecycle',\n 'tangle.stream.termination': observedTermination,\n 'tangle.started_at_ms': startedAt,\n 'tangle.completed_at_ms': completedAt,\n 'tangle.duration_ms': Math.max(0, completedAt - startedAt),\n },\n }, async () => undefined)\n }).catch(() => {\n // Even a diagnostic callback must not replace an execution error.\n try { onObservationError?.() } catch { /* Best-effort telemetry only. */ }\n })\n return observation\n }\n\n const invoke = async (\n method: 'next' | 'return' | 'throw',\n operation: () => ReturnType<typeof source.next>,\n ): Promise<Awaited<ReturnType<typeof source.next>>> => {\n if (state === 'idle') {\n // A pre-start close still reaches the source to release factory-owned\n // resources, but does not manufacture an executed-turn observation.\n if (method === 'next') {\n state = 'running'\n startedAt = Date.now()\n } else {\n state = 'terminal'\n }\n }\n try {\n const result = await operation()\n // Apply return intent in source settlement order. A queued return must\n // not relabel an earlier naturally completed next() as cancellation.\n if (method === 'return' && state === 'running') interrupted = true\n if (result.done) await finish(interrupted ? 'interrupted' : 'exhausted')\n return result\n } catch (error) {\n await finish('failed')\n throw error\n }\n }\n\n return {\n [Symbol.asyncIterator]() { return this },\n next(...args) { return invoke('next', () => source.next(...args)) },\n return(value) { return invoke('return', () => source.return(value)) },\n throw(error) { return invoke('throw', () => source.throw(error)) },\n async [Symbol.asyncDispose]() { await this.return(undefined) },\n }\n}\n\n/**\n * Adapter for product producer factories. It preserves every declared\n * ChatTurnRouteProducer projection, lazily bound to the original receiver,\n * including class-backed or frozen producers. No output is read.\n */\nexport async function produceChatTurnWithIntelligence(\n options: ChatTurnIntelligenceOptions & {\n produce: () => ChatTurnRouteProducer | Promise<ChatTurnRouteProducer>\n },\n): Promise<ChatTurnRouteProducer> {\n // Freeze scalar metadata before an asynchronous producer can change context.\n const snapshot: ChatTurnIntelligenceOptions = { ...options }\n const producer = await options.produce()\n return {\n stream: observeChatTurnStream(producer.stream, snapshot),\n finalText: () => producer.finalText(),\n get assistantParts() { return producer.assistantParts?.bind(producer) },\n get draftParts() { return producer.draftParts?.bind(producer) },\n get usage() { return producer.usage?.bind(producer) },\n get model() { return producer.model },\n get modelFailover() { return producer.modelFailover?.bind(producer) },\n get modelAttribution() { return producer.modelAttribution?.bind(producer) },\n }\n}\n"],"mappings":"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","names":["record","record","parts","record","hasUsage","record","base64","sum","written","result","sniff","sum","written","result","inlinePart","cost"]}
|
|
1
|
+
{"version":3,"sources":["../../src/chat-routes/model-failover-stream.ts","../../src/chat-routes/reasoning-clock.ts","../../src/chat-routes/sandbox-turn-usage.ts","../../src/chat-routes/sandbox-producer.ts","../../src/chat-routes/router-producer.ts","../../src/chat-routes/protected-runtime-producer.ts","../../src/chat-routes/detached-turn.ts","../../src/chat-routes/completed-sandbox-turn.ts","../../src/chat-routes/native-completion.ts","../../src/chat-routes/plan-follow-up.ts","../../src/chat-routes/durable-projection.ts","../../src/chat-routes/upload.ts","../../src/chat-routes/attachment-store.ts","../../src/chat-routes/resolve-attachments.ts","../../src/chat-routes/attachment-write-safety.ts","../../src/chat-routes/promote-file-part.ts","../../src/chat-routes/attachment-upload.ts","../../src/chat-routes/dispatch-parts.ts","../../src/chat-routes/intelligence.ts"],"sourcesContent":["/**\n * Model failover for a STREAMING turn.\n *\n * `/model-resolution`'s `runWithModelFailover` already owns the policy: walk a\n * chain, classify a resolved-or-thrown signal, re-throw a non-outage failure\n * immediately, and carry the attempt trail. This module does NOT re-implement\n * any of that — it composes it. What it adds is the one thing a whole-call\n * primitive cannot express, because a stream fails PARTWAY:\n *\n * **A turn may only fail over before its first client-visible byte.**\n *\n * Once a text delta, tool call, or ask has reached the browser (and the\n * persisted transcript), restarting on another model would duplicate the\n * answer. So each attempt is probed: open the stream, pull events into a small\n * buffer, and decide at the first meaningful event whether this model is\n * serving. Committing replays the buffer and hands the live iterator through;\n * abandoning discards the buffer (those events describe the dead model's\n * session — including its `step-finish` usage, which must never be billed) and\n * lets `runWithModelFailover` walk to the next model.\n *\n * `liveLifecycleEvents` relaxes the buffer for progress only. `start`,\n * `status`, `model-processing`, and session lifecycle carry no answer and no\n * receipt, so a product may take them the moment they arrive and show what\n * the turn is waiting on. Measured 2026-09-11 on a warm production box:\n * OpenCode reports whole parts, so the buffer held every event for 16 s and\n * the product could show nothing through provisioning and agent startup. Off\n * by default: with it on, an abandoned model's progress stays in the stream.\n *\n * The classification itself is `isUpstreamUnavailable` verbatim, so this path\n * inherits the measured facts from the 2026-07-25 outage — above all that an\n * outage is NOT always a thrown error: the sandbox RESOLVES a terminal `error`\n * event carrying `{ errorCode: 'provider_inference_unavailable' }`, which a\n * classifier inspecting only `catch` misses entirely. That resolved shape is\n * the whole reason the breakage went unnoticed, so it is classified here first.\n *\n * Conservative by construction:\n * - A terminal failure that is NOT an outage (400, bad schema, content filter)\n * COMMITS rather than failing over — it surfaces to the user exactly as it\n * does today. Those fail identically on every model; walking the chain would\n * only multiply latency and spend to reach the same error.\n * - A clean stream that produced nothing NEVER walks the chain. An empty answer\n * is not evidence of a dead upstream, and a silent re-roll on another model is\n * precisely the unattributable downgrade this work exists to prevent. Opt-in\n * `emptyTurnRetries` re-runs the SAME model instead, which leaves attribution\n * untouched — measured on production 2026-07-27, an empty turn is a transient\n * platform flake that a same-model re-run recovers (8 hard cases: 7/8\n * delivered on the first pass, 8/8 with one re-run, at a cost of 1 extra turn\n * in 9). Default `0`, so the behavior is unchanged unless a product asks.\n * - A responsive chain of length 1 costs no extra call or latency and remains\n * byte-identical to no failover. A silent chain is now deliberately bounded.\n */\n\nimport {\n isUpstreamUnavailable,\n ModelFailoverExhaustedError,\n readUpstreamUnavailableCode,\n readHttpStatusHint,\n runWithModelFailover,\n type ModelFailoverAttempt,\n} from '../model-resolution/failover'\nimport type { ModelPolicy } from '../policy-filters/index'\nimport { asRecord, asString } from '../stream/index'\n\n/** Terminal event types that end an attempt with a failure verdict. */\nconst TERMINAL_FAILURE_TYPES = new Set(['error', 'session.run.failed'])\n\n/**\n * Part types that carry no transcript content. They are buffered like anything\n * else — replayed verbatim when the attempt commits — but their presence alone\n * never commits a model, so an outage arriving right after a `step-start` is\n * still recoverable. `step-finish` matters most: it carries the abandoned\n * attempt's token/cost receipt, which must be discarded rather than billed.\n */\nconst NON_COMMITTING_PART_TYPES = new Set(['step-start', 'step-finish'])\n\n/**\n * True when `event` puts content in front of the user (or in the persisted\n * transcript), making a restart on another model unsafe.\n *\n * Deliberately an allow-list of KNOWN-INERT types rather than a deny-list: an\n * unrecognized event commits. Getting this wrong in the safe direction costs a\n * missed failover; getting it wrong the other way duplicates a user's answer.\n */\nexport function isCommittingSandboxEvent(event: unknown): boolean {\n const record = asRecord(event)\n if (!record) return false\n const type = asString(record.type) ?? ''\n if (!type) return false\n\n if (type === 'message.part.updated') {\n const part = asRecord(asRecord(record.data)?.part)\n const partType = asString(part?.type) ?? ''\n if (NON_COMMITTING_PART_TYPES.has(partType)) return false\n // A text/reasoning part with NO content yet (the platform opens the part\n // with `text: \"\"` before the first token — verbatim in the 2026-07-26\n // capture) puts nothing in front of the user; discarding it cannot\n // duplicate an answer. The first real token commits.\n if (partType === 'text' || partType === 'reasoning') {\n const text = asString(part?.text) ?? asString(part?.content) ?? ''\n return text.length > 0\n }\n return true\n }\n\n // Session/turn lifecycle, progress status, and warnings belong to the\n // attempt, not the transcript — discardable with an abandoned model.\n // Captured examples (2026-07-26): a dead model\n // emits `start`, `execution.started`, `status`×3, `session.updated`×2,\n // `warning`×2 BEFORE its terminal `error` — any one of them committing\n // would pin the turn to the dead model and kill the failover.\n if (\n type === 'start' ||\n type === 'execution.started' ||\n type === 'status' ||\n type === 'model-processing' ||\n type === 'model.processing' ||\n // Public Sandbox timing metadata: a request was attempted, not answered.\n type === 'model.first_request' ||\n type === 'session.created' ||\n type === 'session.updated' ||\n type === 'session.idle' ||\n type === 'step-start' ||\n type === 'step-finish' ||\n type === 'turn' ||\n type === 'warning'\n ) {\n return false\n }\n\n // Everything else — text/tool shapes the producer folds, asks, plans,\n // `result`/`done`, and any type this shell does not recognize — commits.\n return true\n}\n\n/**\n * Condense an abandoned attempt's raw failure text into something safe to show\n * a customer in the transcript.\n *\n * Measured on the live router 2026-07-27 19:40 UTC: an edge 5xx arrives as\n * Cloudflare's full HTML error PAGE, so the verbatim reason began\n * `<!DOCTYPE html>\\n<!--[if lt IE 7]> <html class=\"no-js ie6 oldie\"…` and the\n * fallback notice pasted 200 bytes of that markup into the answer the customer\n * reads. The cause is worth stating; the markup is not.\n *\n * Only the DISPLAY string is condensed. The full text stays on\n * `modelFailover.attempts[].reason` for the operator, so nothing is lost —\n * this narrows what the customer sees, never what telemetry records.\n */\nexport function summarizeFailoverReason(reason: string): string {\n const collapsed = reason.replace(/\\s+/g, ' ').trim()\n if (!collapsed) return 'upstream unavailable'\n\n // An HTML error page carries no message worth quoting — the status is the\n // whole content. Detected on the ORIGINAL text, before collapsing, because\n // that is the form the producer hands over.\n if (/<!doctype html|<html[\\s>]/i.test(collapsed)) {\n const status = readHttpStatusHint(collapsed)\n return status ? `HTTP ${status}` : 'upstream returned an error page'\n }\n\n // Anything else is a real message. Cap it so a verbose upstream cannot push\n // the answer off the screen, and cut on a word boundary when one is near.\n const LIMIT = 160\n if (collapsed.length <= LIMIT) return collapsed\n const clipped = collapsed.slice(0, LIMIT)\n const lastSpace = clipped.lastIndexOf(' ')\n return `${(lastSpace > LIMIT - 30 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}…`\n}\n\n/** A terminal failure event, classified. `outage` decides failover vs surface. */\ninterface TerminalFailure {\n outage: boolean\n reason: string\n code?: string\n}\n\n/**\n * Classify a terminal failure event. Returns `null` for any non-terminal event.\n *\n * The RESOLVED shape is checked first and deliberately: the sandbox reports an\n * upstream outage by resolving `{ success: false, errorCode:\n * 'provider_inference_unavailable' }` inside a terminal `error` event's `data`,\n * never by throwing. Both `data` and the whole record are offered to\n * `isUpstreamUnavailable` so a payload nested either way is caught.\n */\n/**\n * Progress the consumer may see before the commit point: it names no model\n * output and carries no billing receipt, so streaming it live cannot duplicate\n * an answer or bill an abandoned attempt. `warning` and every\n * `message.part.updated` (including `step-start`/`step-finish`) stay buffered.\n */\nexport function isLiveLifecycleEvent(event: unknown): boolean {\n const type = asString(asRecord(event)?.type) ?? ''\n return (\n type === 'start' ||\n type === 'execution.started' ||\n type === 'status' ||\n type === 'model-processing' ||\n type === 'model.processing' ||\n // Public Sandbox timing metadata: a request was attempted, not answered.\n type === 'model.first_request' ||\n type === 'session.created' ||\n type === 'session.updated' ||\n type === 'session.idle'\n )\n}\n\nexport function classifyTerminalFailure(event: unknown): TerminalFailure | null {\n const record = asRecord(event)\n if (!record) return null\n const type = asString(record.type) ?? ''\n if (!TERMINAL_FAILURE_TYPES.has(type)) return null\n\n const data = asRecord(record.data)\n const outage = isUpstreamUnavailable(data) || isUpstreamUnavailable(record)\n const reason =\n asString(data?.message) ??\n asString(data?.error) ??\n asString(data?.reason) ??\n asString(record.message) ??\n `sandbox stream reported ${type}`\n const code =\n readUpstreamUnavailableCode(data) ??\n asString(data?.errorCode) ??\n asString(data?.code) ??\n readUpstreamUnavailableCode(record) ??\n asString(record.errorCode) ??\n asString(record.code)\n return { outage, reason, ...(code ? { code } : {}) }\n}\n\n/** A model that served: its buffered preamble plus the still-live iterator. */\ninterface AttemptCommit {\n committed: true\n buffered: unknown[]\n /** `null` when the stream already ended during the probe. */\n iterator: AsyncIterator<unknown> | null\n /** Cancel transport work when this attempt is drained or abandoned. */\n abort: () => void\n}\n\n/**\n * A model whose upstream is down. `error`/`errorCode` are read by\n * `runWithModelFailover`'s attempt-trail describe(), so the trail names the\n * real upstream cause rather than a wrapper's own words.\n */\ninterface AttemptOutage {\n committed: false\n error: string\n errorCode?: string\n timeoutCode?: ModelAttemptTimeoutCode\n}\n\ntype AttemptOutcome = AttemptCommit | AttemptOutage\n\n/** Open the raw turn stream for one specific model. */\nexport type OpenModelStream = (args: {\n model: string\n /** 1 for the preferred model, 2 for the first fallback, and so on. */\n attempt: number\n /** Aborted when this attempt times out, is abandoned, or finishes. Forward\n * this to `streamSandboxPrompt(..., { signal })` so a blocked transport is\n * cancelled immediately rather than only receiving iterator.return(). */\n signal: AbortSignal\n}) => AsyncIterable<unknown> | Promise<AsyncIterable<unknown>>\n\n/** Fired when a model is abandoned and the next one is about to be tried. */\nexport interface ModelFallbackInfo {\n from: string\n to: string\n reason: string\n}\n\n/** Define inputs for streaming a turn across a model failover chain */\nexport interface ModelFailoverStreamOptions {\n /** Preferred model first, then fallbacks in descending preference. */\n models: readonly string[]\n open: OpenModelStream\n /**\n * Product admission policy applied to EVERY chain member before its stream\n * is opened — see `runWithModelFailover`'s `policy`. A member the policy\n * refuses is skipped (never opened, never sent the turn's data) and the walk\n * continues; an all-refused chain ends in `ModelFailoverExhaustedError`.\n */\n policy?: ModelPolicy\n /**\n * Maximum time to open/start one model's event source, including its first\n * iterator pull. Default 120 seconds, matching the sandbox provisioning\n * ceiling so cold infrastructure startup is bounded independently from\n * provider inference.\n */\n openTimeoutMs?: number\n /**\n * Hard deadline from the source's first lifecycle event to its first\n * answer-bearing event. Default 60 seconds. Processing/lifecycle events do\n * not commit the attempt and do not extend this deadline.\n */\n firstResponseTimeoutMs?: number\n /** Override the commit-point rule. Default {@link isCommittingSandboxEvent}. */\n isCommitting?: (event: unknown) => boolean\n /**\n * Stream {@link isLiveLifecycleEvent} progress to the consumer as it arrives\n * instead of holding it until the attempt commits. Default `false`, which\n * keeps an abandoned model's events entirely off the wire. With it on, such\n * an event is never replayed from the buffer, and one from an abandoned\n * attempt stays in the stream — it describes the sandbox, not the answer.\n */\n liveLifecycleEvents?: boolean\n onFallback?: (info: ModelFallbackInfo) => void\n /**\n * How many times to RE-RUN THE SAME MODEL when a turn completes having\n * produced no assistant text at all. Default `0` — byte-identical to no\n * retry.\n *\n * This is deliberately not a chain walk. Falling over to a different model\n * on an empty answer is the unattributable downgrade this module refuses to\n * do; re-running the SAME model changes nothing about attribution, because\n * the model that serves is the model that was asked for.\n *\n * Bounded by the same commit rule as failover: only a turn whose ONLY\n * committing event is a terminal receipt with no text is retried, so nothing\n * that reached the user can ever be produced twice.\n */\n emptyTurnRetries?: number\n /** Fired when an empty turn is discarded and the same model re-run. */\n onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** One same-model re-run of a turn that completed with no assistant text. */\nexport interface EmptyTurnRetryInfo {\n model: string\n /** 1 for the first re-run. */\n retry: number\n /** How many re-runs remain after this one. */\n remaining: number\n}\n\n/** The failover-wrapped stream plus the attribution every consumer needs. */\nexport interface ModelFailoverStreamHandle {\n events: AsyncGenerator<unknown, void, unknown>\n /** The model that actually served. `undefined` until the first pull resolves it. */\n servingModel(): string | undefined\n /** Every model tried, in order, with the reason each was abandoned. */\n attempts(): ModelFailoverAttempt[]\n /** True when the preferred model did not serve — the attributability signal. */\n usedFallback(): boolean\n}\n\n/** Default ceiling for opening/starting one source through its first event. */\nexport const DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS = 120_000\n\n/** Default ceiling for the first answer-bearing event after the source's first event. */\nexport const DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS = 60_000\n\n/** Structured timeout codes surfaced by the producer on final exhaustion. */\nexport type ModelAttemptTimeoutCode =\n | 'model_stream_open_timeout'\n | 'provider_first_response_timeout'\n\n/** Every configured model was exhausted and the final one timed out. */\nexport class ModelFailoverTimeoutError extends ModelFailoverExhaustedError {\n readonly code: ModelAttemptTimeoutCode\n readonly model: string\n readonly timeoutMs: number\n\n constructor(input: {\n attempts: ModelFailoverAttempt[]\n code: ModelAttemptTimeoutCode\n model: string\n timeoutMs: number\n }) {\n super(input.attempts)\n this.name = 'ModelFailoverTimeoutError'\n this.code = input.code\n this.model = input.model\n this.timeoutMs = input.timeoutMs\n this.message = input.code === 'model_stream_open_timeout'\n ? `Opening the model event source for ${input.model} exceeded ${input.timeoutMs}ms.`\n : `Model ${input.model} produced no first answer-bearing event within ${input.timeoutMs}ms.`\n }\n}\n\nfunction resolveTimeoutMs(value: number | undefined, fallback: number, label: string): number {\n if (value === undefined) return fallback\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`${label} must be a finite number greater than 0`)\n }\n return Math.max(1, Math.trunc(value))\n}\n\nconst TIMED_OUT = Symbol('model-attempt-timed-out')\n\nfunction deadlineAfter(timeoutMs: number): {\n promise: Promise<typeof TIMED_OUT>\n clear: () => void\n} {\n let timer: ReturnType<typeof setTimeout> | undefined\n const promise = new Promise<typeof TIMED_OUT>((resolve) => {\n timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs)\n })\n return {\n promise,\n clear: () => {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n },\n }\n}\n\n/**\n * True when `event` is a terminal receipt (`result` / `done`) carrying no\n * assistant text.\n *\n * Measured on production `sandbox.tangle.tools` (2026-07-27, 28 turns through\n * the gtm-agent profile): a turn can end `{ outcome: { type: 'completed' } }`\n * with `finalText: ''` and zero token events — the platform reports success and\n * the customer's message is blank. It is not an outage, nothing throws, and\n * `isUpstreamUnavailable` correctly declines to classify it, so before this the\n * blank answer committed and shipped.\n */\nfunction isEmptyTerminalReceipt(event: unknown): boolean {\n const record = asRecord(event)\n if (!record) return false\n const type = asString(record.type) ?? ''\n if (type !== 'result' && type !== 'done') return false\n const data = asRecord(record.data)\n const text = asString(data?.finalText) ?? asString(data?.text) ?? ''\n return text.trim().length === 0\n}\n\n/**\n * Hard ceiling on same-model re-runs. A turn that comes back blank three times\n * running is not a flake this can retry away, and each pass costs a full\n * sandbox turn — so the budget is capped rather than trusted.\n */\nexport const MAX_EMPTY_TURN_RETRIES = 3\n\n/**\n * Coerce the caller's budget to a finite, bounded, non-negative integer.\n *\n * `Math.trunc(NaN)` is `NaN` and `retry >= NaN` is false for every `retry`, so\n * a naive clamp turns a bad config value into a loop that opens sandbox streams\n * until the worker dies. `Infinity` has the same shape. Both resolve to `0` —\n * an unusable budget disables the retry rather than running unbounded.\n */\nexport function resolveEmptyTurnRetries(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return 0\n const truncated = Math.trunc(value)\n if (truncated <= 0) return 0\n return Math.min(truncated, MAX_EMPTY_TURN_RETRIES)\n}\n\n/** Abandon a dead attempt's iterator. Never allowed to mask the outage. */\nasync function closeIterator(iterator: AsyncIterator<unknown>, log?: ModelFailoverStreamOptions['log']): Promise<void> {\n try {\n await iterator.return?.()\n } catch (err) {\n log?.('[chat-routes] abandoning a failed model stream threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n}\n\n/**\n * Wrap `open` in reactive model failover, streaming from the first model in\n * `models` that reaches its commit point.\n *\n * Zero added latency on the happy path: the preferred model is opened first and,\n * the moment it emits anything meaningful, its events flow straight through.\n *\n * @throws ModelFailoverTimeoutError when the final model times out,\n * ModelFailoverExhaustedError when every model's upstream is down, and\n * re-throws a non-outage error from the FIRST model without walking the\n * chain (the latter behaviors are inherited from\n * `runWithModelFailover`).\n */\nexport function streamWithModelFailover(\n options: ModelFailoverStreamOptions,\n): ModelFailoverStreamHandle {\n const committing = options.isCommitting ?? isCommittingSandboxEvent\n const emptyTurnRetries = resolveEmptyTurnRetries(options.emptyTurnRetries)\n const openTimeoutMs = resolveTimeoutMs(\n options.openTimeoutMs,\n DEFAULT_MODEL_STREAM_OPEN_TIMEOUT_MS,\n 'openTimeoutMs',\n )\n const firstResponseTimeoutMs = resolveTimeoutMs(\n options.firstResponseTimeoutMs,\n DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS,\n 'firstResponseTimeoutMs',\n )\n const liveLifecycle = options.liveLifecycleEvents === true\n const progress = createProgressQueue()\n let serving: string | undefined\n let trail: ModelFailoverAttempt[] = []\n let fellBack = false\n let attemptIndex = 0\n let finalTimeout: {\n code: ModelAttemptTimeoutCode\n model: string\n timeoutMs: number\n } | undefined\n\n /**\n * One open-and-drain pass. `empty` marks the pass as an EMPTY TURN — the\n * only committing event was a terminal receipt with no assistant text, so\n * nothing reached the user and the pass can be discarded without any risk of\n * producing an answer twice. It still carries the outcome it would otherwise\n * have returned, so exhausting the retry budget is byte-identical to no\n * retry at all.\n */\n const drainOnce = async (model: string): Promise<{ outcome: AttemptOutcome; empty: boolean }> => {\n attemptIndex += 1\n const controller = new AbortController()\n const abort = (): void => {\n if (!controller.signal.aborted) controller.abort()\n }\n const opening = Promise.resolve().then(() => options.open({\n model,\n attempt: attemptIndex,\n signal: controller.signal,\n }))\n const openDeadline = deadlineAfter(openTimeoutMs)\n let opened: AsyncIterable<unknown> | typeof TIMED_OUT\n try {\n opened = await Promise.race([opening, openDeadline.promise])\n } catch (err) {\n openDeadline.clear()\n abort()\n throw err\n }\n if (opened === TIMED_OUT) {\n openDeadline.clear()\n abort()\n // A Promise cannot be forcibly cancelled. If the abandoned open settles\n // later, close its iterator immediately so it cannot leak a connection.\n void opening.then(\n (lateSource) => {\n try {\n void closeIterator(lateSource[Symbol.asyncIterator](), options.log)\n } catch (err) {\n options.log?.('[chat-routes] closing a late model stream threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n },\n (err) => {\n options.log?.('[chat-routes] abandoned model stream open rejected after timeout', {\n error: err instanceof Error ? err.message : String(err),\n })\n },\n )\n return {\n outcome: {\n committed: false,\n error: `Opening the model event source for ${model} exceeded ${openTimeoutMs}ms`,\n errorCode: 'model_stream_open_timeout',\n timeoutCode: 'model_stream_open_timeout',\n },\n empty: false,\n }\n }\n\n const source = opened\n let iterator: AsyncIterator<unknown>\n try {\n iterator = source[Symbol.asyncIterator]()\n } catch (err) {\n openDeadline.clear()\n abort()\n throw err\n }\n const buffered: unknown[] = []\n // Starting the source includes its first pull. `streamSandboxPrompt` is an\n // async generator, so its cold-start work does not execute until `next()`;\n // counting that time as provider inference would collapse the two phases.\n let next: IteratorResult<unknown> | typeof TIMED_OUT\n try {\n next = await Promise.race([\n Promise.resolve(iterator.next()),\n openDeadline.promise,\n ])\n } catch (err) {\n openDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n openDeadline.clear()\n if (next === TIMED_OUT) {\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: `Starting the model event source for ${model} exceeded ${openTimeoutMs}ms`,\n errorCode: 'model_stream_open_timeout',\n timeoutCode: 'model_stream_open_timeout',\n },\n empty: false,\n }\n }\n if (next.done) {\n abort()\n return { outcome: { committed: true, buffered, iterator: null, abort }, empty: true }\n }\n\n // One deadline for the WHOLE provider pre-response phase. Pulling a\n // lifecycle or processing event races the same promise; liveness cannot\n // reset the clock.\n const firstResponseDeadline = deadlineAfter(firstResponseTimeoutMs)\n\n for (;;) {\n const event = next.value\n let failure: TerminalFailure | null\n let commits = false\n try {\n failure = classifyTerminalFailure(event)\n commits = !failure && committing(event)\n } catch (err) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n if (failure?.outage) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: failure.reason,\n ...(failure.code ? { errorCode: failure.code } : {}),\n },\n empty: false,\n }\n }\n\n if (liveLifecycle && !failure && !commits && isLiveLifecycleEvent(event)) {\n progress.push(event)\n } else {\n buffered.push(event)\n }\n // A terminal NON-outage failure surfaces exactly as it does today.\n if (failure) {\n firstResponseDeadline.clear()\n return { outcome: { committed: true, buffered, iterator, abort }, empty: false }\n }\n if (commits) {\n firstResponseDeadline.clear()\n return { outcome: { committed: true, buffered, iterator, abort }, empty: isEmptyTerminalReceipt(event) }\n }\n\n // A THROWN failure propagates untouched: `runWithModelFailover` classifies\n // it (outage → next model, anything else → re-thrown to the caller).\n try {\n next = await Promise.race([\n Promise.resolve(iterator.next()),\n firstResponseDeadline.promise,\n ])\n } catch (err) {\n firstResponseDeadline.clear()\n abort()\n void closeIterator(iterator, options.log)\n throw err\n }\n if (next === TIMED_OUT) {\n firstResponseDeadline.clear()\n // Do not await `return()`: an async generator blocked inside `next()`\n // may not settle its return until that pull completes. Invoking it is\n // the cancellation signal; waiting for it would recreate the hang.\n abort()\n void closeIterator(iterator, options.log)\n return {\n outcome: {\n committed: false,\n error: `Model ${model} produced no first answer-bearing event within ${firstResponseTimeoutMs}ms`,\n errorCode: 'provider_first_response_timeout',\n timeoutCode: 'provider_first_response_timeout',\n },\n empty: false,\n }\n }\n if (next.done) {\n firstResponseDeadline.clear()\n abort()\n // Clean end with nothing committing. Not an outage — but also not an\n // answer, so it is re-runnable on the same model.\n return { outcome: { committed: true, buffered, iterator: null, abort }, empty: true }\n }\n }\n }\n\n const probe = async (model: string): Promise<AttemptOutcome> => {\n // Only the FINAL attempted model decides the exhaustion code. Clear a\n // prior model's timeout before this model can throw a different outage.\n finalTimeout = undefined\n for (let retry = 0; ; retry += 1) {\n const pass = await drainOnce(model)\n if (!pass.empty || retry >= emptyTurnRetries) {\n if (!pass.outcome.committed && pass.outcome.timeoutCode) {\n finalTimeout = {\n code: pass.outcome.timeoutCode,\n model,\n timeoutMs: pass.outcome.timeoutCode === 'model_stream_open_timeout'\n ? openTimeoutMs\n : firstResponseTimeoutMs,\n }\n } else {\n finalTimeout = undefined\n }\n return pass.outcome\n }\n // Discard the empty pass entirely — including its `step-finish` token\n // receipt, which must not be billed — and re-run the same model.\n const iterator = pass.outcome.committed ? pass.outcome.iterator : null\n if (pass.outcome.committed) pass.outcome.abort()\n if (iterator) void closeIterator(iterator, options.log)\n const info: EmptyTurnRetryInfo = { model, retry: retry + 1, remaining: emptyTurnRetries - retry - 1 }\n options.log?.('[chat-routes] turn completed with no assistant text; re-running the same model', {\n ...info,\n })\n options.onEmptyTurnRetry?.(info)\n }\n }\n\n const events = (async function* (): AsyncGenerator<unknown, void, unknown> {\n let handle: AttemptCommit\n // Progress arrives while the probe is still running. The queue closes only\n // after the verdict settles, so every event pushed before it is delivered\n // in order and ahead of the replayed buffer.\n const settled = runWithModelFailover<AttemptOutcome>({\n models: options.models,\n run: probe,\n ...(options.policy ? { policy: options.policy } : {}),\n // The probe has already classified the raw payload with\n // `isUpstreamUnavailable`; this reads its verdict rather than\n // re-classifying a wrapper object, so the two can never disagree.\n isUnavailableResult: (result) => result.committed === false,\n onFallback: (attempt, nextModel) => {\n const info: ModelFallbackInfo = {\n from: attempt.model,\n to: nextModel,\n reason: attempt.reason ?? 'upstream unavailable',\n }\n options.log?.('[chat-routes] model upstream unavailable; falling over', { ...info })\n options.onFallback?.(info)\n },\n }).finally(() => progress.close())\n // The verdict is read after the drain; mark the rejection handled until then.\n settled.catch(() => undefined)\n for (;;) {\n const item = await progress.pull()\n if (item === PROGRESS_CLOSED) break\n yield item\n }\n try {\n const outcome = await settled\n serving = outcome.model\n trail = outcome.attempts\n fellBack = outcome.usedFallback\n handle = outcome.value as AttemptCommit\n } catch (err) {\n // Exhaustion carries the full trail; keep it so the receipt still names\n // every model tried even though the turn produced nothing.\n const attempts = (err as { attempts?: ModelFailoverAttempt[] })?.attempts\n if (Array.isArray(attempts)) trail = attempts\n if (err instanceof ModelFailoverExhaustedError && finalTimeout) {\n throw new ModelFailoverTimeoutError({\n attempts: trail,\n ...finalTimeout,\n })\n }\n throw err\n }\n\n for (const event of handle.buffered) yield event\n\n const live = handle.iterator\n if (!live) {\n handle.abort()\n return\n }\n try {\n for (;;) {\n const next = await live.next()\n if (next.done) return\n yield next.value\n }\n } finally {\n // The consumer may abandon this generator mid-turn (client drop); close\n // the underlying stream rather than leaking it.\n handle.abort()\n await closeIterator(live, options.log)\n }\n })()\n\n return {\n events,\n servingModel: () => serving,\n attempts: () => trail,\n usedFallback: () => fellBack,\n }\n}\n\nconst PROGRESS_CLOSED: unique symbol = Symbol('progress-closed')\n\n/**\n * FIFO handoff from the probe (a plain promise chain) to the event generator.\n * `close` is idempotent and queues one sentinel behind everything pushed\n * before it, so a consumer never loses a progress event to the verdict.\n */\nfunction createProgressQueue(): {\n push(event: unknown): void\n close(): void\n pull(): Promise<unknown>\n} {\n const items: unknown[] = []\n const waiters: Array<(value: unknown) => void> = []\n let closed = false\n const deliver = (value: unknown): void => {\n const waiter = waiters.shift()\n if (waiter) waiter(value)\n else items.push(value)\n }\n return {\n push(event) {\n if (closed) return\n deliver(event)\n },\n close() {\n if (closed) return\n closed = true\n deliver(PROGRESS_CLOSED)\n },\n pull() {\n if (items.length > 0) return Promise.resolve(items.shift())\n if (closed) return Promise.resolve(PROGRESS_CLOSED)\n return new Promise((resolve) => waiters.push(resolve))\n },\n }\n}\n","/**\n * When each reasoning run of a turn started and ended, so every chat producer\n * persists one reasoning part shape: `{ type: 'reasoning', text, time: { start, end } }`\n * (`ChatReasoningPart` in `/chat-store`).\n *\n * The engine (`./router-producer`) and the sandbox (`./sandbox-producer`) both\n * stream reasoning live as `{ type: 'reasoning', text }` deltas. This clock gives\n * their persisted parts the same timing: a run starts at its first delta and\n * ends when anything else streams (answer text, a tool) or the turn ends. A\n * harness that timed its own part keeps its times; the clock fills only what it\n * left out. `/web-react` reads the result back (`chatReasoningFromParts`), so a\n * reloaded turn shows how long the model thought.\n *\n * Import-free, so the browser-safe chat stream can share the part's type.\n */\n\n/** Start and end, in epoch milliseconds, of one reasoning run. */\nexport interface ReasoningRunTime {\n start?: number\n end?: number\n}\n\n/** The clock one producer keeps for one turn. `K` is how the producer names a run: a part key or the part itself. */\nexport interface ReasoningClock<K> {\n /** A reasoning delta for `run` streamed. Starts its clock on the first; ends any other open run. */\n reasoning(run: K): void\n /** Something other than reasoning streamed, or the turn ended: the open run is over, at `at` (default now). */\n settle(at?: number): void\n /** The run's time, the part's own `start` and `end` winning where it has them. */\n time(run: K, own?: ReasoningRunTime): ReasoningRunTime | undefined\n}\n\n/** A clock for one turn's reasoning runs. */\nexport function createReasoningClock<K>(now: () => number = Date.now): ReasoningClock<K> {\n const runs = new Map<K, ReasoningRunTime>()\n let open: { run: K } | null = null\n const settle = (at?: number) => {\n if (!open) return\n const time = runs.get(open.run)\n if (time && time.end === undefined) time.end = Math.max(time.start ?? 0, at ?? now())\n open = null\n }\n return {\n reasoning(run) {\n if (open && open.run === run) return\n settle()\n const time = runs.get(run)\n // The same run resumed after an interruption: it is open again.\n if (time) delete time.end\n else runs.set(run, { start: now() })\n open = { run }\n },\n settle,\n time(run, own) {\n const time = runs.get(run)\n if (!time) return own\n const start = own?.start ?? time.start\n const end = own?.end ?? time.end\n return { ...(start !== undefined ? { start } : {}), ...(end !== undefined ? { end } : {}) }\n },\n }\n}\n","import { asRecord, type JsonRecord } from '../stream/index'\nimport type { ChatTurnUsage } from './turn-routes'\n\n/** Add one canonical Sandbox `step-finish` receipt to a turn total. */\nexport function addStepFinishUsage(part: JsonRecord, usage: ChatTurnUsage): void {\n const tokens = asRecord(part.tokens)\n if (tokens) {\n const cache = asRecord(tokens.cache)\n const add = (current: number | undefined, value: unknown): number | undefined => {\n const n = Number(value)\n if (!Number.isFinite(n)) return current\n return (current ?? 0) + n\n }\n usage.inputTokens = add(usage.inputTokens, tokens.input)\n usage.outputTokens = add(usage.outputTokens, tokens.output)\n usage.reasoningTokens = add(usage.reasoningTokens, tokens.reasoning)\n if (cache) {\n usage.cacheReadTokens = add(usage.cacheReadTokens, cache.read)\n usage.cacheWriteTokens = add(usage.cacheWriteTokens, cache.write)\n }\n }\n const cost = Number(part.cost)\n if (Number.isFinite(cost)) usage.costUsd = (usage.costUsd ?? 0) + cost\n}\n","/**\n * Sandbox lane: bridge a raw sandbox event stream (`streamSandboxPrompt`) into\n * the `ChatTurnProducer` shape agent-runtime's `handleChatTurn` consumes AND\n * the client vocabulary `/web-react`'s `dispatchChatStreamLine` already parses\n * (`text` / `reasoning` / `tool_call` / `tool_result` / `usage` /\n * `notice` / structured `error` / `interaction`). Legal and tax each hand-rolled\n * this mapping differently;\n * this is that middle, composed from `/stream`'s normalizers — no new loop\n * logic, no SDK import (the event source is an injected `AsyncIterable`).\n *\n * Alongside the live mapping it accumulates the PERSISTED projection — the\n * `message.parts` rows `/chat-store` stores — via `normalizePersistedPart` /\n * `mergePersistedPart` / `finalizeAssistantParts`, plus the usage receipt from\n * `step-finish` parts. `createChatTurnRoutes` reads both after drain.\n */\n\nimport {\n cancelStatusFor,\n interactionPartKey,\n interactionToPersistedPart,\n isRenderableInteractionKind,\n noticePart,\n noticePartKey,\n parseInteractionCancel,\n parseInteractionRequest,\n type InteractionRequestWire,\n} from '../interactions/contract'\nimport {\n parsePlanSubmittedEvent,\n planToPersistedPart,\n} from '../plans/index'\nimport {\n asRecord,\n asString,\n draftAssistantParts,\n finalizeAssistantParts,\n finalizePendingInteractionParts,\n getPartKey,\n mergePersistedPart,\n normalizePersistedPart,\n normalizeToolEvent,\n terminalizeDanglingAssistantToolUpdates,\n type JsonRecord,\n type StreamEvent,\n} from '../stream/index'\nimport {\n buildModelChain,\n ModelFailoverExhaustedError,\n type ModelFailoverAttempt,\n} from '../model-resolution/failover'\nimport type { ModelPolicy } from '../policy-filters/index'\nimport {\n ModelFailoverTimeoutError,\n resolveEmptyTurnRetries,\n streamWithModelFailover,\n summarizeFailoverReason,\n type EmptyTurnRetryInfo,\n type ModelFallbackInfo,\n type ModelFailoverStreamHandle,\n type OpenModelStream,\n} from './model-failover-stream'\nimport { createReasoningClock, type ReasoningRunTime } from './reasoning-clock'\nimport type {\n ChatTurnModelAttribution,\n ChatTurnRouteProducer,\n ChatTurnUsage,\n} from './turn-routes'\nimport { addStepFinishUsage } from './sandbox-turn-usage'\nimport type { ProducerWireEvent } from './wire'\n\n/** Outcome of a `promoteFilePart` attempt. `key`, when given, becomes the\n * persisted part's row key (e.g. `attachment:<path>`) so repeat promotions\n * of the same underlying file fold into one segment instead of appending;\n * omitted, the default `getPartKey` keying applies.\n *\n * On failure, `part` is an OPTIONAL substitute part to persist in place of\n * the raw url-bearing one — this is how a product swaps in a transcript\n * notice (gtm persists a `warning` notice part, never the transient url) for\n * a failed promotion instead of baking a `data:`/sandbox-path url into the\n * durable row. When `part` is present it is persisted (via the same\n * `recordPersistedPart` path as a success, honoring the optional `key`);\n * when absent, the existing raw-part fallback applies unchanged — so a\n * caller that only returns `{ succeeded: false, reason }` keeps today's\n * behavior verbatim. */\nexport type FilePartPromotionOutcome =\n | { succeeded: true; part: Record<string, unknown>; key?: string }\n | { succeeded: false; reason: string; part?: Record<string, unknown>; key?: string }\n\n/** Define options for producing sandbox chat events with rendering and interaction controls */\nexport interface SandboxChatProducerOptions {\n /** The raw sandbox event stream (e.g. `streamSandboxPrompt(...)`).\n *\n * An ALREADY-OPEN stream is bound to one model and cannot be reopened, so\n * this form can never fail over. Prefer {@link openEvents}; exactly one of\n * the two is required. */\n events?: AsyncIterable<unknown>\n /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form\n * of {@link events}. The callback receives the model to run and returns the\n * same `AsyncIterable` `events` would have been (typically\n * `streamSandboxPrompt(shell, box, prompt, { ...opts, model })`).\n *\n * Wiring this turns failover ON with no further flag: whenever the resolved\n * chain (`model` + {@link fallbackModels}) holds more than one entry, a model\n * whose upstream is dead is abandoned BEFORE its first client-visible byte\n * and the next one is tried. A one-entry chain opens exactly once — no extra\n * call, no added latency, byte-identical to today.\n *\n * Requires {@link model}: failover has to know which model it is running. */\n openEvents?: OpenModelStream\n /** Recorded on the persisted assistant message. When failover moves the turn\n * to another model, the producer's `model` reports the model that ACTUALLY\n * served, not this preferred one — see {@link modelFailover}. */\n model?: string\n /** Models to try, in order, when `model`'s upstream is dead. Product config,\n * never a shell default: agent-app cannot know which ids are live, and a\n * baked list would rot into exactly the stale-liveness bug this guards\n * against. Empty/omitted → one attempt, today's behavior.\n *\n * Pick these deliberately. A same-family fallback is NOT automatically safe:\n * `gemini-2.5-flash` produced zero persisted deliverables in 2 of 3 measured\n * runs on a med-legal filing flow where `gemini-2.5-pro` succeeded. That is\n * why every fallback is surfaced (persisted `model`, a transcript notice, and\n * `modelFailover()`) instead of being applied silently. */\n fallbackModels?: readonly string[]\n /** Opt out of failover while still using {@link openEvents}. `false` collapses\n * the chain to `model` alone. */\n modelFailover?: false\n /**\n * Product admission policy applied to every chain member — the RESOLVED\n * preferred model AND every fallback — before its stream is opened. This is\n * the execution-time half of policy enforcement: it covers the model\n * whatever its source (request body, env, product default), and it is what\n * stops a compliant model's mid-turn 502 from re-running the turn's data\n * through a non-compliant fallback. A refused member is skipped with the\n * refusal recorded in `modelFailover().attempts`; an all-refused chain\n * exhausts. Omit → availability only (today's behavior).\n */\n modelPolicy?: ModelPolicy\n /** Maximum time to open/start one model event source through its first event.\n * Default 120 seconds. This is separate from provider first-output latency\n * so cold sandbox startup is not mislabeled as an inference failure. */\n openTimeoutMs?: number\n /** Hard deadline after the source's first lifecycle event for the first\n * answer-bearing event. Default 60 seconds. Processing/lifecycle events do\n * not extend it. */\n firstResponseTimeoutMs?: number\n /** Forward `start`/`status`/`model-processing`/session lifecycle to the\n * client as it arrives instead of after the commit point, so a product can\n * show what a turn is waiting on. Default `false`. An abandoned model's\n * progress then stays on the wire; its answer and receipt never do. */\n liveLifecycleEvents?: boolean\n /** Fired when a model is abandoned mid-chain (telemetry/alerting). The user\n * already sees a transcript notice; this is for the operator. */\n onModelFallback?: (info: ModelFallbackInfo) => void\n /** Re-run the SAME model this many times when a turn completes with no\n * assistant text at all. Default `0` (unchanged behavior). Requires\n * {@link openEvents} — there is nothing to re-open on a fixed stream.\n *\n * Distinct from {@link fallbackModels} on purpose: this never changes which\n * model answers, so it carries none of the attribution risk a downgrade\n * does. Measured on production 2026-07-27 through gtm-agent's profile, a\n * completed-but-blank turn is a transient platform flake — 8 hard cases went\n * 7/8 delivered to 8/8 with one re-run, costing 1 extra turn in 9. */\n emptyTurnRetries?: number\n /** Fired when a blank turn is discarded and the same model re-run. */\n onEmptyTurnRetry?: (info: EmptyTurnRetryInfo) => void\n /** Which ask kinds the product renders a card for. Anything else is\n * auto-declined (see `declineInteraction`) so the run never hangs in the\n * broker waiting on a card no client will show. Default: question/plan.\n * Products with per-turn plan mode can close over it without another option:\n * `(kind) => kind === 'question' || (kind === 'plan' && planEnabled)`.\n * The parsed request lets a product decline an ask by its content, such\n * as a question that duplicates a decision the product already shows. */\n isRenderableInteraction?: (kind: string, request: InteractionRequestWire) => boolean\n /** The transcript notice for an auto-declined ask; defaults to a policy line naming its kind. */\n declinedNotice?: (request: InteractionRequestWire, declineFailed: boolean) => string\n /** Resolve a non-renderable ask (wire `respondToSessionInteraction` with the\n * session's sidecar connection). Without it, a failure notice is emitted and\n * the run stays blocked until the broker times out. */\n declineInteraction?: (id: string) => Promise<void>\n /** Opt-in eager promotion of harness-emitted `file` parts. Unset, a `file`\n * part persists exactly as the harness sent it — a transient `url` (a\n * `data:` URI or in-sandbox path) baked into the transcript, which is\n * today's behavior and stays byte-identical if this is never wired. Set,\n * EVERY `file` part (never `image`, never any other kind) is routed\n * through this callback instead of `recordPersistedPart`'s default\n * fallback — including a part with NEITHER `id` NOR `url` (gtm always\n * attempts promotion; such a part simply fails \"carries no url\" and\n * resolves through the same failure path as any other rejection, rather\n * than being persisted raw and unpromoted) — so the product can durably\n * write the bytes and swap in a path-bearing part before the raw url ever\n * reaches the persisted transcript. Keyed per source-prefixed `id:<id>` /\n * `url:<url>` (an `id` and a `url` sharing the same text must never collide\n * onto one memo entry) and memoized by PROMISE (not result), so re-emitted\n * snapshot events for the same part —\n * the harness resends the whole part on every update, not just deltas —\n * fold onto the one in-flight or settled attempt rather than promoting\n * twice or racing two concurrent writes; a raw part with neither `id` nor\n * `url` cannot be keyed, so it is invoked UN-memoized (once per event) —\n * each occurrence is its own attempt. A rejecting promise is caught,\n * logged via `log`, and treated as `succeeded: false`. On `succeeded:\n * false` the outcome's optional `part` (a substitute — e.g. a warning\n * notice — see {@link FilePartPromotionOutcome}) persists in its place when\n * given; otherwise the raw part persists exactly as it does today — this\n * seam only decides whether to call the promoter and what to do with its\n * outcomes; the promotion mechanics (vault write, key derivation, notice\n * construction) live in the caller's callback, not here. */\n promoteFilePart?: (raw: JsonRecord) => Promise<FilePartPromotionOutcome>\n /** Append terminal error explanations to assistant text. Default true.\n * Set false when the application renders the structured error separately.\n * Error events, diagnostics, partial answers and tool settlement remain available. */\n includeErrorText?: boolean\n /** Record the terminal error explanation as an `error` notice part (persisted\n * on the assistant row and streamed as a `notice` event) instead of appending\n * it to the assistant text. A reloaded failed turn then carries a typed marker\n * a client renders in the danger tone, rather than prose indistinguishable\n * from the answer. Default false (today's text behavior). When true, the\n * error is never appended to the text, whatever `includeErrorText` says. */\n errorNotice?: boolean\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\ninterface TextTracker {\n /** Full accumulated text per part key, to derive suffix deltas from\n * snapshot-only harness events. */\n seen: Map<string, string>\n}\n\n/** Delta to emit for one text/reasoning part update: prefer the harness's\n * explicit delta; otherwise diff the snapshot against what was already\n * emitted for that part (snapshot-only harnesses re-send the whole text). */\nfunction textDelta(tracker: TextTracker, key: string, part: JsonRecord, rawDelta: unknown): string {\n const explicit = typeof rawDelta === 'string' ? rawDelta : undefined\n const previous = tracker.seen.get(key) ?? ''\n if (explicit !== undefined) {\n tracker.seen.set(key, previous + explicit)\n return explicit\n }\n const snapshot = asString(part.text) ?? asString(part.content) ?? ''\n if (!snapshot) return ''\n if (snapshot.startsWith(previous)) {\n tracker.seen.set(key, snapshot)\n return snapshot.slice(previous.length)\n }\n // The snapshot replaced the text outright — emit it whole; the persisted\n // projection stays correct because finalText is authoritative at finalize.\n tracker.seen.set(key, snapshot)\n return snapshot\n}\n\nfunction parseEffectiveBackend(data: JsonRecord): Omit<\n ChatTurnModelAttribution,\n 'requestedModel' | 'echoReceived'\n> | undefined {\n const backend = asRecord(data.effectiveBackend)\n if (!backend) return undefined\n const provider = asString(backend.provider)\n const model = asString(backend.model)\n const rawSource = asString(backend.source)\n const source =\n rawSource === 'request' || rawSource === 'environment' || rawSource === 'profile'\n ? rawSource\n : undefined\n if (!provider && !model && !source) return undefined\n return {\n ...(model ? { servedModel: model } : {}),\n ...(provider ? { servedProvider: provider } : {}),\n ...(source ? { servedSource: source } : {}),\n }\n}\n\nfunction toFiniteNumber(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null\n if (typeof value === 'string' && value.trim() !== '') {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\n/** Parse authoritative usage from a terminal sandbox `result` or `done`. */\nfunction extractReportedTurnUsage(data: JsonRecord | null | undefined): ChatTurnUsage | null {\n const tokenUsage = asRecord(data?.tokenUsage)\n if (!tokenUsage) return null\n\n const inputTokens = toFiniteNumber(tokenUsage.inputTokens)\n const outputTokens = toFiniteNumber(tokenUsage.outputTokens)\n if (inputTokens === null || outputTokens === null) return null\n\n const reported: ChatTurnUsage = { inputTokens, outputTokens }\n const reasoningTokens = toFiniteNumber(tokenUsage.reasoningTokens)\n if (reasoningTokens !== null) reported.reasoningTokens = reasoningTokens\n const cacheReadTokens = toFiniteNumber(tokenUsage.cacheReadInputTokens)\n if (cacheReadTokens !== null) reported.cacheReadTokens = cacheReadTokens\n const cacheWriteTokens = toFiniteNumber(tokenUsage.cacheCreationInputTokens)\n if (cacheWriteTokens !== null) reported.cacheWriteTokens = cacheWriteTokens\n const costUsd = toFiniteNumber(data?.totalCostUsd) ?? toFiniteNumber(tokenUsage.cost)\n if (costUsd !== null) reported.costUsd = costUsd\n return reported\n}\n\n/** Native runtime usage events contain cumulative totals, including repeated terminal snapshots. */\nfunction extractUsageSnapshot(value: JsonRecord | null | undefined): ChatTurnUsage | null {\n const inputTokens = toFiniteNumber(value?.promptTokens)\n const outputTokens = toFiniteNumber(value?.completionTokens)\n if (inputTokens === null || outputTokens === null || inputTokens < 0 || outputTokens < 0) return null\n const reported: ChatTurnUsage = { inputTokens, outputTokens }\n const reasoningTokens = toFiniteNumber(value?.reasoningTokens)\n if (reasoningTokens !== null && reasoningTokens >= 0) reported.reasoningTokens = reasoningTokens\n const costUsd = toFiniteNumber(value?.providerCostUsd)\n if (costUsd !== null && costUsd >= 0) reported.costUsd = costUsd\n return reported\n}\n\nfunction applyTerminalUsage(reported: ChatTurnUsage, usage: ChatTurnUsage): void {\n usage.inputTokens = reported.inputTokens\n usage.outputTokens = reported.outputTokens\n if (reported.reasoningTokens !== undefined) usage.reasoningTokens = reported.reasoningTokens\n if (reported.cacheReadTokens !== undefined) usage.cacheReadTokens = reported.cacheReadTokens\n if (reported.cacheWriteTokens !== undefined) usage.cacheWriteTokens = reported.cacheWriteTokens\n if (reported.costUsd !== undefined) usage.costUsd = reported.costUsd\n}\n\nfunction sandboxStreamErrorMessage(data: unknown): string {\n const record = asRecord(data)\n const message = asString(record?.message) ?? asString(record?.error)\n if (message) return message\n try {\n return JSON.stringify(data) || 'Sandbox stream returned an error event without details.'\n } catch {\n return 'Sandbox stream returned an error event without details.'\n }\n}\n\n/** The typed reason a sandbox `error` event carries, or a generic stream-error code. */\nfunction sandboxStreamErrorCode(data: unknown): string {\n return asString(asRecord(data)?.code) ?? 'sandbox.stream_error'\n}\n\nfunction toProducerWireEvent(event: JsonRecord): ProducerWireEvent {\n return event as unknown as ProducerWireEvent\n}\n\nfunction sandboxStreamFailureDiagnostic(error: unknown): {\n userMessage: string\n failureNote: string\n code: string\n} {\n const streamMessage = (error as { streamMessage?: unknown })?.streamMessage\n const message = typeof streamMessage === 'string'\n ? streamMessage\n : error instanceof Error ? error.message : 'Sandbox stream failed'\n const diagnostics = asRecord((error as { diagnostics?: unknown })?.diagnostics)\n let diagnosticText: string | undefined\n if (diagnostics) {\n try {\n diagnosticText = JSON.stringify(diagnostics)\n } catch {\n diagnosticText = '[unserializable diagnostics]'\n }\n }\n const userMessage = [\n 'The sandbox model stream stopped before a clean completion.',\n `Error: ${message}`,\n 'Please retry. If it repeats, send these support details to the team.',\n ].join('\\n\\n')\n const failureNote = diagnosticText\n ? `sandbox-stream: ${message}; ${diagnosticText}`\n : `sandbox-stream: ${message}`\n let code = 'sandbox.stream_failed'\n if (error instanceof ModelFailoverTimeoutError) {\n code = error.code\n } else if (error instanceof ModelFailoverExhaustedError) {\n code = error.upstreamCode ?? code\n }\n return { userMessage, failureNote, code }\n}\n\n/** Create a sandbox chat producer that manages chat turn routing with logging and interaction rendering options */\nexport function createSandboxChatProducer(options: SandboxChatProducerOptions): ChatTurnRouteProducer {\n const log = options.log ?? ((message, meta) => console.error(message, meta ?? ''))\n const renderable = options.isRenderableInteraction ?? isRenderableInteractionKind\n\n // ── model failover ────────────────────────────────────────────────────────\n // ON by default whenever the caller can reopen the stream (`openEvents`) and\n // named more than one model. There is no `enabled` flag on purpose: the class\n // of failure this fixes was a capability that shipped switched off, and a\n // second opt-in on top of supplying the chain would reproduce it exactly.\n if (options.openEvents && !options.model) {\n throw new Error(\n 'createSandboxChatProducer: `openEvents` requires `model` — failover must know which model it is running',\n )\n }\n if (!options.openEvents && !options.events) {\n throw new Error('createSandboxChatProducer: pass `openEvents` (failover-capable) or `events`')\n }\n // A fixed `events` stream cannot be re-opened, so the retry silently does\n // nothing. A config that asks for recovery and receives none is the exact\n // silent-no-op class this seam exists to remove — refuse it at construction\n // rather than let a product believe blank turns are being retried.\n if (!options.openEvents && resolveEmptyTurnRetries(options.emptyTurnRetries) > 0) {\n throw new Error(\n 'createSandboxChatProducer: `emptyTurnRetries` requires `openEvents` — a fixed `events` stream cannot be re-opened',\n )\n }\n const chain = options.model\n ? buildModelChain(options.model, options.modelFailover === false ? [] : (options.fallbackModels ?? []))\n : []\n\n /** Fallback notices waiting to be emitted. `onFallback` fires from inside the\n * first pull of the failover stream, where a generator cannot yield, so the\n * notice is queued and drained at the top of the next loop iteration —\n * landing immediately before the serving model's first event. */\n const pendingModelNotices: Array<{ id: string; text: string }> = []\n let modelNoticeCount = 0\n\n let failover: ModelFailoverStreamHandle | undefined\n let source: AsyncIterable<unknown>\n if (options.openEvents) {\n failover = streamWithModelFailover({\n models: chain,\n open: options.openEvents,\n log,\n ...(options.modelPolicy ? { policy: options.modelPolicy } : {}),\n ...(options.openTimeoutMs !== undefined ? { openTimeoutMs: options.openTimeoutMs } : {}),\n ...(options.firstResponseTimeoutMs !== undefined\n ? { firstResponseTimeoutMs: options.firstResponseTimeoutMs }\n : {}),\n ...(options.emptyTurnRetries !== undefined ? { emptyTurnRetries: options.emptyTurnRetries } : {}),\n ...(options.onEmptyTurnRetry ? { onEmptyTurnRetry: options.onEmptyTurnRetry } : {}),\n ...(options.liveLifecycleEvents !== undefined ? { liveLifecycleEvents: options.liveLifecycleEvents } : {}),\n onFallback: (info) => {\n modelNoticeCount += 1\n pendingModelNotices.push({\n id: `model-fallback-${modelNoticeCount}`,\n // Named models on both sides: a quality regression after a downgrade\n // must be attributable to the model that actually answered. The\n // REASON is condensed — a real edge 5xx arrives as Cloudflare's HTML\n // error page, and the raw text put `<!DOCTYPE html><!--[if lt IE 7]>…`\n // into the customer's transcript. The verbatim text is still on\n // `modelFailover.attempts[].reason` for the operator.\n text: `${info.from} was unavailable (${summarizeFailoverReason(info.reason)}) — answered with ${info.to} instead.`,\n })\n options.onModelFallback?.(info)\n },\n })\n source = failover.events\n } else {\n source = options.events!\n }\n\n /** The model that ACTUALLY served this turn. Read lazily everywhere (the\n * `model` getter, the draft snapshot, the persisted row) so a fallback that\n * happens after the row was drafted still lands on the final write. */\n const servingModel = (): string | undefined => failover?.servingModel() ?? options.model\n let effectiveBackend: ReturnType<typeof parseEffectiveBackend>\n let substitutionNoticeQueued = false\n const servedModel = (): string | undefined => effectiveBackend?.servedModel ?? servingModel()\n\n const captureEffectiveBackend = (data: JsonRecord | undefined): void => {\n if (!data) return\n const parsed = parseEffectiveBackend(data)\n if (!parsed) return\n effectiveBackend = parsed\n const sentModel = servingModel()\n if (\n substitutionNoticeQueued ||\n !options.model ||\n !sentModel ||\n !parsed.servedModel ||\n parsed.servedModel === sentModel\n ) return\n substitutionNoticeQueued = true\n pendingModelNotices.push({\n id: 'model-substitution-1',\n text: `Requested ${sentModel} — the sandbox answered with ${parsed.servedModel} instead.`,\n })\n }\n\n let fullText = ''\n let terminalObserved = false\n const partOrder: string[] = []\n const partMap = new Map<string, JsonRecord>()\n const tracker: TextTracker = { seen: new Map() }\n /** Each reasoning part's start and end where the harness sent none: the same timing the engine's producer persists. */\n const reasoningClock = createReasoningClock<string>()\n const usage: ChatTurnUsage = {}\n /** Whether each announced tool_call carried populated arguments. */\n const announcedToolArgs = new Map<string, boolean>()\n const settledTools = new Set<string>()\n /** Id-less step boundaries: one occurrence per key, never merged. */\n let stepCounter = 0\n let danglingToolsTerminalized = false\n let interactionOutcome: 'answered' | 'expired' = 'answered'\n let warningCount = 0\n /** `promoteFilePart` memo, keyed by `id ?? url` of the raw part. Holds the\n * PROMISE (never the settled result) so concurrent duplicate events for\n * the same part await the one in-flight attempt instead of each starting\n * their own; the promise never rejects (see below), so a later duplicate\n * reading a settled entry reuses that first outcome — success or\n * failure — rather than retrying. */\n const promotedFileParts = new Map<string, Promise<FilePartPromotionOutcome>>()\n\n function recordPersistedPart(part: JsonRecord, delta: string | undefined, keyOverride?: string): void {\n const persisted = normalizePersistedPart(part)\n if (!persisted) return\n const key = keyOverride ?? getPartKey(persisted)\n if (!partMap.has(key)) partOrder.push(key)\n partMap.set(key, mergePersistedPart(partMap.get(key), persisted, delta))\n }\n\n function* emitTerminalizedTools(): Generator<ProducerWireEvent, void, unknown> {\n if (danglingToolsTerminalized) return\n danglingToolsTerminalized = true\n const updates = terminalizeDanglingAssistantToolUpdates(partOrder, partMap, fullText)\n for (const part of updates) {\n const toolId = asString(part.id)\n if (!toolId || settledTools.has(toolId)) continue\n settledTools.add(toolId)\n const state = asRecord(part.state)\n yield {\n type: 'tool_result',\n toolCallId: toolId,\n toolName: asString(part.tool) ?? 'tool',\n outcome: {\n ok: false,\n ...(asString(state?.error) ? { message: asString(state?.error) } : {}),\n },\n }\n }\n }\n\n function* emitFinalUsage(): Generator<ProducerWireEvent, void, unknown> {\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n if (promptTokens || completionTokens) {\n yield {\n type: 'usage',\n usage: {\n promptTokens,\n completionTokens,\n ...(usage.reasoningTokens !== undefined ? { reasoningTokens: usage.reasoningTokens } : {}),\n ...(usage.costUsd !== undefined ? { providerCostUsd: usage.costUsd } : {}),\n },\n }\n }\n }\n\n /** Emit any queued fallback notices, persisting each so the downgrade is in\n * the durable transcript and not only in the live stream. Uses the existing\n * `warning` notice kind deliberately: every shipped client already renders\n * it, whereas a bespoke kind would be dropped by `dispatchChatStreamLine`'s\n * filter — an attribution nobody sees is not attribution. */\n function* drainModelNotices(): Generator<ProducerWireEvent, void, unknown> {\n while (pendingModelNotices.length > 0) {\n const queued = pendingModelNotices.shift()!\n const notice = noticePart('warning', queued.id, queued.text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'warning', text: queued.text }\n }\n }\n\n let errorNoticeCount = 0\n /** `errorNotice` mode: the terminal error as a persisted `error` notice part,\n * so the durable row says \"this turn failed\" in a typed field. */\n function* emitErrorNotice(text: string, code: string): Generator<ProducerWireEvent, void, unknown> {\n errorNoticeCount += 1\n const notice = noticePart('error', `error-${errorNoticeCount}`, text, code)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'error', text, code }\n }\n\n async function* stream(): AsyncGenerator<ProducerWireEvent, void, unknown> {\n try {\n for await (const raw of source) {\n const record = asRecord(raw)\n captureEffectiveBackend(asRecord(record?.data))\n yield* drainModelNotices()\n if (!record || typeof record.type !== 'string') continue\n // Fold bare tool_call/tool_result shapes into the canonical part event;\n // everything else keeps its original record (verbatim forwarding must\n // not strip fields outside `data`).\n const normalized = normalizeToolEvent({ type: record.type, data: asRecord(record.data) })\n const event: StreamEvent = normalized.type === 'message.part.updated'\n ? normalized\n : { type: record.type, data: asRecord(record.data) }\n\n if (event.type === 'message.part.updated') {\n const part = asRecord(event.data?.part)\n if (!part) continue\n const rawDelta = event.data?.delta\n const partType = String(part.type ?? '')\n\n if (partType === 'text' || partType === 'reasoning') {\n const key = getPartKey(part)\n const delta = textDelta(tracker, key, part, rawDelta)\n recordPersistedPart(part, delta || undefined)\n if (delta) {\n if (partType === 'reasoning') reasoningClock.reasoning(key)\n else reasoningClock.settle()\n if (partType === 'text') fullText += delta\n yield { type: partType, text: delta }\n }\n continue\n }\n\n if (partType === 'tool') {\n reasoningClock.settle()\n recordPersistedPart(part, undefined)\n const persisted = partMap.get(getPartKey(part))\n const state = asRecord(persisted?.state)\n const toolId = String(persisted?.id ?? '')\n const toolName = String(persisted?.tool ?? 'tool')\n const args = asRecord(state?.input) ?? {}\n const hasPopulatedArgs = Object.keys(args).length > 0\n const announcedWithPopulatedArgs = announcedToolArgs.get(toolId)\n if (\n toolId &&\n (announcedWithPopulatedArgs === undefined ||\n (!announcedWithPopulatedArgs && hasPopulatedArgs && !settledTools.has(toolId)))\n ) {\n announcedToolArgs.set(toolId, hasPopulatedArgs)\n yield {\n type: 'tool_call',\n call: { toolCallId: toolId, toolName, args },\n }\n }\n const status = String(state?.status ?? '')\n if (toolId && (status === 'completed' || status === 'error') && !settledTools.has(toolId)) {\n settledTools.add(toolId)\n yield {\n type: 'tool_result',\n toolCallId: toolId,\n toolName,\n outcome: {\n ok: status === 'completed',\n ...(state?.output !== undefined ? { result: state.output } : {}),\n ...(asString(state?.error) ? { message: asString(state?.error) } : {}),\n },\n }\n }\n continue\n }\n\n if (partType === 'step-finish') {\n addStepFinishUsage(part, usage)\n // Persist the per-step receipt too (unique key per occurrence: the\n // parts have no id and two receipts must never merge into one).\n recordPersistedPart(part, undefined, `step-finish:#${stepCounter++}`)\n const promptTokens = usage.inputTokens ?? 0\n const completionTokens = usage.outputTokens ?? 0\n if (promptTokens || completionTokens) {\n yield {\n type: 'usage',\n usage: {\n promptTokens,\n completionTokens,\n ...(usage.reasoningTokens !== undefined\n ? { reasoningTokens: usage.reasoningTokens }\n : {}),\n ...(usage.costUsd !== undefined ? { providerCostUsd: usage.costUsd } : {}),\n },\n }\n }\n continue\n }\n\n if (partType === 'step-start') {\n recordPersistedPart(part, undefined, `step-start:#${stepCounter}`)\n continue\n }\n\n if (partType === 'file' && options.promoteFilePart) {\n const promote = options.promoteFilePart\n // Prefixed by source (`id:`/`url:`) rather than the bare raw string —\n // an `id` and a `url` that happen to share the same text (e.g. both\n // `\"abc\"`) would otherwise collide onto one memo entry and fold two\n // unrelated parts' promotions together. Internal Map key only; never\n // observable outside this module.\n const rawId = asString(part.id)\n const rawUrl = asString(part.url)\n const memoKey = rawId ? `id:${rawId}` : rawUrl ? `url:${rawUrl}` : undefined\n // Always invoke the callback for a `file` part when one is wired —\n // gtm never skips promotion outright, even for a part with neither\n // `id` nor `url` (it simply fails \"carries no url\" and resolves\n // through the ordinary failure path). Memoization by PROMISE only\n // applies when there is something to key it on; keyless parts are\n // invoked un-memoized, once per occurrence.\n //\n // `Promise.resolve().then(...)` (rather than calling `promote(part)`\n // directly) so a SYNC throw from a non-async `promoteFilePart` is\n // captured into the promise chain instead of escaping this function\n // call outright — `.catch` below only ever sees a rejection, never a\n // thrown exception that unwinds past `attempt()`.\n const attempt = (): Promise<FilePartPromotionOutcome> =>\n Promise.resolve()\n .then(() => promote(part))\n .catch((err) => {\n const reason = err instanceof Error ? err.message : String(err)\n log('[chat-routes] file part promotion threw', { key: memoKey ?? '(keyless)', error: reason })\n return { succeeded: false as const, reason }\n })\n\n let pending: Promise<FilePartPromotionOutcome>\n if (memoKey) {\n pending = promotedFileParts.get(memoKey) ?? attempt()\n promotedFileParts.set(memoKey, pending)\n } else {\n pending = attempt()\n }\n\n const outcome = await pending\n if (outcome.succeeded) {\n recordPersistedPart(outcome.part, undefined, outcome.key)\n } else if (outcome.part) {\n // Substitute part (e.g. a warning notice) takes the raw part's\n // place in the transcript — the transient url never lands.\n recordPersistedPart(outcome.part, undefined, outcome.key)\n } else {\n recordPersistedPart(part, undefined)\n }\n continue\n }\n\n // Remaining storable kinds (file/image/subtask) have no live\n // vocabulary line; they persist so the transcript keeps them.\n recordPersistedPart(part, undefined)\n continue\n }\n\n if (event.type === 'interaction') {\n const parsed = parseInteractionRequest(asRecord(record.data))\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed interaction event', { error: parsed.error })\n continue\n }\n if (renderable(parsed.value.kind, parsed.value)) {\n recordPersistedPart(\n interactionToPersistedPart(parsed.value, 'pending'),\n undefined,\n interactionPartKey(parsed.value.id),\n )\n yield toProducerWireEvent(record)\n continue\n }\n // Non-renderable ask: the run is blocked in the broker until someone\n // answers. Decline it so the turn proceeds instead of hanging.\n let declineFailed = false\n if (options.declineInteraction) {\n try {\n await options.declineInteraction(parsed.value.id)\n } catch (err) {\n declineFailed = true\n log('[chat-routes] failed to auto-decline interaction', {\n id: parsed.value.id,\n error: err instanceof Error ? err.message : String(err),\n })\n }\n } else {\n declineFailed = true\n log('[chat-routes] non-renderable interaction with no declineInteraction wired', {\n id: parsed.value.id,\n kind: parsed.value.kind,\n })\n }\n const text = options.declinedNotice?.(parsed.value, declineFailed) ?? (declineFailed\n ? `The agent requested ${parsed.value.kind} approval; declining it failed — it will expire on its own.`\n : `The agent requested ${parsed.value.kind} approval — auto-declined by policy.`)\n const notice = noticePart('auto-declined', `auto-declined-${parsed.value.id}`, text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'auto-declined', text }\n continue\n }\n\n if (event.type === 'interaction.cancel') {\n const parsed = parseInteractionCancel(asRecord(record.data))\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed interaction.cancel event', { error: parsed.error })\n continue\n }\n const key = interactionPartKey(parsed.value.id)\n const existing = partMap.get(key)\n if (existing?.type === 'interaction' && existing.status === 'pending') {\n recordPersistedPart({\n ...existing,\n status: cancelStatusFor(parsed.value.reason),\n ...(parsed.value.reason ? { cancelReason: parsed.value.reason } : {}),\n }, undefined, key)\n }\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'plan.submitted') {\n const parsed = parsePlanSubmittedEvent(record)\n if (!parsed.succeeded) {\n log('[chat-routes] dropping malformed plan.submitted event', { error: parsed.error })\n continue\n }\n recordPersistedPart(planToPersistedPart(parsed.value), undefined)\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'warning') {\n const message = asString(event.data?.message)\n if (message) {\n warningCount += 1\n const code = asString(event.data?.code)\n const text = code ? `${code}: ${message}` : message\n const notice = noticePart('warning', `warning-${warningCount}`, text)\n recordPersistedPart(notice, undefined, noticePartKey(notice.id))\n yield { type: 'notice', id: notice.id, noticeKind: 'warning', text }\n }\n // The flattened notice is additive; existing raw-warning consumers keep\n // receiving the original event too.\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'usage') {\n const reported = extractUsageSnapshot(asRecord(record.usage))\n if (reported) applyTerminalUsage(reported, usage)\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'result') {\n terminalObserved = true\n const finalText = asString(event.data?.finalText)\n if (finalText) fullText = finalText\n const reported = extractReportedTurnUsage(asRecord(event.data))\n if (reported) {\n applyTerminalUsage(reported, usage)\n yield* emitFinalUsage()\n } else {\n // Legacy result shape kept as a secondary fallback.\n const resultUsage = asRecord(event.data?.usage)\n if (resultUsage) {\n const input = Number(resultUsage.inputTokens)\n const output = Number(resultUsage.outputTokens)\n if (Number.isFinite(input)) usage.inputTokens = input\n if (Number.isFinite(output)) usage.outputTokens = output\n }\n }\n yield* emitTerminalizedTools()\n continue\n }\n\n if (event.type === 'done') {\n terminalObserved = true\n const reported = extractReportedTurnUsage(asRecord(event.data))\n if (reported) {\n applyTerminalUsage(reported, usage)\n yield* emitFinalUsage()\n }\n yield* emitTerminalizedTools()\n yield toProducerWireEvent(record)\n continue\n }\n\n if (event.type === 'error') {\n const message = sandboxStreamErrorMessage(event.data)\n const errorContent = fullText.trim()\n ? `The sandbox model stream stopped before a clean completion.\\n\\nError: ${message}`\n : `The sandbox agent returned an error before producing a visible answer.\\n\\nError: ${message}`\n if (options.errorNotice) {\n yield* emitErrorNotice(errorContent, sandboxStreamErrorCode(event.data))\n } else if (options.includeErrorText !== false) {\n const errorDelta = fullText ? `\\n\\n---\\n${errorContent}` : errorContent\n fullText += errorDelta\n yield { type: 'text', text: errorDelta }\n }\n interactionOutcome = 'expired'\n yield* emitTerminalizedTools()\n yield toProducerWireEvent(record)\n continue\n }\n\n // Remaining lifecycle events forward verbatim; the client parser ignores\n // unknown types.\n yield toProducerWireEvent(record)\n }\n // A model that fell over and then produced nothing never entered the loop\n // body; the downgrade must still be recorded.\n yield* drainModelNotices()\n } catch (streamErr) {\n const diagnostic = sandboxStreamFailureDiagnostic(streamErr)\n log('[chat-routes] sandbox stream failed', {\n failureNote: diagnostic.failureNote,\n error: streamErr instanceof Error ? streamErr.message : String(streamErr),\n })\n if (options.errorNotice) {\n yield* emitErrorNotice(diagnostic.userMessage, diagnostic.code)\n } else if (options.includeErrorText !== false) {\n const errorDelta = fullText\n ? `\\n\\n---\\n${diagnostic.userMessage}`\n : diagnostic.userMessage\n fullText += errorDelta\n yield { type: 'text', text: errorDelta }\n }\n interactionOutcome = 'expired'\n yield* emitTerminalizedTools()\n yield {\n type: 'error',\n data: {\n message: diagnostic.userMessage,\n code: diagnostic.code,\n details: { failureNote: diagnostic.failureNote },\n },\n }\n } finally {\n reasoningClock.settle()\n }\n }\n\n /** The persisted parts with each reasoning part's time: the harness's own, else the clock's. */\n const timedParts = (): Map<string, JsonRecord> => {\n const timed = new Map(partMap)\n for (const [key, part] of partMap) {\n if (part.type !== 'reasoning') continue\n const time = reasoningClock.time(key, asRecord(part.time) as ReasoningRunTime | undefined)\n if (time) timed.set(key, { ...part, time })\n }\n return timed\n }\n\n return {\n stream: stream(),\n finalText: () => fullText,\n terminalObserved: () => terminalObserved,\n assistantParts: () => finalizePendingInteractionParts(\n finalizeAssistantParts(partOrder, timedParts(), fullText),\n interactionOutcome,\n ),\n // Mid-stream snapshot for incremental persistence: the same accumulators,\n // WITHOUT the two completion-time settlements (`terminalizeDanglingTool*`\n // and `finalizePendingInteractionParts`). A running tool and an unanswered\n // ask are the correct live state; settling them early would persist\n // phantom failures the final write then reverses.\n draftParts: () => draftAssistantParts(partOrder, timedParts(), fullText),\n usage: () => usage,\n // A GETTER, not a captured value: `turn-routes` reads `producer.model` at\n // draft-snapshot and persist time, both of which happen after the stream\n // resolved which model serves. A plain property would freeze the PREFERRED\n // model into the row and make a downgrade unattributable — the exact\n // failure mode this work exists to prevent.\n get model(): string | undefined {\n return servedModel()\n },\n modelFailover: () => ({\n model: servingModel(),\n attempts: failover?.attempts() ?? [],\n usedFallback: failover?.usedFallback() ?? false,\n }),\n modelAttribution: () => ({\n ...(options.model ? { requestedModel: options.model } : {}),\n ...(effectiveBackend ?? {}),\n echoReceived: effectiveBackend !== undefined,\n }),\n }\n}\n","/**\n * The chat-route producer for a Router turn: `runRouterTurn` behind the same\n * `produce` seam the sandbox lane uses (`createSandboxChatProducer`). The route\n * persists, buffers, replays and settles the turn exactly as it does for the\n * sandbox; only the producer differs.\n *\n * The engine is pushed, the route pulls: engine events land in a queue that the\n * `stream` generator drains in order, so a slow consumer never drops a frame\n * and the engine never waits on the consumer.\n */\nimport { runRouterTurn, type RouterTurnOptions, type RouterTurnOutcome } from '../runtime/router-turn'\nimport { createReasoningClock } from './reasoning-clock'\nimport type { ChatTurnRouteProducer, ChatTurnUsage } from './turn-routes'\nimport type { ProducerWireEvent } from './wire'\n\nexport type RouterTurnProducerOptions = Omit<RouterTurnOptions, 'onEvent'> & {\n /** Observe the finished turn (placement records, metrics, billing). */\n onOutcome?: (outcome: RouterTurnOutcome) => void\n}\n\nexport function createRouterTurnProducer(options: RouterTurnProducerOptions): ChatTurnRouteProducer {\n const queue: ProducerWireEvent[] = []\n let wake: (() => void) | null = null\n let done = false\n let outcome: RouterTurnOutcome | null = null\n const parts: Array<Record<string, unknown>> = []\n const toolParts = new Map<string, Record<string, unknown>>()\n let textPart: { type: 'text'; text: string } | null = null\n let textSegment = 0\n let reasoningPart: { type: 'reasoning'; text: string } | null = null\n // Each reasoning part's start and end, the same timing the sandbox producer persists.\n const reasoningClock = createReasoningClock<Record<string, unknown>>()\n\n const push = (event: ProducerWireEvent) => {\n queue.push(event)\n wake?.()\n }\n\n const run = runRouterTurn({\n ...options,\n onEvent: event => {\n if (event.type === 'text') {\n if (event.segment !== textSegment || !textPart) {\n textSegment = event.segment\n textPart = { type: 'text', text: '' }\n parts.push(textPart)\n }\n textPart.text += event.delta\n reasoningPart = null\n reasoningClock.settle()\n push({ type: 'text', text: event.delta })\n } else if (event.type === 'reasoning') {\n // A reasoning run is one part until a word or a tool interrupts it.\n if (!reasoningPart) {\n reasoningPart = { type: 'reasoning', text: '' }\n parts.push(reasoningPart)\n }\n reasoningPart.text += event.delta\n reasoningClock.reasoning(reasoningPart)\n push({ type: 'reasoning', text: event.delta })\n } else if (event.type === 'tool') {\n reasoningPart = null\n // The model stopped reasoning when it called the tool.\n reasoningClock.settle(event.startedAt)\n if (event.status === 'running') {\n const part = { type: 'tool', id: event.callId, callID: event.callId, tool: event.toolName,\n state: { status: 'running', input: event.input, time: { start: event.startedAt } } }\n toolParts.set(event.callId, part)\n parts.push(part)\n push({ type: 'tool_call', call: { toolCallId: event.callId, toolName: event.toolName, args: event.input } })\n } else {\n const part = toolParts.get(event.callId)\n const state = event.status === 'completed'\n ? { status: 'completed', output: event.output, time: { start: event.startedAt, end: event.endedAt } }\n : { status: 'error', error: event.error.message, time: { start: event.startedAt, end: event.endedAt } }\n if (part) part.state = { ...(part.state as Record<string, unknown>), ...state }\n push({ type: 'tool_result', toolCallId: event.callId, toolName: event.toolName,\n outcome: event.status === 'completed' ? { ok: true, result: event.output } : { ok: false, message: event.error.message, code: event.error.code } })\n }\n } else if (event.type === 'usage') {\n push({ type: 'usage', usage: { promptTokens: event.promptTokens, completionTokens: event.completionTokens } })\n } else if (event.type === 'tool_event') {\n // Recorded on the tool's part, so a reload shows what the tool did, and streamed as it happens.\n const part = toolParts.get(event.callId)\n if (part) {\n const state = part.state as Record<string, unknown>\n part.state = { ...state, events: [...(state.events as unknown[] ?? []), { kind: event.kind, data: event.data, at: event.at }] }\n }\n push({ type: 'tool.event', data: { toolCallId: event.callId, toolName: event.toolName, kind: event.kind, data: event.data, at: event.at } })\n }\n },\n }).then(result => {\n outcome = result\n options.onOutcome?.(result)\n if (result.failure) {\n push({ type: 'error', data: { message: result.failure.message, code: result.failure.code } })\n }\n }).finally(() => {\n reasoningClock.settle()\n done = true\n wake?.()\n })\n\n async function* stream(): AsyncGenerator<ProducerWireEvent, void, unknown> {\n for (;;) {\n while (queue.length) yield queue.shift()!\n if (done) break\n await new Promise<void>(resolve => { wake = resolve })\n wake = null\n }\n await run\n }\n\n // A reasoning part carries its run's time; a run still open in a mid-turn draft has a start and no end.\n const snapshot = () => parts.map(part => part.type === 'reasoning'\n ? { ...part, time: reasoningClock.time(part) }\n : { ...part })\n\n const usage = (): ChatTurnUsage => ({\n inputTokens: outcome?.usage.promptTokens ?? 0,\n outputTokens: outcome?.usage.completionTokens ?? 0,\n })\n\n return {\n stream: stream() as ChatTurnRouteProducer['stream'],\n finalText: () => outcome?.text ?? '',\n assistantParts: snapshot,\n draftParts: snapshot,\n usage,\n get model() { return outcome?.servedModel ?? options.model },\n modelAttribution: () => ({\n requestedModel: options.model,\n ...(outcome?.servedModel ? { servedModel: outcome.servedModel } : {}),\n echoReceived: Boolean(outcome?.servedModel),\n }),\n }\n}\n","import { profileMaterializationAxes, type AgentProfile } from '@tangle-network/agent-interface'\nimport { runProtectedRouterTurn, type RouterTurnGrant } from '../runtime/protected-router-turn'\nimport type { RouterTurnOptions } from '../runtime/router-turn'\nimport type { McpToolDefinition } from '../tools/mcp-rpc'\nimport { createSandboxChatProducer } from './sandbox-producer'\nimport type { ChatRouteEvent, ChatTurnRouteProducer, ChatTurnUsage } from './turn-routes'\n\nexport interface ProtectedRuntimeChatOptions {\n profile: AgentProfile\n prompt: string\n priorMessages?: ReadonlyArray<{ role: string; content: string }>\n /** Runtime owns reservation, activation, revocation and authoritative settlement. */\n grant: RouterTurnGrant\n /** Only tools authorized for this execution. Each tool validates its arguments and effects. */\n tools: readonly McpToolDefinition<{ signal: AbortSignal }>[]\n maxToolCalls: number\n signal?: AbortSignal\n}\n\n/**\n * The AgentProfile leaves (agent-interface's materialization axes) a protected turn\n * carries. Identity, `harness` (cli-base only) and `metadata` are control fields;\n * `modelMetadata` is accepted only when empty. A leaf outside this set, including one\n * agent-interface adds later, is refused by name before a grant is reserved: running\n * the turn without it would silently drop what the profile asked for.\n */\nconst PROTECTED_TURN_AXES: ReadonlySet<string> = new Set([\n 'name', 'description', 'version', 'tags', 'metadata', 'harness', 'tools',\n 'systemPrompt', 'instructions',\n 'modelDefault', 'modelProvider', 'modelReasoningEffort', 'modelMaxVisibleOutputTokens',\n 'modelMaxTotalOutputTokens', 'modelMetadata',\n])\n\n/**\n * The parts of a profile a protected turn applies: its prompt text, its model and\n * reasoning effort, and its completion ceilings. Anything else would change the\n * execution without the grant pricing it, so it is refused by name.\n */\nfunction protectedTurnSettings(profile: AgentProfile, toolNames: ReadonlySet<string>): Pick<RouterTurnOptions,\n 'model' | 'systemPrompt' | 'reasoningEffort' | 'maxTokens' | 'maxCompletionTokens'> {\n if (profile.harness && profile.harness !== 'cli-base') throw new Error(`Protected chat runs only the cli-base harness, not ${profile.harness}`)\n if (profile.resources) throw new Error('Protected chat applies prompt text only; run profile resources in the Sandbox lane')\n if (profile.model?.metadata && Object.keys(profile.model.metadata).length > 0) {\n throw new Error('Protected chat does not apply AgentProfile.model.metadata')\n }\n if (profile.model?.maxReasoningTokens !== undefined) throw new Error('Protected chat cannot enforce maxReasoningTokens')\n const unsupported = profileMaterializationAxes(profile).filter(axis => !PROTECTED_TURN_AXES.has(axis))\n if (unsupported.length > 0) {\n throw new Error(`Protected chat does not apply AgentProfile ${unsupported.join(', ')}; run this profile in the Sandbox lane`)\n }\n const declared = profile.model?.default?.trim()\n const provider = profile.model?.provider?.trim()\n const model = declared && provider && declared.startsWith(`${provider}/`) ? declared.slice(provider.length + 1) : declared\n if (!model) throw new Error('Protected chat requires AgentProfile.model.default')\n for (const [name, enabled] of Object.entries(profile.tools ?? {})) {\n if (enabled !== toolNames.has(name)) throw new Error(`Protected tool ${name} does not match AgentProfile.tools`)\n }\n for (const name of toolNames) {\n if (profile.tools?.[name] !== true) throw new Error(`Protected tool ${name} is not enabled by AgentProfile.tools`)\n }\n const { visible, total } = { visible: profile.model?.maxVisibleOutputTokens, total: profile.model?.maxTotalOutputTokens }\n return {\n model,\n systemPrompt: [profile.prompt?.systemPrompt, ...(profile.prompt?.instructions ?? [])]\n .filter((part): part is string => typeof part === 'string' && part.trim().length > 0).join('\\n'),\n ...(profile.model?.reasoningEffort ? { reasoningEffort: profile.model.reasoningEffort } : {}),\n ...(visible !== undefined ? { maxTokens: visible } : {}),\n ...(total !== undefined ? { maxCompletionTokens: total } : {}),\n }\n}\n\n/** The text a model reads for one tool result: the JSON of its value, or the tool's own error. */\nfunction protectedToolResultText(outcome: { ok: true; result: unknown } | { ok: false; message: string }): string {\n if (!outcome.ok) return `error: ${outcome.message}`\n return JSON.stringify(outcome.result) ?? 'null'\n}\n\n/**\n * Run a profile as one Router turn under a protected model grant\n * (`runProtectedRouterTurn`), projected through the shared chat projection. The\n * usage is published only from the settled grant.\n */\nexport function createProtectedRuntimeChatProducer(options: ProtectedRuntimeChatOptions): ChatTurnRouteProducer {\n if (!Number.isSafeInteger(options.maxToolCalls) || options.maxToolCalls < 0) {\n throw new Error('maxToolCalls must be a non-negative integer')\n }\n options = { ...options,\n grant: { ...options.grant, resolve: structuredClone(options.grant.resolve), reserve: structuredClone(options.grant.reserve) },\n tools: options.tools.map(tool => ({ ...tool, inputSchema: structuredClone(tool.inputSchema) })),\n // Chat history rows carry storage fields (id, parts, timestamps). The\n // provider receives exactly role and content; Anthropic rejects extra keys.\n priorMessages: options.priorMessages?.map(({ role, content }) => ({ role, content })),\n }\n const tools = new Map(options.tools.map(tool => [tool.name, tool]))\n if (tools.size !== options.tools.length) throw new Error('Duplicate protected tool name')\n const profile = structuredClone(options.profile)\n let toolCalls = 0\n let usage: ChatTurnUsage = {}\n let started = false\n let model: string | undefined\n let budgetEnforced = false\n let toolTokens: number | undefined\n\n async function* events(): AsyncGenerator<unknown> {\n if (started) throw new Error('A protected chat producer can only execute once')\n started = true\n options.signal?.throwIfAborted()\n if (Date.now() >= options.grant.deadlineAtMs) throw new Error('Protected execution deadline expired')\n const settings = protectedTurnSettings(profile, new Set(tools.keys()))\n const controller = new AbortController()\n const signal = AbortSignal.any([\n controller.signal, AbortSignal.timeout(Math.max(1, options.grant.deadlineAtMs - Date.now())),\n ...(options.signal ? [options.signal] : []),\n ])\n const channel = new TransformStream<unknown, unknown>()\n const writer = channel.writable.getWriter()\n const reader = channel.readable.getReader()\n const emit = (event: unknown) => writer.write(event)\n // Each model call's text, by segment. The answer is the last segment the model wrote.\n const segments: string[] = []\n const execution = runProtectedRouterTurn({\n ...settings,\n grant: options.grant,\n userMessage: options.prompt,\n history: [...(options.priorMessages ?? [])],\n signal,\n // The grant's ledger expects one buffered completion per model call.\n stream: false,\n maxToolCalls: options.maxToolCalls,\n // The grant bounds model calls; the loop must not stop the turn earlier.\n maxToolTurns: options.grant.reserve.limits.maxModelCalls,\n tools: options.tools.map(tool => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema,\n run: async (args: Record<string, unknown>) => {\n signal.throwIfAborted()\n toolCalls += 1\n const id = `tool-${toolCalls}`\n await emit({ type: 'tool_call', data: { id, name: tool.name, arguments: args } })\n try {\n const result = await tool.run(args, { signal })\n await emit({ type: 'tool_result', data: { id, name: tool.name, output: result } })\n return result\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Tool failed'\n await emit({ type: 'tool_result', data: { id, name: tool.name, error: message } })\n throw error\n }\n } })),\n toolError: error => ({ code: 'tool_failed', message: error instanceof Error ? error.message : 'Tool failed' }),\n renderToolResult: (_toolName, outcome) => protectedToolResultText(outcome),\n onEvent: event => {\n if (event.type === 'text') segments[event.segment - 1] = (segments[event.segment - 1] ?? '') + event.delta\n },\n }).then(async outcome => {\n if (outcome.settlement) {\n usage = { ...outcome.settlement.usage }\n budgetEnforced = outcome.settlement.budgetEnforced\n model = outcome.settlement.model\n // Tool-result text is billed as input under a grant.\n toolTokens = 0\n }\n // A settlement check fails after the turn answered; the answer stands beside the failure.\n if (!outcome.failure || outcome.failure.code.startsWith('settlement_')) {\n const finalText = segments.filter(Boolean).at(-1) ?? ''\n if (finalText) await emit({ type: 'message.part.updated', data: { part: { id: 'text_delta', type: 'text' }, delta: finalText } })\n await emit({ type: 'result', data: { finalText } })\n }\n if (outcome.failure) throw new Error(outcome.failure.message)\n await writer.close()\n }).catch(error => writer.abort(error))\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) break\n yield next.value\n }\n await execution\n } finally {\n controller.abort()\n await reader.cancel().catch(() => undefined)\n await execution\n reader.releaseLock()\n writer.releaseLock()\n }\n }\n\n const producer = createSandboxChatProducer({ events: events(), model: profile.model?.default })\n return {\n ...producer,\n stream: (async function* () {\n let failure: ChatRouteEvent | undefined\n for await (const event of producer.stream) {\n if (event.type === 'error') failure = event\n else yield event\n }\n if (usage.inputTokens !== undefined && usage.outputTokens !== undefined && usage.costUsd !== undefined) {\n yield { type: 'usage' as const, usage: { promptTokens: usage.inputTokens,\n completionTokens: usage.outputTokens, providerCostUsd: usage.costUsd,\n reasoningTokens: usage.reasoningTokens, toolTokens,\n toolCallCount: toolCalls, budgetEnforced } }\n }\n if (failure) yield failure\n })(),\n get model() { return model ?? profile.model?.default },\n usage: () => ({ ...usage }),\n }\n}\n","/**\n * Detached (autonomous) turn → live buffer bridge.\n *\n * The interactive lane (`createChatTurnRoutes`) already streams a user-typed\n * turn to the browser while it runs. An AUTONOMOUS turn — a mission step, a\n * queue job, an inbound-email review — runs detached (`dispatchPrompt`/\n * `streamPrompt` server-side so it survives no one watching) and, historically,\n * only persisted its FINAL message. A browser opening the session mid-run saw a\n * dead screen: the live tokens existed server-side but were never written to\n * the turn-event buffer the client re-attach path (`listRunning` + `/replay`)\n * reads.\n *\n * `runDetachedTurn` is that missing bridge, packaged. It taps the same buffer\n * the interactive lane uses (`createBufferedTurnTap`) with the same producer\n * mapping (`createSandboxChatProducer`), so an autonomous run is watchable\n * token-by-token exactly like an interactive one — while staying durable\n * (a durable driver re-invokes it after a crash; a turn that finished\n * server-side short-circuits instead of re-streaming). Products supply only the\n * domain seams: the raw sandbox event stream, the turn store, and the ids.\n *\n * This is app-shell mechanism (turn durability + live projection), not engine:\n * it owns no loop logic and imports no SDK — the event source is an injected\n * `AsyncIterable`.\n */\n\nimport { toChatMessageParts } from '../chat-store/parts'\nimport type { ModelFailoverAttempt } from '../model-resolution/failover'\nimport {\n coalesceDeltas,\n createBufferedTurnTap,\n type TurnEventStore,\n} from '../stream/index'\nimport {\n assistantRowIdForTurn,\n createAssistantDraftWriter,\n storeSupportsDraftPersistence,\n type AssistantDraftStore,\n type AssistantDraftWriter,\n type AssistantRowValues,\n type DraftStoredMessage,\n type DraftPersistenceTuning,\n} from './draft-persistence'\nimport {\n createSandboxChatProducer,\n type SandboxChatProducerOptions,\n} from './sandbox-producer'\nimport type { ChatTurnUsage } from './turn-routes'\n\n/** The normalized structured message body (tool-call / file / plan / interaction\n * parts) that `/chat-store` persists as the durable assistant row — the same\n * shape `createSandboxChatProducer().assistantParts()` returns. */\nexport type DetachedTurnParts = Array<Record<string, unknown>>\n\n/** Authoritative final receipt for a turn that finished server-side, or whose\n * live stream carried no usage (some harness paths only expose tokens via the\n * completed-turn record, e.g. `box.findCompletedTurn(turnId)`). */\nexport interface DetachedTurnFinal {\n text?: string\n usage?: ChatTurnUsage\n /** The structured parts to persist when this receipt is more complete than\n * the live stream (cached, finished server-side, or a fast stream that\n * delivered scalar text before its message-part events). Omitted when the\n * record only carries a usage receipt. */\n parts?: DetachedTurnParts\n}\n\n/** Define options for managing and projecting a detached turn event stream in a session */\nexport interface DetachedTurnOptions {\n store: TurnEventStore\n turnId: string\n /** Thread/session id — recorded as the buffer scope so a browser opening the\n * session mid-run rediscovers this turn via `listRunning(scopeId)` after it\n * has lost the turnId. */\n scopeId: string\n /** The raw sandbox event stream for this turn (e.g. `streamSandboxPrompt`).\n * Ownership of the box, prompt, tooling, and attachments stays with the\n * caller — this only projects the stream.\n *\n * An already-open stream is bound to one model and cannot fail over. Prefer\n * {@link openEvents}; exactly one of the two is required. */\n events?: AsyncIterable<unknown>\n /** Open the raw sandbox stream FOR A GIVEN MODEL — the failover-capable form\n * of {@link events}. Wiring it turns failover on with no further flag\n * whenever {@link fallbackModels} is non-empty. Requires {@link model}.\n *\n * An autonomous run is the case that most needs this: nobody is watching to\n * notice a dead upstream and retry by hand, so without failover the mission\n * step or queue job simply fails. Forwarded to the producer. */\n openEvents?: SandboxChatProducerOptions['openEvents']\n /** Models to try, in order, when `model`'s upstream is dead. Product config —\n * see the producer's note on why a same-family fallback is not automatically\n * safe and why every fallback is surfaced. */\n fallbackModels?: SandboxChatProducerOptions['fallbackModels']\n /** Opt out of failover while still using {@link openEvents}. */\n modelFailover?: false\n /** Maximum time to open/start one model event source through its first event.\n * Forwarded to the producer; default 120 seconds. */\n openTimeoutMs?: SandboxChatProducerOptions['openTimeoutMs']\n /** Hard deadline after the source's first lifecycle event for the first\n * answer-bearing event. Forwarded to the producer; default 60 seconds. */\n firstResponseTimeoutMs?: SandboxChatProducerOptions['firstResponseTimeoutMs']\n /** Fired when a model is abandoned mid-chain (telemetry/alerting). */\n onModelFallback?: SandboxChatProducerOptions['onModelFallback']\n /** The PREFERRED model. Recorded on the persisted assistant message + usage\n * receipt — unless failover moved the turn, in which case the model that\n * actually served is recorded instead and surfaced on the result. */\n model?: string\n /** Per-flush buffer coalescer. Default `coalesceDeltas`. */\n coalesce?: (events: unknown[]) => unknown[]\n /** Which ask kinds the product renders a card for; anything else is\n * auto-declined via {@link declineInteraction}. Forwarded to the producer. */\n isRenderableInteraction?: SandboxChatProducerOptions['isRenderableInteraction']\n /** Resolve a non-renderable ask so the run never hangs in the broker. An\n * autonomous turn has NO human watching to answer an ask, so a caller that\n * omits this risks the run blocking until the broker times out — wire it for\n * any unattended run. Forwarded to the producer. */\n declineInteraction?: SandboxChatProducerOptions['declineInteraction']\n /** Opt-in eager promotion of harness-emitted `file` parts. Forwarded to the\n * producer (see its docs). */\n promoteFilePart?: SandboxChatProducerOptions['promoteFilePart']\n /** Authoritative final receipt, consulted whenever a re-invoke finds a prior\n * buffer: (a) an already-`complete` turn returns it as the cached result,\n * (b) a `running` turn (crash mid-run) uses it to detect a run that finished\n * server-side, and (c) a clean run whose stream carried no usage or only\n * scalar text falls back to it. For Sandbox runs, use\n * `readCompletedSandboxTurn` so the exact completed session message\n * restores tool/file parts as well as text. */\n completedResult?: () => Promise<DetachedTurnFinal | null | undefined>\n /** Own the durable assistant row for this turn instead of returning the body\n * for the caller to insert — and keep it in step with the stream.\n *\n * An autonomous run is exactly the case a late viewer hits: nobody is\n * watching when it starts, so by the time a browser opens the session the\n * streaming gateway's hot event buffer may already have expired it. Keeping\n * that buffer short is what makes it affordable at scale (its Redis\n * footprint is linear in `ttl x concurrent sessions`), so the durable row —\n * written incrementally here — is what serves the late viewer.\n *\n * WIRING THIS TRANSFERS ROW OWNERSHIP: the returned {@link\n * DetachedTurnResult.messageId} names the row this call wrote (draft rows\n * during the stream, authoritative values at the end, retraction when the\n * turn produced nothing). The caller must NOT insert its own assistant row\n * for the turn. Omit the seam and nothing changes — the result is returned\n * and the caller persists it exactly as today.\n *\n * Idempotency reuses the turn's own identity: the row id defaults to\n * `assistant:<turnId>`, so a durable driver re-invoking after a crash\n * patches the same row instead of duplicating parts. */\n persist?: DraftPersistenceTuning & {\n store: AssistantDraftStore\n threadId: string\n /** Deterministic row id. Default `assistant:<turnId>`. */\n messageId?: string\n /** Pre-persist text transform (`/redact`), applied to drafts AND the final\n * write — parity with the interactive lane's `transformFinalText`. */\n transformText?: (text: string) => string | Promise<string>\n }\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** Describe the result of a detached turn including state, text, parts, usage, and optional error or cache flag */\nexport interface DetachedTurnResult {\n /** `completed` — clean drain: persist + bill. `failed` — a terminal error\n * event, including the producer's structured `sandbox.stream_failed` event\n * when the raw sandbox stream throws: skip billing, render an error row. */\n state: 'completed' | 'failed'\n text: string\n /** The structured assistant body to persist (tool calls, file/plan/interaction\n * parts). Empty array when the run produced none. */\n parts: DetachedTurnParts\n usage: ChatTurnUsage\n /** Present when `state === 'failed'`. */\n error?: string\n /** True when a prior buffer meant this call returned a cached/finished result\n * WITHOUT re-streaming (durable-driver retry after a crash). */\n cached: boolean\n /** The durable assistant row this call wrote, when `persist` was wired.\n * `null` when the turn produced nothing and the row was retracted. Absent\n * when the caller owns persistence (today's behavior). */\n messageId?: string | null\n /** The model that SERVED the turn — the fallback's id when failover moved it.\n * A caller that bills or scores per model must read this, not the model it\n * requested. */\n model?: string\n /** The caller's explicit model request, before shell failover. */\n requestedModel?: string\n /** The effective model echoed by the downstream sandbox. */\n servedModel?: string\n /** The effective provider echoed by the downstream sandbox. */\n servedProvider?: string\n /** How the downstream sandbox selected the effective model. */\n servedSource?: 'request' | 'environment' | 'profile'\n /** True when the preferred model did not serve. Makes an autonomous\n * downgrade — which no human watched happen — attributable after the fact. */\n usedModelFallback?: boolean\n /** Every model tried, in order, with the reason each was abandoned. */\n modelAttempts?: ModelFailoverAttempt[]\n}\n\n/** Terminal failure event types a producer may forward verbatim. */\nconst TERMINAL_ERROR_TYPES = new Set(['error', 'session.run.failed'])\n\nfunction errorMessageOf(ev: unknown): string {\n const rec = ev as { data?: { message?: unknown; reason?: unknown }; message?: unknown } | null\n const raw = rec?.data?.message ?? rec?.data?.reason ?? rec?.message\n return typeof raw === 'string' && raw ? raw : 'run failed'\n}\n\nfunction hasUsage(usage: ChatTurnUsage): boolean {\n return typeof usage.inputTokens === 'number' && usage.inputTokens > 0\n}\n\nfunction cachedResultFrom(\n final: DetachedTurnFinal | null,\n persisted?: DraftStoredMessage,\n): DetachedTurnResult {\n if (!final && !persisted) {\n throw new Error('Completed turn has no retained result or assistant row; retry exact recovery')\n }\n // A text-only recovery must not erase usage already stored for this turn.\n // Unknown values stay absent; an explicit measured zero is preserved.\n const usage: ChatTurnUsage = {}\n for (const key of [\n 'inputTokens', 'outputTokens', 'reasoningTokens',\n 'cacheReadTokens', 'cacheWriteTokens', 'costUsd',\n ] as const) {\n const value = final?.usage?.[key] ?? persisted?.[key]\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) usage[key] = value\n }\n return {\n state: 'completed',\n text: final?.text ?? persisted?.content ?? '',\n parts:\n final?.parts ??\n (persisted?.parts as DetachedTurnParts | null | undefined) ??\n [],\n usage,\n cached: true,\n }\n}\n\n/**\n * Stream a detached turn into the live turn-event buffer, durably.\n *\n * - Idempotent: an already-`complete` turn returns the cached result without\n * re-streaming (a second event sequence would collide with the buffered one).\n * - Crash-safe: a `running` turn (a prior attempt crashed mid-tap) consults\n * `completedResult` to detect a run that finished server-side; only a run that\n * genuinely did not complete is re-streamed, and then over a buffer the\n * store's `resetEvents` cleared so seqs don't corrupt. A store without\n * `resetEvents` refuses that re-stream and leaves the existing buffer intact.\n * - Marks the turn `running` under `scopeId` so a mid-run browser finds it.\n * - Settles `complete`/`error` so the client stops tailing and billing/render\n * can branch on `state`.\n */\nexport async function runDetachedTurn(opts: DetachedTurnOptions): Promise<DetachedTurnResult> {\n const { store, turnId, scopeId } = opts\n\n // Hoisted so the draft writer's snapshot can read the producer's live\n // accumulators; assigned once the idempotency branches decide to re-stream.\n let producer: ReturnType<typeof createSandboxChatProducer> | undefined\n\n /** The model that actually served. Read LAZILY (never captured up front): on\n * a cached/finished-server-side path no producer exists and the preferred\n * model is the best available answer, but on a live re-stream failover may\n * have moved the turn after the row was first drafted. */\n const servingModel = (): string | undefined => producer?.model ?? opts.model\n\n const persistedRow = async (): Promise<DraftStoredMessage | undefined> => {\n if (!opts.persist) return undefined\n return (await opts.persist.store.listMessages(opts.persist.threadId)).find(\n (message) =>\n message.id ===\n (opts.persist?.messageId ?? assistantRowIdForTurn(turnId)),\n )\n }\n\n // Durable row ownership (opt-in). Built before the idempotency branches so a\n // cached/finished-server-side re-invoke still converges the row instead of\n // leaving whatever partial state a crashed attempt wrote.\n let draft: AssistantDraftWriter | undefined\n if (opts.persist) {\n const { store: persistStore, threadId, messageId, transformText, ...tuning } = opts.persist\n if (!storeSupportsDraftPersistence(persistStore)) {\n throw new Error(\n 'runDetachedTurn persist requires a store with updateMessage() — `/chat-store`\\'s createChatStore has it',\n )\n }\n draft = createAssistantDraftWriter({\n ...tuning,\n store: persistStore,\n threadId,\n messageId: messageId ?? assistantRowIdForTurn(turnId),\n snapshot: () => (producer\n ? {\n content: producer.finalText?.() ?? '',\n ...(producer.draftParts ? { parts: producer.draftParts() } : {}),\n ...(producer.usage ? { usage: producer.usage() } : {}),\n ...(servingModel() ? { model: servingModel() } : {}),\n }\n : null),\n ...(transformText ? { transformText } : {}),\n ...(opts.log ? { log: opts.log } : {}),\n })\n }\n\n /** Settle the durable row with authoritative values (or retract it when the\n * turn produced nothing), and stamp the id onto the result. */\n const settleRow = async (\n base: DetachedTurnResult,\n cachedPersisted?: DraftStoredMessage,\n ): Promise<DetachedTurnResult> => {\n // A cached/reconciled return has no producer to replay the sidecar echo.\n // Preserve the attribution already written by the prior attempt instead\n // of replacing its served `model` with today's requested-model fallback.\n const persisted =\n cachedPersisted ?? (!producer ? await persistedRow() : undefined)\n const info = producer?.modelFailover?.()\n const attribution = producer?.modelAttribution?.()\n const model = producer?.model ?? persisted?.model ?? opts.model\n const requestedModel = attribution?.requestedModel ?? persisted?.requestedModel ?? opts.model\n const servedModel = attribution?.servedModel ?? persisted?.servedModel\n const servedProvider = attribution?.servedProvider ?? persisted?.servedProvider\n const servedSource = attribution?.servedSource ?? (\n persisted?.servedSource === 'request' ||\n persisted?.servedSource === 'environment' ||\n persisted?.servedSource === 'profile'\n ? persisted.servedSource\n : undefined\n )\n const result: DetachedTurnResult = {\n ...base,\n ...(model ? { model } : {}),\n ...(requestedModel ? { requestedModel } : {}),\n ...(servedModel ? { servedModel } : {}),\n ...(servedProvider ? { servedProvider } : {}),\n ...(servedSource ? { servedSource } : {}),\n ...(info ? { usedModelFallback: info.usedFallback, modelAttempts: info.attempts } : {}),\n }\n if (!draft) return result\n const transform = opts.persist?.transformText\n const content = transform ? await transform(result.text) : result.text\n const rawParts = transform\n ? await Promise.all(\n result.parts.map(async (part) =>\n String((part as { type?: unknown }).type ?? '') === 'text'\n ? { ...part, text: await transform(String((part as { text?: unknown }).text ?? '')) }\n : part,\n ),\n )\n : result.parts\n const parts = toChatMessageParts(rawParts)\n if (!content.trim() && parts.length === 0) {\n await draft.discard()\n return { ...result, messageId: null }\n }\n const values: AssistantRowValues = {\n content,\n ...(parts.length > 0 ? { parts } : {}),\n ...(result.model ? { model: result.model } : {}),\n ...(result.requestedModel ? { requestedModel: result.requestedModel } : {}),\n ...(result.servedModel ? { servedModel: result.servedModel } : {}),\n ...(result.servedProvider ? { servedProvider: result.servedProvider } : {}),\n ...(result.servedSource ? { servedSource: result.servedSource } : {}),\n ...(result.usage.inputTokens !== undefined ? { inputTokens: result.usage.inputTokens } : {}),\n ...(result.usage.outputTokens !== undefined ? { outputTokens: result.usage.outputTokens } : {}),\n ...(result.usage.reasoningTokens !== undefined ? { reasoningTokens: result.usage.reasoningTokens } : {}),\n ...(result.usage.cacheReadTokens !== undefined ? { cacheReadTokens: result.usage.cacheReadTokens } : {}),\n ...(result.usage.cacheWriteTokens !== undefined ? { cacheWriteTokens: result.usage.cacheWriteTokens } : {}),\n ...(result.usage.costUsd !== undefined ? { costUsd: result.usage.costUsd } : {}),\n }\n await draft.finalize(values)\n return { ...result, messageId: draft.rowId() ?? null }\n }\n\n const completed = async (): Promise<DetachedTurnFinal | null> => {\n if (!opts.completedResult) return null\n // An unavailable authoritative read must not become a fresh stream.\n return (await opts.completedResult()) ?? null\n }\n\n const prior = await store.getStatus(turnId)\n\n if (prior === 'complete') {\n const persisted = await persistedRow()\n return await settleRow(\n cachedResultFrom(await completed(), persisted),\n persisted,\n )\n }\n\n if (prior === 'running') {\n // A prior attempt marked the turn running and then this worker crashed. The\n // detached SESSION may have finished server-side while we were gone — the\n // authoritative check is `completedResult` (findCompletedTurn). If it\n // completed, settle the stuck `running` buffer and return it.\n const final = await completed()\n if (final) {\n await store.setStatus(turnId, 'complete', scopeId)\n const persisted = await persistedRow()\n return await settleRow(cachedResultFrom(final, persisted), persisted)\n }\n // Genuine re-run: clear the partial buffer first, or the fresh tap's seq\n // (restarting at 0) interleaves with the orphaned rows.\n if (!store.resetEvents) {\n throw new Error('Detached turn recovery requires a TurnEventStore with resetEvents before replaying an existing stream')\n }\n await store.resetEvents(turnId)\n }\n\n const tap = createBufferedTurnTap({\n store,\n turnId,\n scopeId,\n coalesce: opts.coalesce ?? coalesceDeltas,\n })\n // Leading turn marker: flips the buffer to `running` (so `listRunning` finds\n // it) and is the browser's `/replay` resume handle.\n await tap.onEvent({ type: 'turn', turnId })\n\n producer = createSandboxChatProducer({\n ...(opts.openEvents ? { openEvents: opts.openEvents } : { events: opts.events }),\n model: opts.model,\n ...(opts.fallbackModels ? { fallbackModels: opts.fallbackModels } : {}),\n ...(opts.modelFailover === false ? { modelFailover: false as const } : {}),\n ...(opts.openTimeoutMs !== undefined ? { openTimeoutMs: opts.openTimeoutMs } : {}),\n ...(opts.firstResponseTimeoutMs !== undefined\n ? { firstResponseTimeoutMs: opts.firstResponseTimeoutMs }\n : {}),\n ...(opts.onModelFallback ? { onModelFallback: opts.onModelFallback } : {}),\n isRenderableInteraction: opts.isRenderableInteraction,\n declineInteraction: opts.declineInteraction,\n promoteFilePart: opts.promoteFilePart,\n log: opts.log,\n })\n\n let runError: string | undefined\n try {\n for await (const ev of producer.stream) {\n const type = (ev as { type?: unknown }).type\n if (typeof type === 'string' && TERMINAL_ERROR_TYPES.has(type)) runError = errorMessageOf(ev)\n await tap.onEvent(ev)\n // Same coalesced cadence the interactive lane uses: the durable row\n // tracks the run so a viewer arriving after the hot buffer's short window\n // reads it from storage.\n draft?.notify(ev as { type?: unknown })\n }\n await tap.done(runError ? 'error' : 'complete')\n } catch (err) {\n await tap.done('error').catch(() => {})\n // Settle any in-flight draft before propagating so the partial row a\n // re-invoke will adopt is a complete write, not a half-landed one.\n await draft?.close().catch(() => {})\n throw err\n }\n\n const text = producer.finalText?.() ?? ''\n const parts = producer.assistantParts?.() ?? []\n let usage: ChatTurnUsage = producer.usage?.() ?? {}\n\n // A terminal result can supply scalar text even when the event subscriber\n // missed every structured message part. Reconcile that text-only shape with\n // the durable completion record before final persistence.\n const onlyTextParts = parts.every((part) =>\n String((part as { type?: unknown }).type ?? '') === 'text',\n )\n if (!runError && (!hasUsage(usage) || !text || onlyTextParts)) {\n const final = await completed()\n if (final?.usage) usage = { ...usage, ...final.usage }\n return await settleRow({\n state: 'completed',\n text: final?.text ?? text,\n parts: final?.parts?.length ? final.parts : parts,\n usage,\n cached: false,\n })\n }\n\n if (runError) return await settleRow({ state: 'failed', text, parts, usage, error: runError, cached: false })\n return await settleRow({ state: 'completed', text, parts, usage, cached: false })\n}\n","/**\n * Recover a detached Sandbox turn from its durable completed records.\n *\n * A fast detached run can finish before the live event subscriber receives\n * every message-part event. The Sandbox still retains two exact records: a\n * turn-id keyed result cache and the completed assistant message on the\n * session. This adapter joins them without guessing across turns, then returns\n * the same `DetachedTurnFinal` shape `runDetachedTurn` already consumes.\n */\n\nimport type { SandboxInstance, SessionMessage } from '@tangle-network/sandbox'\nimport {\n asRecord,\n asString,\n collapseRedundantTextParts,\n finalizeAssistantParts,\n getPartKey,\n mergePersistedPart,\n normalizePersistedPart,\n type JsonRecord,\n} from '../stream/index'\nimport { addStepFinishUsage } from './sandbox-turn-usage'\nimport type { DetachedTurnFinal } from './detached-turn'\nimport type { ChatTurnModelAttribution, ChatTurnUsage } from './turn-routes'\n\n/** The official Sandbox methods needed for completed-turn recovery. */\nexport type CompletedSandboxTurnSource = Pick<\n SandboxInstance,\n 'findCompletedTurn' | 'session'\n>\n\n/** Options for resolving one exact detached turn. */\nexport interface ReadCompletedSandboxTurnOptions {\n turnId: string\n sessionId: string\n /** Read only session messages newer than this Unix-ms time. A long session\n * holds more than one page of messages, oldest first, so an unbounded read\n * both grows with the session and can miss the newest turn. */\n since?: number\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\nfunction finiteNumber(value: unknown): number | undefined {\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined\n if (typeof value !== 'string' || !value.trim()) return undefined\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : undefined\n}\n\nfunction firstNumber(record: JsonRecord | undefined, keys: string[]): number | undefined {\n for (const key of keys) {\n const value = finiteNumber(record?.[key])\n if (value !== undefined) return value\n }\n return undefined\n}\n\n/** @internal Shared terminal-result decoder; callers must establish exact turn identity. */\nexport function usageFromResult(result: JsonRecord | undefined): ChatTurnUsage {\n const raw = asRecord(result?.usage) ?? asRecord(result?.tokenUsage)\n const inputTokens = firstNumber(raw, ['inputTokens', 'promptTokens', 'input'])\n const outputTokens = firstNumber(raw, ['outputTokens', 'completionTokens', 'output'])\n const reasoningTokens = firstNumber(raw, ['reasoningTokens', 'reasoning'])\n const cacheReadTokens = firstNumber(raw, [\n 'cacheReadTokens',\n 'cacheReadInputTokens',\n 'cacheRead',\n ])\n const cacheWriteTokens = firstNumber(raw, [\n 'cacheWriteTokens',\n 'cacheCreationInputTokens',\n 'cacheWrite',\n ])\n const costUsd = firstNumber(result, ['costUsd', 'totalCostUsd', 'cost'])\n ?? firstNumber(raw, ['costUsd', 'cost'])\n\n return {\n ...(inputTokens !== undefined ? { inputTokens } : {}),\n ...(outputTokens !== undefined ? { outputTokens } : {}),\n ...(reasoningTokens !== undefined ? { reasoningTokens } : {}),\n ...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),\n ...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),\n ...(costUsd !== undefined ? { costUsd } : {}),\n }\n}\n\nfunction nonEmptyString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined\n}\n\n/** @internal Attribution travels with the same validated result as content and usage. */\nexport function attributionFromResult(result: Record<string, unknown> | undefined): Pick<\n ChatTurnModelAttribution,\n 'servedModel' | 'servedProvider' | 'servedSource'\n> {\n const metadata = asRecord(result?.metadata)\n const modelAttribution = asRecord(result?.modelAttribution) ?? asRecord(metadata?.modelAttribution)\n const backend = asRecord(result?.effectiveBackend) ?? asRecord(metadata?.effectiveBackend)\n const source = nonEmptyString(result?.servedSource)\n ?? nonEmptyString(modelAttribution?.servedSource)\n ?? nonEmptyString(backend?.source)\n const servedSource = source === 'request' || source === 'environment' || source === 'profile'\n ? source\n : undefined\n const servedModel = nonEmptyString(result?.servedModel)\n ?? nonEmptyString(modelAttribution?.servedModel)\n ?? nonEmptyString(backend?.model)\n const servedProvider = nonEmptyString(result?.servedProvider)\n ?? nonEmptyString(modelAttribution?.servedProvider)\n ?? nonEmptyString(backend?.provider)\n return {\n ...(servedModel ? { servedModel } : {}),\n ...(servedProvider ? { servedProvider } : {}),\n ...(servedSource ? { servedSource } : {}),\n }\n}\n\nfunction textFromResult(result: JsonRecord | undefined): string | undefined {\n return asString(result?.response)\n ?? asString(result?.finalText)\n ?? asString(result?.text)\n ?? asString(result?.output)\n}\n\nfunction completedMessagesForTurn(\n messages: SessionMessage[],\n turnId: string,\n): SessionMessage[] {\n return messages.filter((message) =>\n message.role === 'assistant'\n && message.metadata?.turnId === turnId\n && message.metadata?.interrupted !== true\n && (\n message.metadata?.completed === true\n || message.metadata?.status === 'completed'\n ),\n )\n}\n\nfunction normalizedMessageParts(\n message: SessionMessage,\n): { parts: JsonRecord[]; usage: ChatTurnUsage; derivedText: string } {\n const normalized: JsonRecord[] = []\n const usage: ChatTurnUsage = {}\n for (const raw of message.parts) {\n const record = asRecord(raw)\n if (!record) continue\n const part = normalizePersistedPart(record)\n if (!part) continue\n normalized.push(part)\n if (part.type === 'step-finish') addStepFinishUsage(part, usage)\n }\n\n const collapsed = collapseRedundantTextParts(normalized)\n const derivedText = collapsed\n .filter((part) => part.type === 'text')\n .map((part) => String(part.text ?? ''))\n .join('')\n return { parts: normalized, usage, derivedText }\n}\n\nfunction projectMessageParts(parts: JsonRecord[], finalText: string): JsonRecord[] {\n const order: string[] = []\n const map = new Map<string, JsonRecord>()\n for (let index = 0; index < parts.length; index += 1) {\n const part = parts[index]!\n const type = String(part.type ?? '')\n // A session's aggregate message contains every step receipt. Preserve each\n // occurrence just as the live producer does; other kinds merge by their\n // canonical identity.\n const key = type === 'step-start' || type === 'step-finish'\n ? `${type}:#${index}`\n : getPartKey(part)\n if (!map.has(key)) order.push(key)\n map.set(key, mergePersistedPart(map.get(key), part))\n }\n return finalizeAssistantParts(order, map, finalText)\n}\n\n/**\n * Recover the durable content a Sandbox recorder attached to one assistant\n * message. The caller is responsible for proving that the message belongs to\n * the exact terminal turn; this projection deliberately makes no conclusion\n * about whether that terminal state was completed or interrupted.\n */\nexport function recoverSandboxAssistantMessage(message: SessionMessage): DetachedTurnFinal {\n const recovered = normalizedMessageParts(message)\n return {\n ...(recovered.derivedText ? { text: recovered.derivedText } : {}),\n ...(hasUsage(recovered.usage) ? { usage: recovered.usage } : {}),\n parts: projectMessageParts(recovered.parts, recovered.derivedText),\n }\n}\n\nfunction hasUsage(usage: ChatTurnUsage): boolean {\n return Object.values(usage).some((value) =>\n typeof value === 'number' && Number.isFinite(value),\n )\n}\n\n/**\n * Read the exact completed turn from its keyed cache and completed message.\n * Never borrow a session-wide aggregate from a concurrently advancing session.\n * Observation failure is retryable and distinct from successful absent reads.\n * Model attribution comes from that same identity-checked cache record, never\n * from a second unchecked lookup that could belong to a different turn.\n */\nexport async function readCompletedSandboxTurn(\n box: CompletedSandboxTurnSource,\n options: ReadCompletedSandboxTurnOptions,\n): Promise<(DetachedTurnFinal & Pick<ChatTurnModelAttribution, 'servedModel' | 'servedProvider' | 'servedSource'>) | null> {\n const { turnId, sessionId, log } = options\n const session = box.session(sessionId)\n const [cacheOutcome, messagesOutcome] = await Promise.allSettled([\n box.findCompletedTurn(turnId, { sessionId }),\n session.messages({ limit: 1_000, ...(options.since !== undefined ? { since: options.since } : {}) }),\n ])\n\n if (cacheOutcome.status === 'rejected') {\n log?.('[chat-routes] completed Sandbox turn cache lookup failed', {\n turnId,\n sessionId,\n error: String(cacheOutcome.reason),\n })\n }\n if (messagesOutcome.status === 'rejected') {\n log?.('[chat-routes] completed Sandbox session message lookup failed', {\n turnId,\n sessionId,\n error: String(messagesOutcome.reason),\n })\n }\n\n const rawCached = cacheOutcome.status === 'fulfilled' ? cacheOutcome.value : null\n const cached = rawCached\n && rawCached.turnId === turnId\n && rawCached.sessionId === sessionId\n ? rawCached\n : null\n if (rawCached && !cached) {\n log?.('[chat-routes] ignored mismatched completed Sandbox turn cache record', {\n turnId,\n sessionId,\n })\n }\n\n const messages = messagesOutcome.status === 'fulfilled'\n ? messagesOutcome.value\n : []\n const matchingMessages = completedMessagesForTurn(messages, turnId)\n const matchingCount = matchingMessages.length\n const message = matchingCount === 1 ? matchingMessages[0]! : null\n if (matchingCount > 1) {\n log?.('[chat-routes] ignored ambiguous completed Sandbox session messages', {\n turnId,\n sessionId,\n matchingCount,\n })\n }\n\n if (!cached && !message) {\n // A failed read is unknown, not proof that the turn is absent. The durable\n // owner must retry observation rather than admit another execution.\n if (cacheOutcome.status === 'rejected' || messagesOutcome.status === 'rejected') {\n throw new Error('Completed Sandbox turn could not be verified; retry the exact read')\n }\n if (rawCached || matchingCount > 1) {\n throw new Error('Completed Sandbox turn identity is inconsistent; reconciliation is required')\n }\n return null\n }\n\n // Only turn-addressed evidence is safe. A newer turn can complete between\n // reading the message list and reading a session-wide aggregate result.\n const result = cached ? asRecord(cached.result) : undefined\n\n const recovered = message ? normalizedMessageParts(message) : null\n const text = textFromResult(result) ?? recovered?.derivedText\n const resultUsage = usageFromResult(result)\n const usage = { ...(recovered?.usage ?? {}), ...resultUsage }\n const resultParts = Array.isArray(result?.parts)\n ? result.parts\n .map((part) => asRecord(part))\n .filter((part): part is JsonRecord => Boolean(part))\n .map((part) => normalizePersistedPart(part))\n .filter((part): part is JsonRecord => Boolean(part))\n : null\n const sourceParts = recovered?.parts ?? resultParts\n const parts = sourceParts\n ? projectMessageParts(sourceParts, text ?? recovered?.derivedText ?? '')\n : undefined\n\n return {\n ...(text !== undefined ? { text } : {}),\n ...(hasUsage(usage) ? { usage } : {}),\n ...(parts ? { parts } : {}),\n ...attributionFromResult(result),\n }\n}\n","/**\n * Exact terminal observation for a native Sandbox execution that another\n * request already admitted. This module never sends a prompt, calls\n * `driveTurn`, or cancels a run.\n */\n\nimport type { SandboxInstance, SessionInfo, SessionMessage } from '@tangle-network/sandbox'\nimport type { ChatTurnUsage } from './turn-routes'\nimport type { SessionExecutionInfo } from '@tangle-network/sandbox'\nimport {\n attributionFromResult,\n readCompletedSandboxTurn,\n recoverSandboxAssistantMessage,\n usageFromResult,\n} from './completed-sandbox-turn'\n\nexport interface NativeCompletionReceipt {\n state: 'completed' | 'failed'\n text: string\n parts: Array<Record<string, unknown>>\n /** An empty object is an unknown usage receipt, never a measured zero. */\n usage: ChatTurnUsage\n servedModel?: string\n servedProvider?: string\n servedSource?: 'request' | 'environment' | 'profile'\n error?: string\n completedTurnIds: string[]\n}\n\nexport interface NativeCompletionAdmission {\n executionId: string\n state: 'open' | 'closed'\n admittedTurnIds: readonly string[]\n ownerLeaseUntil: Date | number\n updatedAt?: Date | number\n closedAt?: Date | number | null\n}\n\n/** Product persistence for the one admission that authorizes this observer. */\nexport interface NativeCompletionAdmissionStore {\n read(executionId: string): Promise<NativeCompletionAdmission | null>\n renew(executionId: string, now: Date): Promise<NativeCompletionAdmission | null>\n closeExpired(executionId: string, now: Date): Promise<NativeCompletionAdmission | null>\n}\n\n/** The official Sandbox reads needed for exact native completion observation. */\nexport type NativeCompletionSessionSource = Pick<SandboxInstance, 'findCompletedTurn' | 'session'>\n\nexport interface NativeCompletionObservationOptions {\n /** Null when the product cannot currently resolve the admitted Sandbox. */\n source: NativeCompletionSessionSource | null\n admissionStore: NativeCompletionAdmissionStore\n executionId: string\n sessionId: string\n turnId: string\n registeredAt: number\n absentDispatchDeadlineMs?: number\n receiptDeadlineMs?: number\n now?: number\n}\n\nexport type NativeCompletionObservation =\n | { state: 'running' }\n | { state: 'completed'; receipt: NativeCompletionReceipt }\n | { state: 'failed'; receipt: NativeCompletionReceipt }\n\n/** One exact turn receipt before an admission's ordered aggregate. */\nexport interface NativeCompletionTurnReceipt {\n turnId: string\n state: 'completed' | 'failed'\n text: string\n parts: Array<Record<string, unknown>>\n usage: ChatTurnUsage\n servedModel?: string\n servedProvider?: string\n servedSource?: 'request' | 'environment' | 'profile'\n error?: string\n}\n\nconst DEFAULT_ABSENT_DISPATCH_DEADLINE_MS = 10 * 60_000\nconst DEFAULT_RECEIPT_DEADLINE_MS = 10 * 60_000\n/** Session messages are read from this long before registration, so clock\n * skew between the product Worker and the Sandbox cannot hide the turn. */\nconst MESSAGE_READ_SKEW_MS = 10 * 60_000\n\n/**\n * Largest interrupted execution whose event stream is replayed for its\n * terminal result. The replay parses every event and re-compares the growing\n * response text on each token, so its CPU grows faster than the run; a longer\n * run takes its receipt from the recorded message and failure reason alone.\n */\nconst INTERRUPTED_RESULT_REPLAY_MAX_EVENTS = 2_000\n/**\n * The fixed id of the terminal the sidecar appends when it fails an execution\n * whose runtime restarted before the run ended.\n */\nconst RUNTIME_RESTART_TERMINAL_EVENT_ID = 'interrupted-done'\n/** The sidecar's reason for that terminal, used when the replay is not read. */\nexport const RUNTIME_RESTARTED_ERROR = 'Execution interrupted: the agent runtime restarted before the run produced a terminal event. The run cannot be resumed; retry it.'\n\nfunction replayableExecution(run: Pick<SessionExecutionInfo, 'eventCount'>): boolean {\n return typeof run.eventCount === 'number' && run.eventCount <= INTERRUPTED_RESULT_REPLAY_MAX_EVENTS\n}\n\n/**\n * Serialized UTF-8 budget for one terminal receipt. A Cloudflare Workflow\n * refuses a step result over 1 MiB and D1 refuses a row over 2 MB, and a\n * receipt approaches both as tool payloads accumulate over a long turn.\n */\nexport const NATIVE_COMPLETION_RECEIPT_MAX_BYTES = 900 * 1024\n\n/** Per-string caps tried in order until a receipt fits its budget. */\n// Status, ids and paths stay well under the smallest cap.\nconst RECEIPT_STRING_CAPS = [16_384, 4_096, 1_024, 256] as const\n\nconst encoder = new TextEncoder()\n\nfunction serializedBytes(value: unknown): number {\n return encoder.encode(JSON.stringify(value)).length\n}\n\nfunction capString(value: string, cap: number): string {\n if (value.length <= cap) return value\n return `${value.slice(0, cap)}…[truncated ${value.length - cap} characters; the Sandbox session keeps the full value]`\n}\n\nfunction capStrings(value: unknown, cap: number): unknown {\n if (typeof value === 'string') return capString(value, cap)\n if (Array.isArray(value)) return value.map((item) => capStrings(item, cap))\n const object = record(value)\n if (!object) return value\n return Object.fromEntries(Object.entries(object).map(([key, item]) => [key, capStrings(item, cap)]))\n}\n\n/** Tool payloads and reasoning shrink; the answer text, files and interactions do not. */\nfunction capReceiptPart(part: Record<string, unknown>, cap: number): Record<string, unknown> {\n const type = String(part.type ?? '')\n if (type === 'tool') {\n const state = record(part.state)\n if (!state) return part\n const capped = capStrings(state, cap) as Record<string, unknown>\n if (serializedBytes(capped) === serializedBytes(state)) return part\n return { ...part, state: { ...capped, metadata: { ...record(capped.metadata), receiptTruncated: true } } }\n }\n if (type === 'reasoning' && typeof part.text === 'string' && part.text.length > cap) {\n return { ...part, text: capString(part.text, cap), receiptTruncated: true }\n }\n return part\n}\n\n/** Last resort for a receipt whose part count alone exceeds the budget. */\nfunction skeletonPart(part: Record<string, unknown>): Record<string, unknown> | null {\n const type = String(part.type ?? '')\n if (type === 'reasoning') return null\n if (type !== 'tool') return part\n const state = record(part.state)\n return {\n type: 'tool',\n ...(part.id !== undefined ? { id: part.id } : {}),\n ...(part.tool !== undefined ? { tool: part.tool } : {}),\n state: { status: state?.status ?? 'completed', metadata: { receiptTruncated: true } },\n }\n}\n\n/**\n * Fit a terminal receipt inside {@link NATIVE_COMPLETION_RECEIPT_MAX_BYTES}.\n * A receipt under the budget is returned unchanged. Over it, the longest\n * strings in tool payloads and reasoning are clipped with a marker, at\n * successively smaller caps, so a long turn's answer is always persistable.\n */\nexport function boundNativeCompletionReceipt(\n receipt: NativeCompletionReceipt,\n maxBytes: number = NATIVE_COMPLETION_RECEIPT_MAX_BYTES,\n): NativeCompletionReceipt {\n if (serializedBytes(receipt) <= maxBytes) return receipt\n for (const cap of RECEIPT_STRING_CAPS) {\n const bounded = { ...receipt, parts: receipt.parts.map((part) => capReceiptPart(part, cap)) }\n if (serializedBytes(bounded) <= maxBytes) return bounded\n }\n const skeleton = {\n ...receipt,\n parts: receipt.parts.map(skeletonPart).filter((part): part is Record<string, unknown> => part !== null),\n }\n if (serializedBytes(skeleton) <= maxBytes) return skeleton\n // Only the answer text is left to shrink.\n const textBudget = Math.max(0, Math.floor(maxBytes / 4))\n return {\n ...skeleton,\n text: capString(skeleton.text, textBudget),\n parts: skeleton.parts.map((part) => (part.type === 'text' && typeof part.text === 'string'\n ? { ...part, text: capString(part.text, textBudget) }\n : part)),\n }\n}\n\nfunction terminal(receipt: NativeCompletionReceipt): NativeCompletionObservation {\n const bounded = boundNativeCompletionReceipt(receipt)\n return bounded.state === 'completed'\n ? { state: 'completed', receipt: bounded }\n : { state: 'failed', receipt: bounded }\n}\n\nfunction timestamp(value: Date | number | null | undefined): number | undefined {\n if (value instanceof Date) return value.getTime()\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : undefined\n}\n\nfunction isSandboxNotFoundError(value: unknown): boolean {\n const error = record(value)\n return error?.name === 'NotFoundError' || error?.code === 'NOT_FOUND' || error?.status === 404\n}\n\nfunction interruptedMessages(messages: SessionMessage[], turnId: string): SessionMessage[] {\n return messages.filter((message) =>\n message.role === 'assistant'\n && message.metadata?.turnId === turnId\n && (message.metadata.interrupted === true || message.metadata.status === 'interrupted'),\n )\n}\n\nfunction turnAssistantMessages(messages: SessionMessage[], turnId: string): SessionMessage[] {\n return messages.filter((message) => message.role === 'assistant' && message.metadata?.turnId === turnId)\n}\n\nfunction consistent<T>(values: Array<T | undefined>): T | undefined {\n if (values.length === 0 || values.some((value) => value === undefined)) return undefined\n const first = values[0] as T\n return values.every((value) => value === first) ? first : undefined\n}\n\nfunction sum(values: Array<number | undefined>): number | undefined {\n if (values.some((value) => value === undefined)) return undefined\n return (values as number[]).reduce((total, value) => total + value, 0)\n}\n\n/** Assemble admitted exact receipts in dispatch order. Missing usage stays unknown. */\nexport function aggregateNativeCompletionReceipts(turns: readonly NativeCompletionTurnReceipt[]): NativeCompletionReceipt {\n const failed = turns.some((turn) => turn.state === 'failed')\n const usage: ChatTurnUsage = {}\n for (const key of ['inputTokens', 'outputTokens', 'reasoningTokens', 'cacheReadTokens', 'cacheWriteTokens', 'costUsd'] as const) {\n const total = sum(turns.map((turn) => turn.usage[key]))\n if (total !== undefined) usage[key] = total\n }\n // A missing exact receipt is the terminal observer failure even when an\n // earlier execution also failed. Preserve that earlier partial transcript,\n // but surface why this aggregate could not be fully reconciled.\n const error = turns.find((turn) =>\n turn.error?.startsWith('Admitted native execution did not produce')\n || turn.error?.startsWith('Native Sandbox execution ledger is missing'),\n )?.error ?? turns.find((turn) => turn.error)?.error\n return {\n state: failed ? 'failed' : 'completed',\n text: turns.map((turn) => turn.text).join(''),\n parts: turns.flatMap((turn) => turn.parts),\n usage,\n ...(consistent(turns.map((turn) => turn.servedModel)) ? { servedModel: consistent(turns.map((turn) => turn.servedModel)) } : {}),\n ...(consistent(turns.map((turn) => turn.servedProvider)) ? { servedProvider: consistent(turns.map((turn) => turn.servedProvider)) } : {}),\n ...(consistent(turns.map((turn) => turn.servedSource)) ? { servedSource: consistent(turns.map((turn) => turn.servedSource)) } : {}),\n ...(error ? { error } : {}),\n completedTurnIds: turns.map((turn) => turn.turnId),\n }\n}\n\n/** The failure a completed run with an aborted tool call settles with. */\nexport const TOOL_ABORTED_ERROR = 'The Sandbox aborted a tool call before the turn finished, as when its runtime restarts mid-turn'\n\n/**\n * The name of a tool whose call the Sandbox aborted, or undefined. OpenCode\n * marks a tool interrupted by a runtime restart with \"Tool execution aborted\";\n * an ordinary tool error (a missing file, an HTTP 403) does not end the turn.\n */\nfunction abortedToolCall(parts: ReadonlyArray<Record<string, unknown>>): string | undefined {\n for (const part of parts) {\n if (part.type !== 'tool') continue\n const state = part.state as { status?: unknown; error?: unknown } | undefined\n if (state?.status === 'error' && /tool execution aborted/i.test(String(state.error ?? ''))) {\n return typeof part.tool === 'string' ? part.tool : 'tool'\n }\n }\n return undefined\n}\n\n/**\n * Whether the session still runs this admission's work, so its lease should be\n * renewed. The session's own status is not enough: it keeps a persisted\n * `running` status when the runtime restarts under it (the sidecar may report\n * that as recovery state `stale`, or not at all), while its execution ledger\n * has already failed the interrupted run. Renewing on status alone observed\n * GTM b81b's restarted completions forever on 2026-10-10. Live means an\n * admitted turn's execution is active, or not yet recorded while its dispatch\n * can still be on the way.\n */\nfunction admittedWorkIsLive(\n status: SessionInfo,\n runsByTurnId: ReadonlyMap<string, SessionExecutionInfo>,\n admittedTurnIds: readonly string[],\n dispatchMayBePending: boolean,\n): boolean {\n if (status.status !== 'queued' && status.status !== 'running') return false\n if (status.raw?.state === 'stale') return false\n return admittedTurnIds.some((turnId) => {\n const run = runsByTurnId.get(turnId)\n return run ? run.status === 'active' : dispatchMayBePending\n })\n}\n\nfunction missingTurnReceipt(turnId: string, error: string): NativeCompletionTurnReceipt {\n // An execution ledger entry proves this turn reached a terminal state, but\n // not that its terminal transcript was retained. Keep any earlier exact\n // evidence when the bounded wait expires; `{}` means aggregate usage is\n // unknown because this turn has no usage receipt.\n return { turnId, state: 'failed', text: '', parts: [], usage: {}, error }\n}\n\n/**\n * Observe only the turns admitted under `executionId`. Transport failures throw\n * so the durable owner retries. A missing or partial terminal record remains\n * `running` until the configured deadline; it never starts another run.\n */\nexport async function observeNativeCompletion(\n options: NativeCompletionObservationOptions,\n): Promise<NativeCompletionObservation> {\n const now = options.now ?? Date.now()\n let admission = await options.admissionStore.read(options.executionId)\n if (!admission) throw new Error('Native completion admission is missing')\n if (admission.executionId !== options.executionId) throw new Error('Native completion admission identity conflict')\n const leaseUntil = timestamp(admission.ownerLeaseUntil) ?? 0\n const source = options.source\n const session = source?.session(options.sessionId)\n let status: SessionInfo | null\n if (!session) {\n status = null\n } else {\n try {\n status = await session.status()\n } catch (error) {\n if (!isSandboxNotFoundError(error)) throw error\n status = null\n }\n }\n\n if (!status || !session || !source) {\n if (admission.state === 'open' && leaseUntil <= now) {\n admission = await options.admissionStore.closeExpired(options.executionId, new Date(now)) ?? admission\n }\n if (admission.state === 'open' || now - options.registeredAt < (options.absentDispatchDeadlineMs ?? DEFAULT_ABSENT_DISPATCH_DEADLINE_MS)) {\n return { state: 'running' }\n }\n return terminal(aggregateNativeCompletionReceipts([\n missingTurnReceipt(options.turnId, 'Native Sandbox dispatch was not observed before the admission deadline'),\n ]))\n }\n const exactSession = session\n const dispatchMayBePending = now - options.registeredAt < (options.absentDispatchDeadlineMs ?? DEFAULT_ABSENT_DISPATCH_DEADLINE_MS)\n let runsByTurnId: Map<string, SessionExecutionInfo> | undefined\n const readRuns = async () => runsByTurnId ??= new Map((await exactSession.runs()).map((run) => [run.executionId, run]))\n\n if (admission.state === 'open') {\n if (admittedWorkIsLive(status, await readRuns(), admission.admittedTurnIds, dispatchMayBePending)) {\n admission = await options.admissionStore.renew(options.executionId, new Date(now)) ?? admission\n } else if (leaseUntil <= now) {\n admission = await options.admissionStore.closeExpired(options.executionId, new Date(now)) ?? admission\n }\n if (admission.state === 'open') return { state: 'running' }\n }\n // `closeExpired` may return the current row after another owner appended a\n // continuation. Read its full ordered ledger rather than observing the\n // earlier snapshot from before the close attempt.\n const admittedTurnIds = [...admission.admittedTurnIds]\n if (admittedTurnIds.length === 0 || admittedTurnIds[0] !== options.turnId) {\n throw new Error('Native completion admission has an invalid turn sequence')\n }\n\n const ledger = await readRuns()\n const missingExecution = admittedTurnIds.find((turnId) => !ledger.has(turnId))\n if (missingExecution && dispatchMayBePending) return { state: 'running' }\n if (admittedTurnIds.some((turnId) => ledger.get(turnId)?.status === 'active')) return { state: 'running' }\n\n // Completed recovery owns its message read. Load interrupted history only\n // when it is needed, and share that one read across failed continuations.\n // Both reads start shortly before registration, so their size follows this\n // turn rather than the whole session.\n const since = Math.max(0, options.registeredAt - MESSAGE_READ_SKEW_MS)\n let messages: SessionMessage[] | undefined\n const recovered: NativeCompletionTurnReceipt[] = []\n for (const turnId of admittedTurnIds) {\n const run = ledger.get(turnId)\n if (!run) {\n recovered.push(missingTurnReceipt(turnId, 'Native Sandbox execution ledger is missing the admitted turn'))\n continue\n }\n const completed = run.status === 'completed'\n ? await readCompletedSandboxTurn(source, { turnId, sessionId: options.sessionId, since })\n : null\n if (run.status === 'completed' && completed) {\n const aborted = abortedToolCall(completed.parts ?? [])\n recovered.push({\n ...completed,\n turnId,\n // A run the runtime ended while a tool was still running reports\n // completed, but its reply stops mid-work. It is a failed turn.\n state: aborted ? 'failed' : 'completed',\n text: completed.text ?? '',\n parts: completed.parts ?? [],\n usage: completed.usage ?? {},\n ...(aborted ? { error: `${TOOL_ABORTED_ERROR} (${aborted})` } : {}),\n })\n continue\n }\n // The process that would stamp a turn interrupted is gone when its runtime\n // restarts; the sidecar fails the execution on restart, and the turn's own\n // assistant message holds whatever reply it had streamed.\n const restarted = run.status === 'failed' && run.lastEventId === RUNTIME_RESTART_TERMINAL_EVENT_ID\n let interrupted = run.status === 'failed' || run.status === 'cancelled'\n ? interruptedMessages(messages ??= await exactSession.messages({ limit: 1_000, since }), turnId)\n : []\n if (restarted && interrupted.length === 0) interrupted = turnAssistantMessages(messages ?? [], turnId)\n if (restarted && interrupted.length !== 1) {\n recovered.push({ turnId, state: 'failed', text: '', parts: [], usage: {}, error: RUNTIME_RESTARTED_ERROR })\n continue\n }\n if ((run.status === 'failed' || run.status === 'cancelled') && interrupted.length === 1) {\n const message = recoverSandboxAssistantMessage(interrupted[0]!)\n const result = replayableExecution(run)\n ? await exactSession.result({ executionId: turnId }) as unknown as Record<string, unknown>\n : undefined\n recovered.push({\n turnId,\n state: 'failed',\n text: message.text ?? (typeof result?.response === 'string' ? result.response : undefined) ?? '',\n parts: message.parts ?? [],\n usage: { ...message.usage, ...usageFromResult(result) },\n ...attributionFromResult(result),\n error: (typeof result?.error === 'string' ? result.error : undefined)\n ?? interrupted[0]!.metadata?.interruptReason\n ?? (restarted ? RUNTIME_RESTARTED_ERROR : undefined)\n ?? status.failureReason?.message,\n })\n continue\n }\n const closedAt = timestamp(admission.closedAt)\n ?? timestamp(admission.updatedAt)\n ?? options.registeredAt\n if (now - closedAt < (options.receiptDeadlineMs ?? DEFAULT_RECEIPT_DEADLINE_MS)) return { state: 'running' }\n recovered.push(missingTurnReceipt(\n turnId,\n `Admitted native execution did not produce an exact completion: ${turnId}`,\n ))\n }\n return terminal(aggregateNativeCompletionReceipts(recovered))\n}\n","/**\n * The chat turn that attaches to a decided plan's follow-up execution.\n *\n * Deciding a plan does not run anything in the product. The Sandbox platform\n * enqueues the approved (or revision) execution itself under the turn id\n * `planFollowUpTurnId(planId, revision, outcome)`, and the Sidecar names that\n * execution `plan-followup-<sha256(sessionId \\0 turnId)>`. The browser card then\n * re-enters the chat route with a `planFollowUp` descriptor. That attach is a\n * real turn: it streams, persists an assistant row, and bills. It therefore\n * needs the same single-runner protection as a fresh turn.\n *\n * This module owns that attach: the descriptor check against the persisted\n * plan, the execution identity, the exclusive D1 claim on the `turn_status`\n * row, and the replay-then-follow event stream. Products keep their own\n * authorization, billing, and persistence around it.\n */\nimport { createHash, randomUUID } from 'node:crypto'\nimport type { SandboxEvent, SandboxInstance } from '@tangle-network/sandbox'\nimport { persistedPartToPlan, planFollowUpTurnId } from '../plans'\n\n/** The decision a follow-up continues. */\nexport type PlanFollowUpOutcome = 'approved' | 'rejected'\n\n/** The descriptor a decided plan card sends back to the chat route. */\nexport interface PlanFollowUpAttach {\n planId: string\n revision: number\n outcome: PlanFollowUpOutcome\n turnId: string\n}\n\n/** Result of checking a follow-up descriptor against the session's persisted plans. */\nexport type PlanFollowUpResolution =\n | { ok: true; attach: PlanFollowUpAttach; executionId: string }\n | { ok: false; status: 400 | 409; code: string; error: string }\n\n/** Result of one exclusive follow-up claim. */\nexport type PlanFollowUpAdmission =\n | { admitted: true; lease: string }\n | { admitted: false; reason: 'in_flight' | 'completed' }\n\n/** Result of settling a claim. `lease_lost` means a stale reclaim superseded it. */\nexport type PlanFollowUpSettlement =\n | { settled: true }\n | { settled: false; reason: 'lease_lost' }\n\n/** A bound D1 statement used by the follow-up gate. */\nexport interface D1BoundForPlanFollowUps {\n run(): Promise<unknown>\n}\n\n/**\n * Structural D1 contract for the follow-up gate. `batch` is required: it is\n * D1's atomic boundary, and the gate confirms ownership from the row read in\n * the same batch instead of from a driver-specific affected-row count.\n */\nexport interface D1LikeForPlanFollowUps {\n prepare(sql: string): { bind(...values: unknown[]): D1BoundForPlanFollowUps }\n batch(statements: D1BoundForPlanFollowUps[]): Promise<unknown[]>\n}\n\n/** Configure the follow-up gate. Clock and lease are injectable for tests. */\nexport interface PlanFollowUpGateOptions {\n now?: () => Date\n createLease?: () => string\n /** How long a `running` claim may sit before another isolate may reclaim it. Default one hour. */\n staleAfterMs?: number\n}\n\n/** Exclusive claim over one follow-up execution's `turn_status` row. */\nexport interface PlanFollowUpGate {\n admit(executionId: string, scopeId: string): Promise<PlanFollowUpAdmission>\n /** Record a completed attach. Only the current lease may settle. */\n settle(executionId: string, scopeId: string, lease: string): Promise<PlanFollowUpSettlement>\n /** Record a failed attach so a retry is admitted immediately. */\n abandon(executionId: string, scopeId: string, lease: string): Promise<PlanFollowUpSettlement>\n}\n\nconst PLAN_FOLLOW_UP_STALE_AFTER_MS = 60 * 60 * 1000\nconst PLAN_FOLLOW_UP_ATTACH_TIMEOUT_MS = 120_000\nconst TERMINAL_SESSION_EVENT_TYPES = new Set(['done', 'error', 'result'])\n\n/** The Sidecar's execution id for a follow-up turn. Keep it byte-identical to the Sidecar. */\nexport function planFollowUpExecutionId(sessionId: string, turnId: string): string {\n return `plan-followup-${createHash('sha256').update(`${sessionId}\\0${turnId}`).digest('hex')}`\n}\n\n/** Parse a follow-up descriptor. The turn id is derived, never trusted. */\nexport function parsePlanFollowUpAttach(value: unknown): PlanFollowUpAttach | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null\n const record = value as Record<string, unknown>\n const planId = typeof record.planId === 'string' ? record.planId.trim() : ''\n const turnId = typeof record.turnId === 'string' ? record.turnId.trim() : ''\n const revision = record.revision\n const outcome = record.outcome\n if (!planId || !turnId || (outcome !== 'approved' && outcome !== 'rejected')) return null\n if (typeof revision !== 'number' || !Number.isInteger(revision) || revision < 1) return null\n if (turnId !== planFollowUpTurnId(planId, revision, outcome)) return null\n return { planId, revision, outcome, turnId }\n}\n\n/**\n * Validate a follow-up request against what the session actually persisted:\n * the plan must be in this session at this revision and must already carry the\n * claimed decision.\n */\nexport function resolvePlanFollowUpRequest(input: {\n sessionId: string\n planFollowUp: unknown\n messages: ReadonlyArray<{ role: string; parts?: unknown }>\n}): PlanFollowUpResolution {\n const attach = parsePlanFollowUpAttach(input.planFollowUp)\n if (!attach) {\n return { ok: false, status: 400, code: 'invalid_plan_follow_up', error: 'Invalid planFollowUp descriptor' }\n }\n const plan = input.messages\n .filter((message) => message.role === 'assistant' && Array.isArray(message.parts))\n .flatMap((message) => message.parts as unknown[])\n .map((part) => (part && typeof part === 'object' && !Array.isArray(part)\n ? persistedPartToPlan(part as Record<string, unknown>)\n : null))\n .find((candidate) => candidate?.planId === attach.planId && candidate.revision === attach.revision)\n if (!plan) {\n return {\n ok: false,\n status: 409,\n code: 'plan_follow_up_missing',\n error: `Plan ${attach.planId} revision ${attach.revision} is not in this session`,\n }\n }\n if (plan.status !== attach.outcome) {\n return {\n ok: false,\n status: 409,\n code: 'plan_follow_up_not_decided',\n error: `Plan ${attach.planId} is ${plan.status}, not ${attach.outcome}`,\n }\n }\n return { ok: true, attach, executionId: planFollowUpExecutionId(input.sessionId, attach.turnId) }\n}\n\n/**\n * Session events carry two payload shapes: replayed events hold the stream\n * event under `data`, while live events nest it under `data.properties`.\n * Downstream reads `data.<field>`, so a live event is unwrapped. A replayed\n * payload that still carries its own `type` drops it, so `data` holds only the\n * event's fields.\n */\nexport function unwrapSessionEventPayload(event: SandboxEvent): SandboxEvent {\n const data = event.data && typeof event.data === 'object' && !Array.isArray(event.data)\n ? event.data as Record<string, unknown>\n : undefined\n if (!data) return event\n const properties = data.properties && typeof data.properties === 'object' && !Array.isArray(data.properties)\n ? data.properties as Record<string, unknown>\n : undefined\n if (properties) return { ...event, data: properties } as SandboxEvent\n if (typeof data.type === 'string') {\n const { type: _type, ...rest } = data\n return { ...event, data: rest } as SandboxEvent\n }\n return event\n}\n\n/**\n * Attach to the already-enqueued follow-up execution: replay its buffered\n * events from the start, then follow it live to completion. It never sends a\n * new prompt. The session-events iterator has no reconnect, so a stream that\n * ends without a terminal event fails instead of persisting a partial turn.\n */\nexport async function* streamPlanFollowUpEvents(\n box: SandboxInstance,\n sessionId: string,\n executionId: string,\n options: { timeoutMs?: number } = {},\n): AsyncGenerator<SandboxEvent> {\n const timeoutMs = options.timeoutMs ?? PLAN_FOLLOW_UP_ATTACH_TIMEOUT_MS\n const abort = new AbortController()\n const iterator = box.session(sessionId).events({\n executionId,\n since: '0',\n signal: abort.signal,\n })[Symbol.asyncIterator]()\n let timeout: ReturnType<typeof setTimeout> | undefined\n let sawTerminalEvent = false\n const track = (event: SandboxEvent): SandboxEvent => {\n if (TERMINAL_SESSION_EVENT_TYPES.has(String(event.type ?? ''))) sawTerminalEvent = true\n return unwrapSessionEventPayload(event)\n }\n try {\n const first = await Promise.race([\n iterator.next(),\n new Promise<never>((_, reject) => {\n timeout = setTimeout(\n () => reject(new Error(`Plan follow-up ${executionId} did not start within ${Math.round(timeoutMs / 1000)} seconds`)),\n timeoutMs,\n )\n }),\n ])\n if (timeout) clearTimeout(timeout)\n if (first.done) throw new Error(`Plan follow-up ${executionId} ended before emitting an event`)\n yield track(first.value)\n while (true) {\n const next = await iterator.next()\n if (next.done) {\n if (!sawTerminalEvent) {\n throw new Error(`Plan follow-up ${executionId} stream ended without a terminal event`)\n }\n return\n }\n yield track(next.value)\n }\n } finally {\n if (timeout) clearTimeout(timeout)\n abort.abort()\n await iterator.return?.(undefined)\n }\n}\n\ninterface PlanFollowUpStatusRow {\n status: string\n leaseToken: string | null\n}\n\n/**\n * Exclusive claim over a follow-up execution in the shared `turn_status`\n * table. Every claim writes a random lease and confirms ownership from the\n * row returned by the same D1 batch. A running claim older than\n * `staleAfterMs` may be reclaimed; the superseded holder then settles into\n * `lease_lost` instead of overwriting the newer claim. Apply\n * `TURN_STATUS_LEASE_MIGRATION_SQL` (from `/stream`) once after the turn\n * tables: it adds the `leaseToken` column the claim writes.\n */\nexport function createD1PlanFollowUpGate(\n db: D1LikeForPlanFollowUps,\n options: PlanFollowUpGateOptions = {},\n): PlanFollowUpGate {\n const now = options.now ?? (() => new Date())\n const createLease = options.createLease ?? randomUUID\n const staleAfterMs = options.staleAfterMs ?? PLAN_FOLLOW_UP_STALE_AFTER_MS\n const readStatus = (executionId: string) => db.prepare(`\n SELECT status, leaseToken\n FROM turn_status\n WHERE turnId = ?\n LIMIT 1\n `).bind(executionId)\n\n async function readAfterBatch(\n write: D1BoundForPlanFollowUps,\n executionId: string,\n operation: string,\n ): Promise<PlanFollowUpStatusRow> {\n const results = await db.batch([write, readStatus(executionId)])\n const rows = (results[1] as { results?: unknown[] } | undefined)?.results\n const row = rows?.[0] as Partial<PlanFollowUpStatusRow> | undefined\n if (!row || typeof row.status !== 'string') {\n throw new Error(`Plan follow-up ${executionId} has no status after ${operation}`)\n }\n if (row.leaseToken !== null && row.leaseToken !== undefined && typeof row.leaseToken !== 'string') {\n throw new Error(`Plan follow-up ${executionId} has an invalid lease after ${operation}`)\n }\n return { status: row.status, leaseToken: row.leaseToken ?? null }\n }\n\n async function settle(\n executionId: string,\n scopeId: string,\n lease: string,\n status: 'complete' | 'error',\n ): Promise<PlanFollowUpSettlement> {\n const row = await readAfterBatch(\n db.prepare(`\n UPDATE turn_status\n SET status = ?, scopeId = ?, updatedAt = ?\n WHERE turnId = ? AND status = 'running' AND leaseToken = ?\n `).bind(status, scopeId, now().toISOString(), executionId, lease),\n executionId,\n `settling ${status}`,\n )\n if (row.leaseToken !== lease) return { settled: false, reason: 'lease_lost' }\n if (row.status !== status) {\n throw new Error(`Plan follow-up ${executionId} remained ${row.status} while settling ${status}`)\n }\n return { settled: true }\n }\n\n return {\n async admit(executionId, scopeId) {\n const claimedAt = now()\n const lease = createLease()\n const staleBefore = new Date(claimedAt.getTime() - staleAfterMs).toISOString()\n const row = await readAfterBatch(\n db.prepare(`\n INSERT INTO turn_status (turnId, status, scopeId, updatedAt, leaseToken)\n VALUES (?, 'running', ?, ?, ?)\n ON CONFLICT(turnId) DO UPDATE SET\n status = 'running',\n scopeId = excluded.scopeId,\n updatedAt = excluded.updatedAt,\n leaseToken = excluded.leaseToken\n WHERE turn_status.status = 'error'\n OR (turn_status.status = 'running' AND turn_status.updatedAt < ?)\n `).bind(executionId, scopeId, claimedAt.toISOString(), lease, staleBefore),\n executionId,\n 'admission',\n )\n if (row.status === 'running' && row.leaseToken === lease) return { admitted: true, lease }\n if (row.status === 'running') return { admitted: false, reason: 'in_flight' }\n if (row.status === 'complete') return { admitted: false, reason: 'completed' }\n throw new Error(`Plan follow-up ${executionId} has unexpected admission status: ${row.status}`)\n },\n settle: (executionId, scopeId, lease) => settle(executionId, scopeId, lease, 'complete'),\n abandon: (executionId, scopeId, lease) => settle(executionId, scopeId, lease, 'error'),\n }\n}\n","import { getPartKey, mergePersistedPart, type StreamEvent } from '../stream/index'\nimport type { ChatTurnRouteProducer } from './turn-routes'\n\n/** Resolve chat route events and materialize their durable state records */\nexport interface ChatRouteDurableProjection {\n observe(event: unknown): void | Promise<void>\n materialize(): Array<Record<string, unknown>> | Promise<Array<Record<string, unknown>>>\n}\n\n/** Log chat route projection messages with optional metadata for durable processing */\nexport type ChatRouteDurableProjectionLogger =\n (message: string, meta?: Record<string, unknown>) => void\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n\n/** Adds durable lifecycle projection to any producer lane without moving its\n * transport into agent-app. Purely STRUCTURAL: agent-app ships no implementer\n * of {@link ChatRouteDurableProjection} and deliberately does not — the one it\n * used to ship (`/durable-chat`) was removed in 0.44.0 with zero fleet imports.\n * Pass any `{ observe, materialize }` object backed by your own store. The projection is observed inline and its\n * materialized parts replace same-key pending snapshots after the stream\n * drains. Projection persistence is best-effort for the live lane: a store\n * outage must not terminate an otherwise healthy sandbox stream. Failures are\n * reported through the optional logger so products can retain diagnostics. */\nexport function withDurableChatProjection(\n producer: ChatTurnRouteProducer,\n projection: ChatRouteDurableProjection,\n log: ChatRouteDurableProjectionLogger = (message, meta) => console.error(message, meta ?? ''),\n): ChatTurnRouteProducer {\n let projected: Array<Record<string, unknown>> = []\n async function* stream(): AsyncGenerator<StreamEvent, void, unknown> {\n for await (const event of producer.stream) {\n try {\n await projection.observe(event)\n } catch (error) {\n log('[chat-routes] durable projection observe failed', {\n eventType: event.type,\n error: errorMessage(error),\n })\n }\n yield event\n }\n try {\n projected = await projection.materialize()\n } catch (error) {\n log('[chat-routes] durable projection materialize failed', {\n error: errorMessage(error),\n })\n }\n }\n return {\n ...producer,\n stream: stream(),\n assistantParts: () => {\n const parts = producer.assistantParts?.() ?? []\n const order: string[] = []\n const byKey = new Map<string, Record<string, unknown>>()\n for (const part of [...parts, ...projected]) {\n const key = getPartKey(part)\n if (!byKey.has(key)) order.push(key)\n byKey.set(key, mergePersistedPart(byKey.get(key), part))\n }\n return order.map((key) => byKey.get(key)!)\n },\n }\n}\n","/**\n * `createUploadRoute` — the multimodal middle. Accepts multipart file uploads\n * and returns `PromptInputPart`-shaped descriptors the client echoes back on\n * send (`ChatTurnRequestPayload.parts`):\n *\n * ≤ inlineMaxBytes (700 KiB default) → inline `data:` URI part — rides the\n * turn body directly, no sandbox round trip.\n * > inlineMaxBytes → written into the sandbox workspace (base64 through the\n * structural `write` seam — `box.fs` satisfies it) and referenced by\n * `path`. Mandatory two-step: the gateway caps request bodies at ~1 MiB,\n * so a large file can never ride the prompt POST.\n *\n * The sink is structural (no sandbox-SDK import); products pass `box.fs`.\n *\n * @remarks Sole consumer today is the `--chat` scaffold (`create-agent-app\n * --chat` → `template-chat/src/chat.ts`), the reference multimodal path — its\n * inline-`data:`-or-ephemeral-sandbox-workspace split stays the scaffold's\n * proven upload pattern, not a fleet primitive; keep that distinction in mind\n * before widening its surface. Fleet apps with a durable store of their own\n * (KV, or AES-GCM-encrypted R2) no longer need to hand-roll a vault upload\n * route: `createAttachmentUploadRoute` (`./attachment-upload`, agent-app#234)\n * is the shared hardened path for that persistence model — a content-sniffed\n * type gate, two-phase atomic batch writes, and per-kind/aggregate size caps,\n * all seamed through an injected `WriteAttachmentFn`. Point readers there\n * instead of widening this route to cover both models.\n */\n\nimport type { ChatTurnFilePartInput } from './wire'\n\n/** 700 KiB: base64 inflates ~4/3, so an inline part stays comfortably under\n * the ~1 MiB gateway body cap alongside the JSON envelope. */\nexport const UPLOAD_INLINE_MAX_BYTES = 700 * 1024\n\n/** 8 MiB default ceiling per file — one base64 `write` call handles it. Raise\n * it only with a sink that can take the bigger single write. */\nexport const UPLOAD_MAX_FILE_BYTES = 8 * 1024 * 1024\n\nconst DEFAULT_UPLOAD_DIRECTORY = '/workspace/uploads'\n\nfunction normalizeUploadDirectory(value: string): string | null {\n const withoutTrailingSlash = value.trim().replace(/\\/+$/, '')\n if (!withoutTrailingSlash.startsWith('/') || withoutTrailingSlash.length === 0) return null\n const segments = withoutTrailingSlash.slice(1).split('/')\n if (segments.some((segment) => segment.length === 0 || segment === '.' || segment === '..')) return null\n return `/${segments.join('/')}`\n}\n\n/** Structural match of the sandbox SDK's `box.fs` write surface (v0.10.5+:\n * `encoding: 'base64'` is the worker-safe binary path). */\nexport interface SandboxUploadSink {\n write(path: string, content: string, options?: { encoding?: 'utf8' | 'base64' }): Promise<unknown>\n}\n\n/** Resolve upload authorization status and provide upload destination or error response */\nexport type UploadAuthorization =\n | {\n ok: true\n /** Where large files land. Absent/null: only inline uploads are\n * accepted and an over-inline-cap file is rejected with 413. */\n sink?: SandboxUploadSink | null\n /** Per-request override of the workspace directory large files go to. */\n uploadDir?: string\n }\n | { ok: false; response: Response }\n\n/** Define options to authorize uploads and configure file size limits and upload directory */\nexport interface CreateUploadRouteOptions {\n /** Authenticate the caller and resolve the sandbox file sink (usually\n * `ensureWorkspaceSandbox(...)` → `box.fs`). */\n authorize(args: { request: Request }): Promise<UploadAuthorization>\n /** Inline-vs-sandbox threshold. Default {@link UPLOAD_INLINE_MAX_BYTES}. */\n inlineMaxBytes?: number\n /** Hard per-file cap. Default {@link UPLOAD_MAX_FILE_BYTES}. */\n maxFileBytes?: number\n /** Absolute workspace directory for path-ref files.\n * Default `'/workspace/uploads'`. */\n uploadDir?: string\n}\n\n/** One uploaded file, ready for the composer chip and the turn body. */\nexport interface UploadedChatFile {\n id: string\n name: string\n size: number\n mediaType: string\n /** True when the part carries the bytes inline (`data:` URI). */\n inline: boolean\n /** Echo this back verbatim in `ChatTurnRequestPayload.parts`. */\n part: ChatTurnFilePartInput\n}\n\n/** Path-safe file name: basename only, conservative charset, length-capped. */\nexport function sanitizeUploadFilename(name: string): string {\n const base = name.split(/[\\\\/]/).pop() ?? 'file'\n const safe = base.replace(/[^A-Za-z0-9._-]+/g, '_').replace(/^\\.+/, '_')\n return (safe || 'file').slice(0, 120)\n}\n\nconst BASE64_CHUNK = 0x8000\n\n/** Convert a Uint8Array of bytes into a base64-encoded string */\nexport function bytesToBase64(bytes: Uint8Array): string {\n let binary = ''\n for (let offset = 0; offset < bytes.length; offset += BASE64_CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + BASE64_CHUNK))\n }\n return btoa(binary)\n}\n\nfunction uploadError(status: number, code: string, error: string): Response {\n return Response.json({ code, error }, { status })\n}\n\n/** Create an upload route handler that authorizes requests and processes file uploads with size limits */\nexport function createUploadRoute(options: CreateUploadRouteOptions): (request: Request) => Promise<Response> {\n const inlineMaxBytes = options.inlineMaxBytes ?? UPLOAD_INLINE_MAX_BYTES\n const maxFileBytes = options.maxFileBytes ?? UPLOAD_MAX_FILE_BYTES\n\n return async function upload(request: Request): Promise<Response> {\n const auth = await options.authorize({ request })\n if (!auth.ok) return auth.response\n const sink = auth.sink ?? null\n const configuredUploadDir = auth.uploadDir ?? options.uploadDir ?? DEFAULT_UPLOAD_DIRECTORY\n\n let form: FormData\n try {\n form = await request.formData()\n } catch {\n return uploadError(400, 'INVALID_UPLOAD', 'Expected a multipart/form-data body with file fields')\n }\n const files: File[] = []\n form.forEach((value) => {\n if (value instanceof File) files.push(value)\n })\n if (files.length === 0) {\n return uploadError(400, 'INVALID_UPLOAD', 'No files in the upload body')\n }\n\n const uploaded: UploadedChatFile[] = []\n for (const file of files) {\n const name = sanitizeUploadFilename(file.name)\n const mediaType = file.type || 'application/octet-stream'\n const partType: ChatTurnFilePartInput['type'] = mediaType.startsWith('image/') ? 'image' : 'file'\n\n if (file.size > maxFileBytes) {\n return uploadError(\n 413,\n 'FILE_TOO_LARGE',\n `${name} is ${file.size}B, over the ${maxFileBytes}B per-file cap`,\n )\n }\n\n const id = crypto.randomUUID()\n if (file.size <= inlineMaxBytes) {\n const base64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()))\n uploaded.push({\n id,\n name,\n size: file.size,\n mediaType,\n inline: true,\n part: {\n type: partType,\n filename: name,\n mediaType,\n url: `data:${mediaType};base64,${base64}`,\n },\n })\n continue\n }\n\n if (!sink) {\n return uploadError(\n 413,\n 'SANDBOX_REQUIRED',\n `${name} is ${file.size}B, over the ${inlineMaxBytes}B inline cap, and no sandbox is available to hold it`,\n )\n }\n const uploadDir = normalizeUploadDirectory(configuredUploadDir)\n if (!uploadDir) {\n return uploadError(\n 500,\n 'INVALID_UPLOAD_DIRECTORY',\n 'uploadDir must be an absolute path without empty, \".\" or \"..\" segments',\n )\n }\n const path = `${uploadDir}/${id}-${name}`\n const base64 = bytesToBase64(new Uint8Array(await file.arrayBuffer()))\n await sink.write(path, base64, { encoding: 'base64' })\n uploaded.push({\n id,\n name,\n size: file.size,\n mediaType,\n inline: false,\n part: { type: partType, filename: name, mediaType, path },\n })\n }\n\n return Response.json({ files: uploaded })\n }\n}\n","/**\n * Storage seams for the chat-attachment vertical (`resolveChatAttachments`,\n * `buildDispatchParts`, `promoteAgentFilePart`). Structural function ports in\n * the same style as `upload.ts`'s `SandboxUploadSink`: REQUIRED injection, no\n * default implementation — agent-app owns the size/budget/idempotency\n * mechanism, the product owns where the bytes actually live.\n *\n * One reader, two callers: `resolveChatAttachments` reads only the\n * authoritative `size` (never trusting the client-reported size), while\n * `buildDispatchParts` reads the inline `content` (base64 or raw bytes) to\n * build a `data:` URI. Both fit behind ONE `ReadAttachmentFn` so a product\n * wires a single vault/object-store adapter, not two. gtm's KV vault (stores\n * base64 bodies) and `/object-store`'s `ObjectStore` (hands back raw bytes)\n * both satisfy this shape.\n */\n\n/**\n * The result of reading one stored attachment. `ok:true` MUST carry the\n * authoritative decoded byte `size` (the cap is meaningless against a\n * client-controlled number) and, when the caller needs to inline the file,\n * its content as `base64` and/or raw `bytes`. `mediaType` is the stored\n * content type when the store knows it (used as the fallback when the wire\n * part carried none). `ok:false` carries a human `reason` that SHOULD name the\n * offending path — it is surfaced verbatim in the caller's typed outcome.\n */\nexport type AttachmentReadResult =\n | {\n ok: true\n /** Authoritative decoded byte length of the stored content. */\n size: number\n /** Inline content as base64 — reused verbatim for a `data:` URI, so a\n * store holding already-base64 bodies never decodes-and-re-encodes. */\n base64?: string\n /** Inline content as raw bytes — base64-encoded by the caller when a\n * `data:` URI is needed. Ignored when `base64` is present. */\n bytes?: Uint8Array\n /** Stored content type, when the store tracks one. */\n mediaType?: string\n }\n | { ok: false; reason: string }\n\n/**\n * Read one stored attachment for `scopeId` (the product's workspace/tenant\n * key) at its store-relative `path`. Missing, deleted, or unreadable content\n * MUST resolve to `{ ok: false, reason }`, never throw — a store failure is a\n * per-attachment outcome the caller folds into its own typed result, not a\n * turn-level exception.\n */\nexport type ReadAttachmentFn = (scopeId: string, path: string) => Promise<AttachmentReadResult>\n\n/** The ownership identity for one attempted write. The upload and promotion\n * routes create a unique path for each attempt, and the product adapter stores\n * this id with it. */\nexport interface AttachmentWriteOwnership {\n readonly id: string\n readonly path: string\n}\n\n/** Public-safe outcome for a storage outage. Backend details belong in logs. */\nexport const ATTACHMENT_STORAGE_FAILURE_MESSAGE = 'Attachment storage is temporarily unavailable. Please try again.'\n\n/** Stable client error code when compensating cleanup did not complete. */\nexport const ATTACHMENT_ROLLBACK_FAILURE_CODE = 'rollback_failed'\n\n/** Public-safe outcome when compensating cleanup did not complete. */\nexport const ATTACHMENT_ROLLBACK_FAILURE_MESSAGE = 'Attachment cleanup failed. Please try again.'\n\n/** Add a path-safe ownership id to a logical path and return an immutable store key. */\nexport function immutableAttachmentPath(logicalPath: string, ownershipId: string): string {\n if (!/^[A-Za-z0-9_-]{1,128}$/.test(ownershipId)) {\n throw new Error('attachment ownership id must be a path-safe identifier')\n }\n return `${logicalPath}--${ownershipId}`\n}\n\n/** Compensation for one attachment write. The adapter MUST compare the stored\n * ownership id before deleting, so an older rollback cannot delete a newer\n * overwrite. */\nexport interface AttachmentWriteReceipt {\n rollback(): void | Promise<void>\n ownership: AttachmentWriteOwnership\n}\n\n/** Clean up a write whose function threw after the store may have committed.\n * The adapter must delete only the supplied ownership, never the logical path\n * unconditionally. */\nexport type AbortAttachmentWriteFn = (\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n) => void | Promise<void>\n\n/**\n * The stable writer result from the original attachment-store contract.\n * Existing products may return this shape and keep using the legacy writer\n * lane. New products should use {@link AtomicAttachmentWriteResult}.\n */\nexport type AttachmentWriteResult = { ok: true } | { ok: false; reason: string }\n\n/** Options from the stable attachment-store contract. */\nexport interface AttachmentWriteOptions {\n mediaType?: string\n name?: string\n originalName?: string\n size?: number\n}\n\n/**\n * The stable writer port. It is intentionally unchanged so products released\n * before the ownership receipt contract continue to compile.\n *\n * The legacy lane does not promise batch rollback. Use\n * {@link createAtomicAttachmentWriter} for ownership-safe writes.\n */\nexport type WriteAttachmentFn = (\n scopeId: string,\n path: string,\n content: Uint8Array | string,\n opts: AttachmentWriteOptions,\n) => Promise<AttachmentWriteResult>\n\n/** Options for a streamed write. `size` is the exact byte count `content`\n * yields. `mediaType` is decided from the file name and its first bytes,\n * before the route has read the rest: an archive's central directory sits at\n * its end, so the route's response, not this value, carries the final type. */\nexport interface AttachmentStreamWriteOptions extends AttachmentWriteOptions {\n size: number\n mediaType: string\n}\n\n/**\n * Streamed writer port for an upload too large to hold in memory. `content`\n * yields exactly `opts.size` bytes. The route checks the file while it\n * streams and withholds the final bytes until the whole file has passed, so\n * on a refusal `content` errors instead of ending. The adapter must make the\n * bytes readable at `path` only when `content` ends cleanly, and must leave\n * whatever was stored at `path` before untouched when it errors. A store put\n * of a fixed-length stream (R2 `put` behind `FixedLengthStream`) does both.\n */\nexport type WriteAttachmentStreamFn = (\n scopeId: string,\n path: string,\n content: ReadableStream<Uint8Array>,\n opts: AttachmentStreamWriteOptions,\n) => Promise<AttachmentWriteResult>\n\n/** Options for an ownership-safe writer. */\nexport interface AtomicAttachmentWriteOptions extends AttachmentWriteOptions {\n ownership: AttachmentWriteOwnership\n}\n\n/** Result for an ownership-safe writer. A receipt is required on both paths. */\nexport type AtomicAttachmentWriteResult =\n | { ok: true; receipt: AttachmentWriteReceipt }\n | { ok: false; reason: string; receipt: AttachmentWriteReceipt }\n\n/** Ownership-safe writer port used by the atomic upload and promotion lanes. */\nexport type AtomicWriteAttachmentFn = (\n scopeId: string,\n path: string,\n content: Uint8Array | string,\n opts: AtomicAttachmentWriteOptions,\n) => Promise<AtomicAttachmentWriteResult>\n\n/** A complete ownership-safe attachment store adapter. */\nexport interface AtomicAttachmentWriter {\n write: AtomicWriteAttachmentFn\n abort: AbortAttachmentWriteFn\n}\n\n/**\n * Build the explicit atomic adapter used by new routes.\n *\n * Keeping the write and abort functions together prevents a caller from\n * enabling ownership paths while forgetting the ambiguous-write cleanup.\n */\nexport function createAtomicAttachmentWriter(input: AtomicAttachmentWriter): AtomicAttachmentWriter {\n return {\n write: input.write.bind(input),\n abort: input.abort.bind(input),\n }\n}\n","/**\n * `resolveChatAttachments` — validate a turn body's `attachments` field into\n * persistable {@link ChatAttachmentPart}s. Every path is re-validated (a path\n * off the wire is never trusted to stay inside the store root) and every size\n * is re-derived from the STORED body via the injected {@link ReadAttachmentFn},\n * never the client-reported `size` — the upload path lets a caller rewrite its\n * own frontmatter, so a stored size cannot bound anything and the wire size can\n * be anything. Both the aggregate cap and the size carried on the returned part\n * come from the authoritative read.\n *\n * Storage-parameterized: the frontmatter parsing / base64 sizing that derives\n * the authoritative size lives BEHIND `readAttachment` (a product's vault or\n * object-store adapter), so this module is a pure validator + budget gate with\n * no store knowledge. Lifted from gtm-agent's `resolve-attachments.ts`\n * (workspaceId → scopeId, the vault read → the injected reader) and kept\n * behavior-identical for gtm-agent#618 adoption.\n */\n\nimport { attachmentInputToPart, type ChatAttachmentPart } from '../chat-store/parts'\nimport type { ReadAttachmentFn } from './attachment-store'\nimport {\n MAX_ATTACHMENT_TOTAL_BYTES,\n attachmentTotalSizeErrorMessage,\n parseChatAttachmentInputs,\n type AttachmentPathCheck,\n} from './attachment-validation'\n\n/** Resolve the result of chat attachment processing with success status and corresponding data or error */\nexport type ResolveChatAttachmentsResult =\n | { succeeded: true; value: ChatAttachmentPart[] }\n | { succeeded: false; error: string }\n\n/** Define options to resolve and validate chat attachments with size, count, and path constraints */\nexport interface ResolveChatAttachmentsOptions {\n /** The product's workspace/tenant key, passed to `readAttachment`. */\n scopeId: string\n /** Authoritative size + content reader — see {@link ReadAttachmentFn}. */\n readAttachment: ReadAttachmentFn\n /** Most attachments one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */\n maxCount?: number\n /** Aggregate raw-byte ceiling. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */\n maxTotalBytes?: number\n /** Path validator override. Default {@link defaultValidateAttachmentPath}. */\n validatePath?: (path: string) => AttachmentPathCheck\n}\n\n/**\n * Validate and resolve a turn body's `attachments` field into persistable\n * parts. Every path is confirmed present (and not deleted) in the caller's own\n * store by `readAttachment` before it is trusted, and size is derived from the\n * authoritative read for both the aggregate cap and the returned part's size.\n */\nexport async function resolveChatAttachments(\n value: unknown,\n options: ResolveChatAttachmentsOptions,\n): Promise<ResolveChatAttachmentsResult> {\n const maxTotalBytes = options.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const parsed = parseChatAttachmentInputs(value, options)\n if (!parsed.succeeded) return parsed\n const inputs = parsed.value\n\n // Advisory only: client-controlled, so a dishonest caller can slip past it —\n // but an honest oversized request should fail before it costs a single store\n // read. The authoritative check runs per-attachment below, against the\n // body-derived size.\n const advisoryTotal = inputs.reduce((sum, input) => sum + input.size, 0)\n if (advisoryTotal > maxTotalBytes) {\n return { succeeded: false, error: attachmentTotalSizeErrorMessage(advisoryTotal, maxTotalBytes) }\n }\n\n // One attachment is read, sized, and dropped at a time rather than via\n // Promise.all — that would materialize every attachment's body in memory\n // simultaneously (ten truthful 10 MiB references is ~134 MiB of base64, over\n // a Workers heap) before the cap ever gets a chance to reject the request.\n // Sequential reads bound resident bytes to one attachment at a time and bail\n // the moment the running total exceeds the cap, at the cost of up to\n // `maxCount` serial reads instead of one parallel batch.\n let totalStoredBytes = 0\n for (const input of inputs) {\n const read = await options.readAttachment(options.scopeId, input.path)\n if (!read.ok) return { succeeded: false, error: read.reason }\n totalStoredBytes += read.size\n if (totalStoredBytes > maxTotalBytes) {\n return { succeeded: false, error: attachmentTotalSizeErrorMessage(totalStoredBytes, maxTotalBytes) }\n }\n input.size = read.size\n }\n\n return { succeeded: true, value: inputs.map(attachmentInputToPart) }\n}\n","import type {\n AtomicAttachmentWriteResult,\n AttachmentWriteOwnership,\n AttachmentWriteReceipt,\n AttachmentWriteResult,\n} from './attachment-store'\n\ntype ObjectLike = Record<PropertyKey, unknown>\n\nfunction objectLike(value: unknown): value is ObjectLike {\n return value !== null && (typeof value === 'object' || typeof value === 'function')\n}\n\n/** Read an injected writer result without allowing hostile getters to escape. */\nexport function inspectLegacyAttachmentWriteResult(\n value: unknown,\n): AttachmentWriteResult | undefined {\n try {\n if (!objectLike(value)) return undefined\n const ok = value.ok\n if (ok === true) return { ok: true }\n if (ok === false && typeof value.reason === 'string') {\n return { ok: false, reason: value.reason }\n }\n } catch {\n // A malformed or hostile adapter result is a storage failure.\n }\n return undefined\n}\n\n/**\n * Read an ownership-aware writer result with strict runtime checks.\n *\n * The public types protect TypeScript callers only. The product adapter is an\n * injection boundary, so a proxy, malformed value, or truthy non-boolean must\n * fail closed before the route invokes cleanup or publishes a path.\n */\nexport function inspectAtomicAttachmentWriteResult(\n value: unknown,\n ownership: AttachmentWriteOwnership,\n): AtomicAttachmentWriteResult | undefined {\n try {\n if (!objectLike(value)) return undefined\n const receiptValue = value.receipt\n if (!objectLike(receiptValue)) return undefined\n const receiptOwnership = receiptValue.ownership\n if (!objectLike(receiptOwnership)) return undefined\n if (\n receiptOwnership.id !== ownership.id ||\n receiptOwnership.path !== ownership.path ||\n typeof receiptOwnership.id !== 'string' ||\n typeof receiptOwnership.path !== 'string' ||\n typeof receiptValue.rollback !== 'function'\n ) {\n return undefined\n }\n if (value.ok === true) {\n return { ok: true, receipt: receiptValue as unknown as AttachmentWriteReceipt }\n }\n if (value.ok === false && typeof value.reason === 'string') {\n return {\n ok: false,\n reason: value.reason,\n receipt: receiptValue as unknown as AttachmentWriteReceipt,\n }\n }\n } catch {\n // A malformed or hostile adapter result is a storage failure.\n }\n return undefined\n}\n","/**\n * `promoteAgentFilePart` — turn a harness-emitted `type:\"file\"` stream part\n * into a store-backed {@link ChatAttachmentPart}. The harness hands back a URL\n * pointing at bytes it produced (a `data:` URI, or a path inside the sandbox);\n * nothing durable survives past the turn unless it is written into the\n * product's store, the same way a user upload is. Typed outcomes throughout:\n * every failure mode (unsupported scheme, no sandbox, oversize, store-write\n * failure, malformed part) resolves to `{ succeeded: false, filename, reason }`\n * rather than throwing past this boundary, so the caller folds a visible notice\n * instead of losing the file silently.\n *\n * Storage-parameterized port of gtm-agent's `promote-file-parts.ts` with the\n * refactor gtm never made: persistence goes through an injected stable writer\n * or ownership-safe adapter, the path strategy is the injected\n * `buildAttachmentPath` (neutral `uploads/agent/<date>/` default, no domain\n * bucket taxonomy baked), the MIME map is an injectable hook, and the date\n * segment reads an injectable clock. The logical `hash8(id ?? url ?? filename)`\n * naming is preserved. Atomic callers also receive a fresh ownership suffix so\n * an ambiguous write cannot overwrite an older object.\n */\n\nimport {\n statSandboxFileSize,\n readSandboxBinaryBytes,\n type SandboxExecChannel,\n} from '../sandbox/binary-read'\nimport { attachmentKindForMime, type ChatAttachmentKind, type ChatAttachmentPart } from '../chat-store/parts'\nimport { redactErrorMessage } from '../redact'\nimport {\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n immutableAttachmentPath,\n type AtomicAttachmentWriter,\n type AttachmentWriteOwnership,\n type AttachmentWriteReceipt,\n type WriteAttachmentFn,\n} from './attachment-store'\nimport {\n inspectAtomicAttachmentWriteResult,\n inspectLegacyAttachmentWriteResult,\n} from './attachment-write-safety'\nimport { sanitizeAttachmentFileName } from './attachment-validation'\nimport { formatBytes } from './wire'\n\n/** Default ceiling on a promoted file's raw (pre-encoding) byte size. */\nexport const PROMOTE_MAX_FILE_BYTES = 10 * 1024 * 1024\n\n/** Define the structure for a raw file part with optional metadata and media type information */\nexport interface RawAgentFilePart {\n type: 'file'\n id?: string\n filename?: string\n /** AI-SDK-shaped parts carry the MIME type here… */\n mediaType?: string\n /** …but OpenCode's native FilePart calls the same field `mime`. */\n mime?: string\n url?: string\n}\n\n/** Resolve the result of promoting a file part with success status and relevant data or error details */\nexport type PromoteFilePartResult =\n | { succeeded: true; part: ChatAttachmentPart }\n | { succeeded: false; filename: string; reason: string }\n\ntype ByteResolution =\n | { succeeded: true; bytes: Uint8Array }\n | { succeeded: false; reason: string }\n\n/** Arguments handed to a {@link PromoteAgentFilePartOptions.buildAttachmentPath}\n * override — everything needed to place the file deterministically. */\nexport interface AttachmentPathArgs {\n /** Sanitized display filename (basename, safe charset). */\n filename: string\n /** First 8 hex chars of the SHA-256 idempotency digest. */\n hash8: string\n /** `YYYY-MM-DD` from the injected clock. */\n date: string\n /** Resolved media type. */\n mediaType: string\n /** `image`/`file` split of the media type. */\n kind: ChatAttachmentKind\n}\n\n/** Minimal extension→mime map — the last-resort media type when the part\n * carries none. Generic file typing, NOT a product accept-list (which is a\n * domain value the product supplies): an unknown extension falls to\n * `text/plain`, it never rejects. */\nconst EXT_TO_MIME: Record<string, string> = {\n md: 'text/markdown',\n markdown: 'text/markdown',\n txt: 'text/plain',\n log: 'text/plain',\n csv: 'text/csv',\n tsv: 'text/tab-separated-values',\n json: 'application/json',\n yaml: 'text/yaml',\n yml: 'text/yaml',\n xml: 'application/xml',\n html: 'text/html',\n htm: 'text/html',\n pdf: 'application/pdf',\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n heic: 'image/heic',\n heif: 'image/heif',\n svg: 'image/svg+xml',\n mp4: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n ogg: 'audio/ogg',\n m4a: 'audio/mp4',\n aac: 'audio/aac',\n}\n\n/** Default MIME hook: extension → mime, or `text/plain` for the unknown. */\nexport function sniffMimeFromName(filename: string): string {\n const ext = filename.split('.').pop()?.toLowerCase()\n if (!ext) return 'text/plain'\n return EXT_TO_MIME[ext] ?? 'text/plain'\n}\n\n/** Decode base64 with `atob` (not `Buffer.from`, which SKIPS out-of-alphabet\n * characters and would decode a corrupt payload to something plausible). */\nfunction base64ToBytes(base64: string): Uint8Array {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)\n return bytes\n}\n\nfunction parseDataUrl(url: string): { base64: boolean; data: string } | null {\n const match = /^data:[^,]*,([\\s\\S]*)$/i.exec(url)\n if (!match) return null\n return { base64: /;base64,/i.test(url), data: match[1] ?? '' }\n}\n\n/** The MIME type embedded in a `data:` URI's header, if any — the last-resort\n * signal when the part itself carries no mediaType/mime field. */\nfunction dataUrlMime(url: string | undefined): string | undefined {\n if (!url) return undefined\n const match = /^data:([^;,]+)[;,]/i.exec(url)\n return match ? match[1] : undefined\n}\n\nfunction basenameFromUrl(url: string | undefined): string | undefined {\n if (!url || /^data:/i.test(url)) return undefined\n const withoutQuery = url.split(/[?#]/)[0] ?? url\n const segments = withoutQuery.split('/').filter(Boolean)\n return segments[segments.length - 1] || undefined\n}\n\n/** `file://<path>` strips to `<path>`; a bare absolute path passes through\n * unchanged. The remainder is percent-decoded — sidecar file URLs encode\n * spaces and other reserved characters. */\nfunction resolveFileUrlPath(url: string): { succeeded: true; path: string } | { succeeded: false; reason: string } {\n const withoutScheme = /^file:\\/\\//i.test(url) ? url.slice('file://'.length) : url\n try {\n return { succeeded: true, path: decodeURIComponent(withoutScheme) }\n } catch (err) {\n return { succeeded: false, reason: `malformed file path: ${redactErrorMessage(err)}` }\n }\n}\n\n/** Matches gtm's `attachmentSizeErrorMessage` (attachment-limits.ts:87-89)\n * verbatim, via the shared {@link formatBytes} — e.g. \"report.pdf is 10MB;\n * attachments are limited to 10MB\" — so an oversize promotion notice reads\n * identically whether gtm's original composed it or agent-app's promoter did. */\nfunction oversizeReason(filename: string, actual: number, limit: number): string {\n return `${filename} is ${formatBytes(actual)}; attachments are limited to ${formatBytes(limit)}`\n}\n\nfunction resolveDataUrlBytes(url: string, filename: string, maxBytes: number): ByteResolution {\n const parsed = parseDataUrl(url)\n if (!parsed) return { succeeded: false, reason: 'malformed data URI' }\n let bytes: Uint8Array\n try {\n bytes = parsed.base64 ? base64ToBytes(parsed.data) : new TextEncoder().encode(decodeURIComponent(parsed.data))\n } catch (err) {\n return { succeeded: false, reason: `failed to decode data URI: ${redactErrorMessage(err)}` }\n }\n if (bytes.byteLength > maxBytes) {\n return { succeeded: false, reason: oversizeReason(filename, bytes.byteLength, maxBytes) }\n }\n return { succeeded: true, bytes }\n}\n\nasync function resolveSandboxFileBytes(input: {\n path: string\n box: SandboxExecChannel\n sessionId: string\n filename: string\n maxBytes: number\n}): Promise<ByteResolution> {\n // exec can reject outright (box teardown, timeout, transport failure) — that\n // is a per-file failure, not a turn failure, so it must resolve to a typed\n // outcome like a nonzero exit code does.\n const stat = await statSandboxFileSize(input.box, input.path, { sessionId: input.sessionId })\n if (!stat.succeeded) {\n return { succeeded: false, reason: `could not stat agent file: ${redactErrorMessage(stat.error)}` }\n }\n // Rejected before the bytes are ever pulled — a base64 exec of an oversize\n // file would waste a full sandbox round trip only to be discarded.\n if (stat.value > input.maxBytes) {\n return { succeeded: false, reason: oversizeReason(input.filename, stat.value, input.maxBytes) }\n }\n\n const read = await readSandboxBinaryBytes(input.box, input.path, stat.value, { sessionId: input.sessionId })\n if (!read.succeeded) {\n return { succeeded: false, reason: `could not read agent file: ${redactErrorMessage(read.error)}` }\n }\n return { succeeded: true, bytes: read.value.bytes }\n}\n\nasync function resolveBytes(input: {\n raw: RawAgentFilePart\n box: SandboxExecChannel | undefined\n sessionId: string\n filename: string\n maxBytes: number\n}): Promise<ByteResolution> {\n const url = input.raw.url\n if (!url) return { succeeded: false, reason: 'the file part carries no url' }\n\n if (/^data:/i.test(url)) return resolveDataUrlBytes(url, input.filename, input.maxBytes)\n\n const isSandboxPath = /^file:\\/\\//i.test(url) || url.startsWith('/')\n if (!isSandboxPath) {\n const scheme = /^([A-Za-z][A-Za-z0-9+.-]*):/.exec(url)?.[1]?.toLowerCase() ?? 'unknown'\n return { succeeded: false, reason: `unsupported file URL scheme: ${scheme}` }\n }\n if (!input.box) return { succeeded: false, reason: 'no sandbox to read agent file' }\n\n const resolvedPath = resolveFileUrlPath(url)\n if (!resolvedPath.succeeded) return resolvedPath\n return resolveSandboxFileBytes({\n path: resolvedPath.path,\n box: input.box,\n sessionId: input.sessionId,\n filename: input.filename,\n maxBytes: input.maxBytes,\n })\n}\n\n/** First 8 hex chars of the SHA-256 of `seed` — deterministic (no\n * `Math.random`) so promoting the same source part twice, even across\n * requests, resolves to the same store path and overwrites in place. */\nasync function hash8(seed: string): Promise<string> {\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(seed))\n return Array.from(new Uint8Array(digest).slice(0, 4))\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Neutral default placement: everything under `uploads/agent/<date>/`, named\n * `<base>-<hash8><ext>`. No domain bucket taxonomy (assets/audio/videos…) —\n * a product that wants one supplies `buildAttachmentPath`. */\nfunction defaultBuildAttachmentPath(args: AttachmentPathArgs): string {\n const extensionMatch = /\\.[A-Za-z0-9]+$/.exec(args.filename)\n const extension = extensionMatch ? extensionMatch[0] : ''\n const base = extension ? args.filename.slice(0, -extension.length) : args.filename\n return `uploads/agent/${args.date}/${base}-${args.hash8}${extension}`\n}\n\ntype PromoteFilePartLogger = Pick<Console, 'error'>\n\nfunction logPromotionStorageError(\n logger: PromoteFilePartLogger,\n message: string,\n fields: Record<string, unknown>,\n): void {\n try {\n const safeFields = Object.fromEntries(\n Object.entries(fields).map(([key, value]) => [\n key,\n typeof value === 'string' ? redactErrorMessage(value) : value,\n ]),\n )\n logger.error(message, safeFields)\n } catch {\n // Logging must not turn a typed storage failure into a thrown response.\n }\n}\n\nasync function compensatePromotionWrite(\n writer: AtomicAttachmentWriter,\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n receipt: AttachmentWriteReceipt,\n): Promise<{ rollbackError?: unknown; abortError?: unknown }> {\n try {\n await receipt.rollback()\n return {}\n } catch (rollbackError) {\n try {\n await writer.abort(scopeId, ownership)\n return { rollbackError }\n } catch (abortError) {\n return { rollbackError, abortError }\n }\n }\n}\n\ninterface PromoteFilePartCommonOptions {\n raw: RawAgentFilePart\n /** The turn's box — required only to promote a sandbox-path part; a `data:`\n * URI needs none. */\n box?: SandboxExecChannel\n /** The product's workspace/tenant key, passed to `writeAttachment`. */\n scopeId: string\n /** The turn's session id, used for the sandbox stat/read exec calls. */\n sessionId: string\n /** Store-path strategy. Default {@link defaultBuildAttachmentPath}. */\n buildAttachmentPath?: (args: AttachmentPathArgs) => string\n /** Raw-byte ceiling. Default {@link PROMOTE_MAX_FILE_BYTES}. */\n maxBytes?: number\n /** Last-resort media-type hook. Default {@link sniffMimeFromName}. */\n sniffMime?: (filename: string) => string\n /** Clock for the date path segment. Default `() => new Date()`. */\n now?: () => Date\n /** Unique id source for tests or a product's id service. */\n createWriteId?: () => string\n /** Server-side sink for redacted storage details. */\n logger?: PromoteFilePartLogger\n}\n\n/** Stable promotion options from the original writer contract. */\nexport interface PromoteAgentFilePartOptions extends PromoteFilePartCommonOptions {\n /** REQUIRED store writer — no default (the product owns its store). */\n writeAttachment: WriteAttachmentFn\n}\n\n/** Ownership-safe promotion options for new products. */\nexport interface AtomicPromoteAgentFilePartOptions extends PromoteFilePartCommonOptions {\n /** Complete writer + ambiguous-write cleanup adapter. */\n attachmentWriter: AtomicAttachmentWriter\n}\n\nfunction isAtomicPromotionOptions(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): options is AtomicPromoteAgentFilePartOptions {\n return 'attachmentWriter' in options\n}\n\n/** Promote a part using the stable writer contract. */\nexport function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>\n\n/** Promote a part using an ownership-safe writer and cleanup adapter. */\nexport function promoteAgentFilePart(options: AtomicPromoteAgentFilePartOptions): Promise<PromoteFilePartResult>\n\n/** Promote a part when the caller selects the writer contract at runtime. */\nexport function promoteAgentFilePart(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): Promise<PromoteFilePartResult>\n\n/** Promote a part of an agent file with optional byte limits and MIME type detection. */\nexport async function promoteAgentFilePart(\n options: PromoteAgentFilePartOptions | AtomicPromoteAgentFilePartOptions,\n): Promise<PromoteFilePartResult> {\n const atomic = isAtomicPromotionOptions(options)\n const maxBytes = options.maxBytes ?? PROMOTE_MAX_FILE_BYTES\n const sniffMime = options.sniffMime ?? sniffMimeFromName\n const buildAttachmentPath = options.buildAttachmentPath ?? defaultBuildAttachmentPath\n const now = options.now ?? (() => new Date())\n const logger = options.logger ?? console\n const createWriteId = atomic\n ? options.createWriteId ?? (() => {\n if (typeof crypto === 'undefined' || typeof crypto.randomUUID !== 'function') {\n throw new Error('attachment promotion requires crypto.randomUUID')\n }\n return crypto.randomUUID()\n })\n : undefined\n\n const filename = sanitizeAttachmentFileName(\n options.raw.filename ?? basenameFromUrl(options.raw.url) ?? 'agent-file',\n )\n\n const resolved = await resolveBytes({\n raw: options.raw,\n box: options.box,\n sessionId: options.sessionId,\n filename,\n maxBytes,\n })\n if (!resolved.succeeded) return { succeeded: false, filename, reason: resolved.reason }\n\n let mediaType: string\n let kind: ChatAttachmentKind\n let logicalPath: string\n try {\n mediaType = (options.raw.mediaType ?? options.raw.mime ?? dataUrlMime(options.raw.url) ?? sniffMime(filename)).toLowerCase()\n kind = attachmentKindForMime(mediaType)\n const digest = await hash8(options.raw.id ?? options.raw.url ?? filename)\n const date = now().toISOString().split('T')[0] ?? ''\n logicalPath = buildAttachmentPath({ filename, hash8: digest, date, mediaType, kind })\n } catch (error) {\n logPromotionStorageError(logger, '[promote-file-part] path planning failed', {\n error: redactErrorMessage(error),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n\n if (!atomic) {\n let written: unknown\n try {\n written = await options.writeAttachment(options.scopeId, logicalPath, resolved.bytes, {\n mediaType,\n name: filename,\n originalName: options.raw.filename ?? filename,\n size: resolved.bytes.byteLength,\n })\n } catch (error) {\n const reason = redactErrorMessage(error)\n logPromotionStorageError(logger, '[promote-file-part] legacy writer failed', {\n path: logicalPath,\n error: reason,\n })\n return { succeeded: false, filename, reason }\n }\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result) {\n logPromotionStorageError(logger, '[promote-file-part] legacy writer returned an invalid result', {\n path: logicalPath,\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logPromotionStorageError(logger, '[promote-file-part] legacy write rejected', {\n path: logicalPath,\n error: reason,\n })\n return { succeeded: false, filename, reason }\n }\n\n return {\n succeeded: true,\n part: {\n type: kind,\n path: logicalPath,\n name: filename,\n size: resolved.bytes.byteLength,\n mediaType,\n },\n }\n }\n\n let ownershipId: string\n let path: string\n try {\n ownershipId = createWriteId!()\n path = immutableAttachmentPath(logicalPath, ownershipId)\n } catch (error) {\n logPromotionStorageError(logger, '[promote-file-part] could not allocate ownership key', {\n path: logicalPath,\n error: redactErrorMessage(error),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n const ownership: AttachmentWriteOwnership = Object.freeze({ id: ownershipId, path })\n\n // `name` is the sanitized filename already computed above; `originalName`\n // is the pre-sanitization source name (gtm's frontmatter `originalName`) —\n // the one field sanitization would otherwise destroy with no way back.\n let written: Awaited<ReturnType<AtomicAttachmentWriter['write']>>\n try {\n written = await options.attachmentWriter.write(options.scopeId, path, resolved.bytes, {\n mediaType,\n name: filename,\n originalName: options.raw.filename ?? filename,\n size: resolved.bytes.byteLength,\n ownership,\n })\n } catch (err) {\n let abortError: unknown\n try {\n await options.attachmentWriter.abort(options.scopeId, ownership)\n } catch (error) {\n abortError = error\n }\n logPromotionStorageError(logger, '[promote-file-part] write failed', {\n path,\n ownershipId: ownership.id,\n error: redactErrorMessage(err),\n ...(abortError === undefined ? {} : { abortError: redactErrorMessage(abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n const result = inspectAtomicAttachmentWriteResult(written, ownership)\n if (!result) {\n let abortError: unknown\n try {\n await options.attachmentWriter.abort(options.scopeId, ownership)\n } catch (error) {\n abortError = error\n }\n logPromotionStorageError(logger, '[promote-file-part] writer returned a mismatched ownership receipt', {\n path,\n ownershipId: ownership.id,\n ...(abortError === undefined ? {} : { abortError: redactErrorMessage(abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n if (!result.ok) {\n const compensation = await compensatePromotionWrite(\n options.attachmentWriter,\n options.scopeId,\n ownership,\n result.receipt,\n )\n logPromotionStorageError(logger, '[promote-file-part] write rejected', {\n path,\n ownershipId: ownership.id,\n error: redactErrorMessage(result.reason),\n ...(compensation.rollbackError === undefined\n ? {}\n : { rollbackError: redactErrorMessage(compensation.rollbackError) }),\n ...(compensation.abortError === undefined\n ? {}\n : { abortError: redactErrorMessage(compensation.abortError) }),\n })\n return { succeeded: false, filename, reason: ATTACHMENT_STORAGE_FAILURE_MESSAGE }\n }\n\n return {\n succeeded: true,\n part: {\n type: kind,\n path,\n name: filename,\n size: resolved.bytes.byteLength,\n mediaType,\n },\n }\n}\n","/**\n * `createAttachmentUploadRoute` — the fleet-primitive durable-store upload\n * route: a two-phase batch (every file is validated before any file is written),\n * with a stable legacy lane and an ownership-safe receipt lane for new callers,\n * a content-sniffed type gate\n * (`checkAttachmentType` over `sniffBinary`'s magic-byte read, not the\n * extension or the browser-reported MIME), binary/text caps with optional\n * per-sniffed-mime overrides, an aggregate byte cap, and sanitized filenames.\n * Storage is fully seamed through the injected\n * `WriteAttachmentFn` or `AtomicAttachmentWriter` (`./attachment-store`) — no\n * default store, the product owns where bytes actually live (vault, object\n * store, …), and auth/rate limiting is entirely the injected `authorize`\n * seam's job: this factory\n * never invents a 401 or 429 response, it only returns `auth.response`\n * verbatim on failure.\n *\n * Two body shapes: a `multipart/form-data` batch, parsed whole, and one raw\n * file body named by the `filename` query parameter. A raw body larger than\n * the sniff head streams to `writeAttachmentStream` when the product supplies\n * one: the Worker never holds the file whole, which is what lets a product\n * admit data files of tens of MB inside a 128 MB isolate.\n *\n * Lifted from gtm-agent's `src/routes/api.vault.upload.ts` (the hardening\n * lineage other lifted modules in this vertical cite: gtm#584 binary\n * corruption, gtm#592 sniff gate/caps, gtm#593 batch-atomic writes) and\n * generalized the way `resolve-attachments.ts` generalized gtm's read path —\n * the vault-specific pieces (KV vault paths, frontmatter, per-user rate\n * limiting) are all injected seams here, while the validate-then-write phase\n * split and the type/size gate ordering survive byte-for-byte.\n *\n * @remarks Sibling to, NOT an extension of, `./upload.ts`'s\n * `createUploadRoute` — a different persistence model (durable product store\n * vs. inline-`data:`-or-ephemeral-sandbox-workspace). See that module's doc\n * comment for the up-to-date framing between the two.\n */\n\nimport { formatBytes, type ChatAttachmentInput, type ChatAttachmentKind } from './wire'\nimport { attachmentKindForMime } from '../chat-store/parts'\nimport { redactErrorMessage } from '../redact'\nimport {\n ATTACHMENT_ROLLBACK_FAILURE_CODE,\n ATTACHMENT_ROLLBACK_FAILURE_MESSAGE,\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n type AtomicAttachmentWriter,\n immutableAttachmentPath,\n type AttachmentWriteOwnership,\n type AttachmentWriteReceipt,\n type WriteAttachmentFn,\n type WriteAttachmentStreamFn,\n} from './attachment-store'\nimport {\n inspectAtomicAttachmentWriteResult,\n inspectLegacyAttachmentWriteResult,\n} from './attachment-write-safety'\nimport {\n ALLOWED_ATTACHMENT_SNIFFED_MIMES,\n ATTACHMENT_MAX_COUNT,\n MAX_ATTACHMENT_TOTAL_BYTES,\n MAX_BINARY_ATTACHMENT_BYTES,\n MAX_TEXT_ATTACHMENT_BYTES,\n attachmentSizeErrorMessage,\n attachmentTotalSizeErrorMessage,\n checkAttachmentType,\n defaultValidateAttachmentPath,\n sanitizeAttachmentFileName,\n sniffedMimeImpliedByName,\n type AttachmentPathCheck,\n} from './attachment-validation'\nimport { createStreamingSniffer, sniffBinary, STREAM_SNIFF_HEAD_BYTES, type SniffResult } from './binary-sniff'\nimport { sniffMimeFromName } from './promote-file-part'\n\n/** Stable authorization result for the original writer contract. */\nexport type AttachmentUploadAuthorization =\n | {\n ok: true\n scopeId: string\n writeAttachment?: WriteAttachmentFn\n writeAttachmentStream?: WriteAttachmentStreamFn\n }\n | { ok: false; response: Response }\n\n/** Authorization result for an ownership-safe attachment writer. */\nexport type AtomicAttachmentUploadAuthorization =\n | {\n ok: true\n scopeId: string\n attachmentWriter?: AtomicAttachmentWriter\n }\n | { ok: false; response: Response }\n\n/** The logger receives only sanitized backend details. The client receives the\n * opaque message below, regardless of the store's failure text. */\nexport type AttachmentUploadLogger = Pick<Console, 'error'>\n\nfunction logAttachmentUploadError(\n logger: AttachmentUploadLogger,\n message: string,\n fields: Record<string, unknown>,\n): void {\n try {\n const safeFields = Object.fromEntries(\n Object.entries(fields).map(([key, value]) => [\n key,\n typeof value === 'string' ? redactErrorMessage(value) : value,\n ]),\n )\n logger.error(message, safeFields)\n } catch {\n // Logging must not turn a typed storage failure into a thrown response.\n }\n}\n\ninterface AttachmentUploadRouteCommonOptions {\n /** Overridable caps. Defaults come from `./attachment-validation`. */\n limits?: {\n /** Most files one request may carry. Default {@link ATTACHMENT_MAX_COUNT}. */\n maxCount?: number\n /** Ceiling on a binary file's raw size. Default {@link MAX_BINARY_ATTACHMENT_BYTES}. */\n maxBinaryBytes?: number\n /** Optional binary-file ceilings keyed by the content-sniffed mime. */\n maxBytesBySniffedMime?: ReadonlyMap<string, number>\n /** Ceiling on a text file's raw size. Default {@link MAX_TEXT_ATTACHMENT_BYTES}. */\n maxTextBytes?: number\n /** Aggregate raw-byte ceiling. Default {@link MAX_ATTACHMENT_TOTAL_BYTES}. */\n maxTotalBytes?: number\n /** Largest `multipart/form-data` body this route parses. A form body is\n * held whole while it parses, at about three copies at peak, so a\n * product that admits files too large for that sets this and takes them\n * as raw bodies (see {@link createAttachmentUploadRoute}). Default: no\n * limit beyond the per-file and aggregate caps. */\n maxFormBytes?: number\n }\n /** Attachment kinds this route accepts. Default `['image', 'file']`. */\n allowedKinds?: ChatAttachmentKind[]\n /** Sniffed-mime allowlist fed to `checkAttachmentType`. */\n allowedSniffedMimes?: ReadonlySet<string>\n /** Sanitized-name → logical store path. */\n pathFor?: (name: string) => string\n /** Store-path validator. Default {@link defaultValidateAttachmentPath}. */\n validatePath?: (path: string) => AttachmentPathCheck\n /** Last-resort media-type hook for text content the sniffer cannot type. */\n sniffMime?: (name: string) => string\n /** Unique id source for atomic ownership keys. */\n createWriteId?: () => string\n /** Server-side error sink. Backend text is sanitized before it is logged. */\n logger?: AttachmentUploadLogger\n}\n\n/** Stable route options for products using the original writer contract. */\nexport interface CreateLegacyAttachmentUploadRouteOptions extends AttachmentUploadRouteCommonOptions {\n /** Authenticate the caller, rate-limit, and resolve the store scope\n * (workspace/tenant id) — never a query param. */\n authorize(args: { request: Request }): Promise<AttachmentUploadAuthorization>\n /** Stable writer. `authorize` may override it per-request. */\n writeAttachment: WriteAttachmentFn\n /** Writer for a raw-body upload larger than {@link STREAM_SNIFF_HEAD_BYTES}:\n * the route checks the file while it streams to this writer, so the file\n * is never held whole. Without it, a raw body is read whole and goes\n * through `writeAttachment` under the same caps. */\n writeAttachmentStream?: WriteAttachmentStreamFn\n}\n\n/**\n * The original public route-options interface remains available for consumers\n * that extend it. Ownership-safe callers use the atomic interface below.\n */\nexport interface CreateAttachmentUploadRouteOptions extends CreateLegacyAttachmentUploadRouteOptions {}\n\n/** Ownership-safe route options for new products. */\nexport interface CreateAtomicAttachmentUploadRouteOptions extends AttachmentUploadRouteCommonOptions {\n /** Authenticate the caller, rate-limit, and resolve the store scope. */\n authorize(args: { request: Request }): Promise<AtomicAttachmentUploadAuthorization>\n /** Complete writer + ambiguous-write cleanup adapter. */\n attachmentWriter: AtomicAttachmentWriter\n}\n\nfunction attachmentUploadError(status: number, code: string, message: string, path?: string): Response {\n return Response.json(\n { error: path === undefined ? { code, message } : { code, message, path } },\n { status },\n )\n}\n\nfunction atomicAttachmentFailure(path: string, cleanupFailures = 0): Response {\n return attachmentUploadError(\n 503,\n cleanupFailures > 0 ? ATTACHMENT_ROLLBACK_FAILURE_CODE : 'attachment_store_unavailable',\n cleanupFailures > 0 ? ATTACHMENT_ROLLBACK_FAILURE_MESSAGE : ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n path,\n )\n}\n\n/** Longest raw-upload `filename` accepted. */\nconst MAX_RAW_FILENAME_LENGTH = 255\n\ntype RawUploadTarget =\n | { succeeded: true; filename: string; declaredBytes: number }\n | { succeeded: false; response: Response }\n\n/**\n * A raw upload is one file as the request body, named by the `filename`\n * query parameter, with its size in `Content-Length` or `X-Upload-Length`\n * (the browser's `File.size`), as in `/object-store`'s upload route. Knowing\n * the size up front is what lets a store receive a fixed-length stream.\n */\nfunction rawUploadTarget(request: Request): RawUploadTarget {\n const filename = new URL(request.url).searchParams.get('filename')\n if (!filename || filename.length > MAX_RAW_FILENAME_LENGTH) {\n return {\n succeeded: false,\n response: attachmentUploadError(\n 400,\n 'invalid_upload',\n 'Expected a multipart/form-data body with file fields, or one raw file body named by the filename query parameter',\n ),\n }\n }\n const uploadLength = request.headers.get('X-Upload-Length')\n const transportLength = request.headers.get('Content-Length')\n if (uploadLength !== null && transportLength !== null && uploadLength !== transportLength) {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_length', 'Conflicting upload lengths') }\n }\n const declared = uploadLength ?? transportLength\n if (declared === null) {\n return {\n succeeded: false,\n response: attachmentUploadError(411, 'length_required', 'A raw upload declares its size in Content-Length or X-Upload-Length'),\n }\n }\n const declaredBytes = Number(declared)\n if (!/^(0|[1-9]\\d*)$/.test(declared) || !Number.isSafeInteger(declaredBytes)) {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_length', 'Invalid upload length') }\n }\n return { succeeded: true, filename, declaredBytes }\n}\n\nconst LENGTH_MISMATCH_MESSAGE = 'Upload length does not match the file body'\n\n/** Read a small raw body whole, refusing one longer or shorter than declared. */\nasync function readRawBody(\n request: Request,\n declaredBytes: number,\n): Promise<{ succeeded: true; value: Uint8Array<ArrayBuffer> } | { succeeded: false; response: Response }> {\n const mismatch = { succeeded: false as const, response: attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE) }\n const bytes = new Uint8Array(declaredBytes)\n if (!request.body) return declaredBytes === 0 ? { succeeded: true, value: bytes } : mismatch\n const reader = request.body.getReader()\n let offset = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (offset + value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n return mismatch\n }\n bytes.set(value, offset)\n offset += value.length\n }\n } catch {\n return { succeeded: false, response: attachmentUploadError(400, 'invalid_upload', 'The upload body ended early') }\n }\n return offset === declaredBytes ? { succeeded: true, value: bytes } : mismatch\n}\n\n/** Read a body of at most `maxBytes`, refusing a longer one as soon as it\n * passes the cap. A declared `Content-Length` over the cap is refused before\n * any byte is read. */\nasync function readBodyUpTo(\n request: Request,\n maxBytes: number,\n): Promise<{ succeeded: true; value: Uint8Array<ArrayBuffer> } | { succeeded: false; tooLarge: boolean }> {\n const declared = Number(request.headers.get('Content-Length') ?? Number.NaN)\n if (Number.isFinite(declared) && declared > maxBytes) return { succeeded: false, tooLarge: true }\n if (!request.body) return { succeeded: true, value: new Uint8Array(0) }\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let length = 0\n try {\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n length += value.length\n if (length > maxBytes) {\n await reader.cancel().catch(() => undefined)\n return { succeeded: false, tooLarge: true }\n }\n chunks.push(value)\n }\n } catch {\n return { succeeded: false, tooLarge: false }\n }\n const bytes = new Uint8Array(length)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.length\n }\n return { succeeded: true, value: bytes }\n}\n\n/** The media type a streamed write records before the whole file is read.\n * The head decides every family except a zip container, whose Office\n * subtype lives in the central directory at the end; for that, the\n * extension's implied type stands in, and the route's response carries the\n * final one. */\nfunction provisionalMediaType(name: string, head: SniffResult, sniffMime: (name: string) => string): string {\n if (!head.binary) return sniffMime(name)\n if (head.mime === 'application/zip') return sniffedMimeImpliedByName(name) ?? head.mime\n return head.mime ?? 'application/octet-stream'\n}\n\ninterface SuccessfulAttachmentWrite {\n path: string\n ownership: AttachmentWriteOwnership\n receipt: AttachmentWriteReceipt\n}\n\n/** Run every available compensation, from the newest write to the oldest. The\n * receipt owns the compare-and-delete operation; this route never deletes a\n * logical path itself. */\nasync function rollbackSuccessfulWrites(\n writes: readonly SuccessfulAttachmentWrite[],\n scopeId: string,\n writer: AtomicAttachmentWriter,\n logger: AttachmentUploadLogger,\n): Promise<number> {\n let failures = 0\n for (let index = writes.length - 1; index >= 0; index -= 1) {\n const write = writes[index]!\n try {\n await write.receipt.rollback()\n } catch (error) {\n failures += 1\n logAttachmentUploadError(logger, '[attachment-upload] cleanup failed', {\n path: write.path,\n ownershipId: write.ownership.id,\n error: redactErrorMessage(error),\n })\n failures += await abortOwnership(writer, scopeId, write.ownership, logger)\n }\n }\n return failures\n}\n\nasync function abortOwnership(\n writer: AtomicAttachmentWriter,\n scopeId: string,\n ownership: AttachmentWriteOwnership,\n logger: AttachmentUploadLogger,\n): Promise<number> {\n try {\n await writer.abort(scopeId, ownership)\n return 0\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] ambiguous write cleanup failed', {\n path: ownership.path,\n ownershipId: ownership.id,\n error: redactErrorMessage(error),\n })\n return 1\n }\n}\n\nfunction isAtomicRouteOptions(\n options: CreateAttachmentUploadRouteOptions | CreateAtomicAttachmentUploadRouteOptions,\n): options is CreateAtomicAttachmentUploadRouteOptions {\n return 'attachmentWriter' in options\n}\n\n/** Resolve an attachment upload route handler with customizable limits and validation options. */\nexport function createAttachmentUploadRoute(\n options: CreateAttachmentUploadRouteOptions | CreateAtomicAttachmentUploadRouteOptions,\n): (request: Request) => Promise<Response> {\n const atomic = isAtomicRouteOptions(options)\n const maxCount = options.limits?.maxCount ?? ATTACHMENT_MAX_COUNT\n const maxBinaryBytes = options.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES\n const maxBytesBySniffedMime = options.limits?.maxBytesBySniffedMime\n const maxTextBytes = options.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES\n const maxTotalBytes = options.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const allowedKinds: ChatAttachmentKind[] = options.allowedKinds ?? ['image', 'file']\n const allowedSniffedMimes = options.allowedSniffedMimes ?? ALLOWED_ATTACHMENT_SNIFFED_MIMES\n const pathFor = options.pathFor ?? ((name: string) => name)\n const validatePath = options.validatePath ?? defaultValidateAttachmentPath\n const sniffMime = options.sniffMime ?? sniffMimeFromName\n const createWriteId = atomic\n ? options.createWriteId ?? (() => {\n if (typeof crypto === 'undefined' || typeof crypto.randomUUID !== 'function') {\n throw new Error('attachment upload requires crypto.randomUUID')\n }\n return crypto.randomUUID()\n })\n : undefined\n const logger = options.logger ?? console\n const maxFormBytes = options.limits?.maxFormBytes\n const streamWriter = atomic ? undefined : options.writeAttachmentStream\n const limitFor = (sniff: SniffResult): number => {\n if (!sniff.binary) return maxTextBytes\n return sniff.mime === null ? maxBinaryBytes : maxBytesBySniffedMime?.get(sniff.mime) ?? maxBinaryBytes\n }\n // The most any admitted type may weigh, checked against a raw upload's\n // declared size before a byte is read; the type's own cap is checked once\n // the content is known.\n const largestFileBytes = Math.max(maxTextBytes, maxBinaryBytes, ...(maxBytesBySniffedMime?.values() ?? []))\n\n /**\n * The streamed lane: one raw body larger than the sniff head, written as it\n * arrives. The sniffer keeps only the head and tail, and the last chunk is\n * withheld until the whole file passes the type, kind and size gates, so a\n * refused file never reaches the store in full and its write fails.\n */\n async function streamRawUpload(input: {\n request: Request\n scopeId: string\n writer: WriteAttachmentStreamFn\n filename: string\n name: string\n path: string\n declaredBytes: number\n }): Promise<Response> {\n const { request, declaredBytes, name, path } = input\n if (!request.body) return attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE)\n const reader = request.body.getReader()\n const sniffer = createStreamingSniffer()\n const head: Uint8Array[] = []\n let refusal: Response | undefined\n const tooLong = () => attachmentUploadError(400, 'invalid_length', LENGTH_MISMATCH_MESSAGE)\n\n try {\n while (sniffer.size < STREAM_SNIFF_HEAD_BYTES) {\n const { done, value } = await reader.read()\n if (done) break\n if (sniffer.size + value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n return tooLong()\n }\n sniffer.push(value)\n head.push(value)\n }\n } catch {\n return attachmentUploadError(400, 'invalid_upload', 'The upload body ended early')\n }\n const mediaTypeBeforeEnd = provisionalMediaType(name, sniffBinary(sniffer.head()), sniffMime)\n\n let held = head.pop()\n let finalSniff: SniffResult | undefined\n const refuse = (controller: ReadableStreamDefaultController<Uint8Array>, response: Response) => {\n refusal = response\n controller.error(new Error('attachment upload refused'))\n }\n const content = new ReadableStream<Uint8Array>({\n start(controller) {\n for (const chunk of head) controller.enqueue(chunk)\n },\n async pull(controller) {\n let next: ReadableStreamReadResult<Uint8Array>\n try {\n next = await reader.read()\n } catch {\n refuse(controller, attachmentUploadError(400, 'invalid_upload', 'The upload body ended early'))\n return\n }\n if (!next.done) {\n if (sniffer.size + next.value.length > declaredBytes) {\n await reader.cancel().catch(() => undefined)\n refuse(controller, tooLong())\n return\n }\n sniffer.push(next.value)\n if (held) controller.enqueue(held)\n held = next.value\n return\n }\n if (sniffer.size !== declaredBytes) {\n refuse(controller, tooLong())\n return\n }\n const sniff = sniffer.finish()\n const typeCheck = checkAttachmentType(name, sniff, allowedSniffedMimes)\n if (!typeCheck.succeeded) {\n refuse(controller, attachmentUploadError(\n typeCheck.code === 'attachment_type_mismatch' ? 400 : 415,\n typeCheck.code,\n typeCheck.message,\n ))\n return\n }\n const kind = attachmentKindForMime(sniff.mime ?? '')\n if (!allowedKinds.includes(kind)) {\n refuse(controller, attachmentUploadError(\n 415,\n 'attachment_kind_not_allowed',\n `${name} is a \"${kind}\" attachment, which this upload route does not accept`,\n ))\n return\n }\n const limit = limitFor(sniff)\n if (declaredBytes > limit) {\n refuse(controller, attachmentUploadError(413, 'attachment_too_large', attachmentSizeErrorMessage(name, declaredBytes, limit)))\n return\n }\n finalSniff = sniff\n if (held) controller.enqueue(held)\n held = undefined\n controller.close()\n },\n cancel(reason) {\n return reader.cancel(reason).catch(() => undefined)\n },\n }, { highWaterMark: 0 })\n\n let written: unknown\n try {\n written = await input.writer(input.scopeId, path, content, {\n mediaType: mediaTypeBeforeEnd,\n name,\n originalName: input.filename,\n size: declaredBytes,\n })\n } catch (error) {\n if (!finalSniff) await reader.cancel().catch(() => undefined)\n if (refusal) return refusal\n logAttachmentUploadError(logger, '[attachment-upload] stream writer failed', { path, error: redactErrorMessage(error) })\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, path)\n }\n if (!finalSniff) await reader.cancel().catch(() => undefined)\n if (refusal) return refusal\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result || (result.ok && !finalSniff)) {\n // A writer that reports success without reading the file to its end\n // stored something the route never checked.\n logAttachmentUploadError(logger, '[attachment-upload] stream writer returned without a checked file', { path })\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, path)\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logAttachmentUploadError(logger, '[attachment-upload] stream write rejected', { path, error: reason })\n return attachmentUploadError(413, 'attachment_write_failed', reason, path)\n }\n const sniff = finalSniff!\n const uploaded: ChatAttachmentInput = {\n path,\n name,\n size: declaredBytes,\n mediaType: sniff.mime ?? sniffMime(name),\n kind: attachmentKindForMime(sniff.mime ?? ''),\n }\n return Response.json({ files: [uploaded] })\n }\n\n return async function attachmentUpload(request: Request): Promise<Response> {\n const auth = await options.authorize({ request })\n if (!auth.ok) return auth.response\n const atomicWriter = atomic\n ? (auth as Extract<AtomicAttachmentUploadAuthorization, { ok: true }>).attachmentWriter ?? options.attachmentWriter\n : undefined\n const legacyWriter = atomic\n ? undefined\n : (auth as Extract<AttachmentUploadAuthorization, { ok: true }>).writeAttachment ?? options.writeAttachment\n if (atomic && !atomicWriter) {\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE)\n }\n\n const files: File[] = []\n const contentType = request.headers.get('Content-Type')?.toLowerCase() ?? ''\n if (!contentType.startsWith('multipart/form-data')) {\n const target = rawUploadTarget(request)\n if (!target.succeeded) return target.response\n const { filename, declaredBytes } = target\n const name = sanitizeAttachmentFileName(filename)\n if (declaredBytes > largestFileBytes) {\n return attachmentUploadError(413, 'attachment_too_large', attachmentSizeErrorMessage(name, declaredBytes, largestFileBytes))\n }\n if (declaredBytes > maxTotalBytes) {\n return attachmentUploadError(413, 'attachments_total_too_large', attachmentTotalSizeErrorMessage(declaredBytes, maxTotalBytes))\n }\n if (streamWriter && declaredBytes > STREAM_SNIFF_HEAD_BYTES) {\n const path = pathFor(name)\n const pathCheck = validatePath(path)\n if (!pathCheck.succeeded) return attachmentUploadError(400, 'invalid_attachment_path', pathCheck.error, path)\n const writer = (auth as Extract<AttachmentUploadAuthorization, { ok: true }>).writeAttachmentStream ?? streamWriter\n return streamRawUpload({ request, scopeId: auth.scopeId, writer, filename, name, path, declaredBytes })\n }\n // A small raw body takes the batch path below as a one-file batch.\n const body = await readRawBody(request, declaredBytes)\n if (!body.succeeded) return body.response\n files.push(new File([body.value], filename))\n } else {\n let formSource: Request | Response = request\n if (maxFormBytes !== undefined) {\n // Read at most the cap before parsing, whether or not the client sent\n // a Content-Length, so an oversized form is refused while small.\n const bounded = await readBodyUpTo(request, maxFormBytes)\n if (!bounded.succeeded && !bounded.tooLarge) {\n return attachmentUploadError(400, 'invalid_upload', 'The upload body ended early')\n }\n if (!bounded.succeeded) {\n return attachmentUploadError(\n 413,\n 'form_upload_too_large',\n `Form uploads are limited to ${formatBytes(maxFormBytes)}; send a larger file as a raw request body named by the filename query parameter`,\n )\n }\n formSource = new Response(bounded.value, { headers: { 'Content-Type': request.headers.get('Content-Type') ?? '' } })\n }\n let form: FormData\n try {\n form = await formSource.formData()\n } catch {\n return attachmentUploadError(400, 'invalid_upload', 'Expected a multipart/form-data body with file fields')\n }\n // Collect every File value regardless of field name — the client may\n // send one field per file or a single repeated field.\n form.forEach((value) => {\n if (value instanceof File) files.push(value)\n })\n }\n if (files.length === 0) {\n return attachmentUploadError(400, 'invalid_upload', 'No files in the upload body')\n }\n\n if (files.length > maxCount) {\n return attachmentUploadError(\n 400,\n 'attachment_count_exceeded',\n `Too many files — the ${maxCount}-file limit was exceeded`,\n )\n }\n\n // Advisory only: client-controlled `file.size`, so a dishonest caller can\n // slip past it — but an honest oversized batch should fail before a\n // single byte is read. The authoritative aggregate check (against the\n // sniffed/decoded byte length) runs per-file in phase 1, below.\n const advisoryTotal = files.reduce((sum, file) => sum + file.size, 0)\n if (advisoryTotal > maxTotalBytes) {\n return attachmentUploadError(\n 413,\n 'attachments_total_too_large',\n attachmentTotalSizeErrorMessage(advisoryTotal, maxTotalBytes),\n )\n }\n\n interface PreparedWrite {\n path: string\n ownershipId?: string\n name: string\n bytes: Uint8Array\n originalName: string\n size: number\n mediaType: string\n kind: ChatAttachmentKind\n }\n\n // Phase 1: validate every file and prepare its write input WITHOUT\n // writing anything. A batch fails atomically — any file's validation\n // error must reject the whole request before an earlier file in the\n // same batch is persisted.\n const prepared: PreparedWrite[] = []\n const seenLogicalPaths = new Set<string>()\n let totalBytes = 0\n\n for (const file of files) {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const sniff = sniffBinary(bytes)\n // Attachment paths double as store keys, so the stored name must fit\n // the store charset; the as-uploaded name survives as `originalName`.\n const name = sanitizeAttachmentFileName(file.name)\n\n const typeCheck = checkAttachmentType(name, sniff, allowedSniffedMimes)\n if (!typeCheck.succeeded) {\n return attachmentUploadError(\n typeCheck.code === 'attachment_type_mismatch' ? 400 : 415,\n typeCheck.code,\n typeCheck.message,\n )\n }\n\n const kind = attachmentKindForMime(sniff.mime ?? '')\n if (!allowedKinds.includes(kind)) {\n return attachmentUploadError(\n 415,\n 'attachment_kind_not_allowed',\n `${name} is a \"${kind}\" attachment, which this upload route does not accept`,\n )\n }\n\n const limit = limitFor(sniff)\n if (bytes.length > limit) {\n return attachmentUploadError(\n 413,\n 'attachment_too_large',\n attachmentSizeErrorMessage(name, bytes.length, limit),\n )\n }\n\n const logicalPath = pathFor(name)\n let ownershipId: string | undefined\n let path = logicalPath\n if (atomic) {\n try {\n ownershipId = createWriteId!()\n path = immutableAttachmentPath(logicalPath, ownershipId)\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] could not allocate ownership key', {\n path: logicalPath,\n error: redactErrorMessage(error),\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n logicalPath,\n )\n }\n }\n const pathCheck = validatePath(path)\n if (!pathCheck.succeeded) {\n return attachmentUploadError(400, 'invalid_attachment_path', pathCheck.error, path)\n }\n // Small hardening over gtm: a batch whose sanitized names collide onto\n // the same store path (e.g. two variously-cased \"Report.PDF\" uploads)\n // would otherwise silently overwrite one with the other in phase 2.\n const duplicatePath = atomic ? logicalPath : path\n if (seenLogicalPaths.has(duplicatePath)) {\n return attachmentUploadError(\n 400,\n 'attachment_duplicate_path',\n `attachments must not repeat a path within one upload: ${duplicatePath}`,\n path,\n )\n }\n seenLogicalPaths.add(duplicatePath)\n\n // Authoritative aggregate check, against the actual decoded byte\n // length rather than the client-reported `file.size` checked above.\n totalBytes += bytes.length\n if (totalBytes > maxTotalBytes) {\n return attachmentUploadError(\n 413,\n 'attachments_total_too_large',\n attachmentTotalSizeErrorMessage(totalBytes, maxTotalBytes),\n )\n }\n\n const mediaType = sniff.mime ?? sniffMime(name)\n prepared.push({ path, ownershipId, name, bytes, originalName: file.name, size: bytes.length, mediaType, kind })\n }\n\n // Phase 2: every file in the batch passed validation — write them all.\n const uploaded: ChatAttachmentInput[] = []\n const successfulWrites: SuccessfulAttachmentWrite[] = []\n for (const input of prepared) {\n if (!atomic) {\n let written: unknown\n try {\n written = await legacyWriter!(auth.scopeId, input.path, input.bytes, {\n mediaType: input.mediaType,\n name: input.name,\n originalName: input.originalName,\n size: input.size,\n })\n } catch (error) {\n logAttachmentUploadError(logger, '[attachment-upload] legacy writer failed', {\n path: input.path,\n error: redactErrorMessage(error),\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n input.path,\n )\n }\n const result = inspectLegacyAttachmentWriteResult(written)\n if (!result) {\n logAttachmentUploadError(logger, '[attachment-upload] legacy writer returned an invalid result', {\n path: input.path,\n })\n return attachmentUploadError(\n 503,\n 'attachment_store_unavailable',\n ATTACHMENT_STORAGE_FAILURE_MESSAGE,\n input.path,\n )\n }\n if (!result.ok) {\n const reason = redactErrorMessage(result.reason)\n logAttachmentUploadError(logger, '[attachment-upload] legacy write rejected', {\n path: input.path,\n error: reason,\n })\n return attachmentUploadError(413, 'attachment_write_failed', reason, input.path)\n }\n uploaded.push({\n path: input.path,\n name: input.name,\n size: input.size,\n mediaType: input.mediaType,\n kind: input.kind,\n })\n continue\n }\n\n const ownershipId = input.ownershipId\n if (!ownershipId) {\n return attachmentUploadError(503, 'attachment_store_unavailable', ATTACHMENT_STORAGE_FAILURE_MESSAGE, input.path)\n }\n const ownership: AttachmentWriteOwnership = Object.freeze({ id: ownershipId, path: input.path })\n let written: Awaited<ReturnType<AtomicAttachmentWriter['write']>>\n try {\n written = await atomicWriter!.write(auth.scopeId, input.path, input.bytes, {\n mediaType: input.mediaType,\n name: input.name,\n originalName: input.originalName,\n size: input.size,\n ownership,\n })\n } catch (error) {\n const cleanupFailures = (await abortOwnership(atomicWriter!, auth.scopeId, ownership, logger)) +\n await rollbackSuccessfulWrites(successfulWrites, auth.scopeId, atomicWriter!, logger)\n logAttachmentUploadError(logger, '[attachment-upload] write failed', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n error: redactErrorMessage(error),\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n const result = inspectAtomicAttachmentWriteResult(written, ownership)\n if (!result) {\n const cleanupFailures = (await abortOwnership(atomicWriter!, auth.scopeId, ownership, logger)) +\n await rollbackSuccessfulWrites(successfulWrites, auth.scopeId, atomicWriter!, logger)\n logAttachmentUploadError(logger, '[attachment-upload] writer returned a mismatched ownership receipt', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n if (!result.ok) {\n const cleanupFailures = (await rollbackSuccessfulWrites([\n { path: input.path, ownership, receipt: result.receipt },\n ], auth.scopeId, atomicWriter!, logger)) + await rollbackSuccessfulWrites(\n successfulWrites,\n auth.scopeId,\n atomicWriter!,\n logger,\n )\n logAttachmentUploadError(logger, '[attachment-upload] write rejected', {\n path: input.path,\n ownershipId: ownership.id,\n cleanupFailures,\n error: redactErrorMessage(result.reason),\n })\n return atomicAttachmentFailure(input.path, cleanupFailures)\n }\n successfulWrites.push({ path: input.path, ownership, receipt: result.receipt })\n uploaded.push({\n path: input.path,\n name: input.name,\n size: input.size,\n mediaType: input.mediaType,\n kind: input.kind,\n })\n }\n\n return Response.json({ files: uploaded })\n }\n}\n","/**\n * `buildDispatchParts` — assemble the `PromptInputPart[]` a turn carrying\n * attachments and/or `@`-mentions dispatches to the sandbox. `parts[0]` is\n * always the full prompt text (typed text plus the attachment + mention pointer\n * blocks); each attachment or mention becomes one media part. An attachment\n * (read from the product store via the injected reader) draws the inline byte\n * budget first; a mention (read from the LIVE box) takes what is left. A file\n * inlines as a `data:` URI when it fits the remaining budget, otherwise uses\n * the in-box file reference supported by the current sandbox prompt contract.\n * Every media part is deduped by its resolved absolute path. This module only\n * produces the parts array; the caller decides when a turn dispatches parts\n * instead of a plain string.\n *\n * Storage-parameterized port of gtm-agent's `dispatch-parts.ts`: the vault\n * default reader is dropped (`readAttachment` is REQUIRED — the product supplies\n * its store adapter), the `GTM_SANDBOX_VAULT_DIR` prefixing becomes the required\n * `resolveAttachmentPath` seam, the `GTM_MULTIMODAL_FORCE_PATH` env fallback\n * becomes an explicit `forcePath` flag, and every budget cap reads an overridable\n * `./wire` constant. Kept behavior-identical for gtm-agent#618 adoption (the\n * demotion math and emitted part shapes reproduce its dispatched prompt bytes).\n */\n\nimport { isWorkspaceFileExportable } from '../web/file-export'\nimport { flattenHistory, type PromptInputPart } from '../sandbox'\nimport {\n statSandboxFileSize,\n readSandboxBinaryBytes,\n type SandboxExecChannel,\n} from '../sandbox/binary-read'\nimport {\n ChatTurnInputError,\n mediaTypeForMentionPath,\n base64WireLen,\n DISPATCH_REQUEST_MAX_BYTES,\n DISPATCH_STRUCTURAL_RESERVE_BYTES,\n DISPATCH_MAX_PARTS,\n type ChatTurnPartInput,\n} from './wire'\nimport type { ChatAttachmentPart, ChatMentionPart } from '../chat-store/parts'\nimport { bytesToBase64 } from './upload'\nimport type { ReadAttachmentFn } from './attachment-store'\nimport { pathToFileURL } from 'node:url'\n\nexport type { PromptInputPart }\n\n/** Resolve the outcome of dispatching parts with success status and corresponding value or error message */\nexport type DispatchPartsOutcome =\n | { succeeded: true; value: PromptInputPart[] }\n | { succeeded: false; error: string }\n\n/** One mention file's size (always) and inline bytes (only when the caller\n * asked for them — a path-only mention never reads its bytes). */\ntype SandboxMentionReadOutcome =\n | { succeeded: true; value: { size: number; base64?: string } }\n | { succeeded: false; error: string }\n\n/** Resolve sandbox mention details by reading from a specified path with optional byte reading */\nexport type ReadSandboxMentionFn = (\n box: SandboxExecChannel,\n absolutePath: string,\n options: { readBytes: boolean },\n) => Promise<SandboxMentionReadOutcome>\n\nfunction byteLen(value: string): number {\n return new TextEncoder().encode(value).length\n}\n\n/**\n * Default mention reader: stats the in-box file (which also proves it still\n * exists — a since-deleted mention fails loud here), then reads its bytes only\n * when the caller wants to inline it. Both hops cross the sandbox exec channel\n * via the substrate's binary-read helpers.\n */\nasync function readSandboxMention(\n box: SandboxExecChannel,\n absolutePath: string,\n options: { readBytes: boolean },\n): Promise<SandboxMentionReadOutcome> {\n const stat = await statSandboxFileSize(box, absolutePath)\n if (!stat.succeeded) {\n return { succeeded: false, error: `mentioned sandbox file missing or unreadable: ${absolutePath} — ${stat.error}` }\n }\n if (!options.readBytes) return { succeeded: true, value: { size: stat.value } }\n\n const read = await readSandboxBinaryBytes(box, absolutePath, stat.value)\n if (!read.succeeded) {\n return { succeeded: false, error: `mentioned sandbox file read failed: ${absolutePath} — ${read.error}` }\n }\n return { succeeded: true, value: { size: stat.value, base64: bytesToBase64(read.value.bytes) } }\n}\n\n/** HARD INVARIANT: an image carries exactly one URL or absolute path, while a\n * file carries the URL required by the current sandbox prompt contract. */\nfunction violatesUrlPathXor(part: PromptInputPart): boolean {\n if (part.type === 'text') return false\n const hasUrl = typeof part.url === 'string' && (\n part.url.startsWith('data:')\n || part.url.startsWith('file://')\n )\n const hasPath = 'path' in part && typeof part.path === 'string' && part.path.startsWith('/')\n if (part.type === 'file') return !hasUrl || hasPath\n return hasUrl === hasPath\n}\n\nfunction sandboxFileUrl(absolutePath: string): string | undefined {\n if (!absolutePath.startsWith('/')) return undefined\n try {\n return pathToFileURL(absolutePath).href\n } catch {\n return undefined\n }\n}\n\n/**\n * Convert the browser-safe chat attachment contract into the current sandbox\n * prompt contract. Images carry exactly one URL or path; generic files carry a\n * filename and URL.\n */\nexport function normalizeChatPromptForSandbox(\n prompt: string | readonly ChatTurnPartInput[],\n): string | PromptInputPart[] {\n if (typeof prompt === 'string') return prompt\n return prompt.map((part): PromptInputPart => {\n if (part.type === 'text') return part\n if ('content' in part) {\n throw new ChatTurnInputError('Sandbox prompt parts do not accept inline content; provide a URL or path')\n }\n\n const url = typeof part.url === 'string' && part.url.length > 0 ? part.url : undefined\n const path = typeof part.path === 'string' && part.path.length > 0 ? part.path : undefined\n if (Boolean(url) === Boolean(path)) {\n throw new ChatTurnInputError(`Sandbox ${part.type} parts require exactly one URL or path`)\n }\n if (path && !path.startsWith('/')) {\n throw new ChatTurnInputError(`Sandbox ${part.type} paths must be absolute: ${path}`)\n }\n\n if (part.type === 'image') {\n return {\n type: 'image',\n ...(part.filename ? { filename: part.filename } : {}),\n ...(part.mediaType ? { mediaType: part.mediaType } : {}),\n ...(url ? { url } : { path: path! }),\n }\n }\n\n const filename = part.filename?.trim()\n if (!filename) {\n throw new ChatTurnInputError('Sandbox file parts require a filename')\n }\n const fileUrl = url ?? sandboxFileUrl(path!)\n if (!fileUrl) {\n throw new ChatTurnInputError(`Sandbox file paths must be absolute: ${path}`)\n }\n return {\n type: 'file',\n filename,\n ...(part.mediaType ? { mediaType: part.mediaType } : {}),\n url: fileUrl,\n }\n })\n}\n\n/** Build input parameters for dispatching chat message parts including text, attachments, mentions, and history */\nexport interface BuildDispatchPartsInput {\n text: string\n attachments: ChatAttachmentPart[]\n mentions?: ChatMentionPart[]\n history: Array<{ role: 'user' | 'assistant'; content: string }>\n systemPrompt: string\n /** Serialized size of the backend profile the SDK inlines into the same\n * prompt request body — a large, non-negotiable rider that must come out of\n * the inline budget or near-cap attachments 413 at the proxy instead of\n * demoting to path parts. */\n profileWireBytes: number\n /** The product's workspace/tenant key, passed to `readAttachment`. */\n scopeId: string\n /** Maps an attachment's store-relative path to the in-box absolute path a\n * local media reference uses (same style as `fileMentionsToParts`'s\n * `resolvePath`). */\n resolveAttachmentPath: (path: string) => string\n /** Maps a mention's workspace-relative path to its in-box absolute path.\n * Default: {@link BuildDispatchPartsInput.resolveAttachmentPath} — in gtm the\n * vault mount roots both; a product that mounts them apart overrides this. */\n resolveMentionPath?: (path: string) => string\n /** The turn's already-ensured box — required when `mentions` is non-empty\n * (mention bytes are read from the live box, not the store). */\n box?: SandboxExecChannel\n /** Force every media part to a local file reference, skipping all inlining. */\n forcePath?: boolean\n /** REQUIRED store reader for attachment content — no default (the product\n * owns its store; see {@link ReadAttachmentFn}). */\n readAttachment: ReadAttachmentFn\n readSandboxMention?: ReadSandboxMentionFn\n /** Whole-request proxy cap. Default {@link DISPATCH_REQUEST_MAX_BYTES}. */\n requestMaxBytes?: number\n /** JSON-envelope reserve off the top of the request cap. Default\n * {@link DISPATCH_STRUCTURAL_RESERVE_BYTES}. */\n structuralReserveBytes?: number\n /** Sidecar per-request parts-array cap. Default {@link DISPATCH_MAX_PARTS}. */\n maxParts?: number\n}\n\n/** Content of one attachment read, normalized to the base64 a `data:` URI\n * needs — `base64` reused verbatim, else `bytes` encoded once. */\nfunction readResultToBase64(read: { base64?: string; bytes?: Uint8Array }): string | undefined {\n if (typeof read.base64 === 'string') return read.base64\n if (read.bytes) return bytesToBase64(read.bytes)\n return undefined\n}\n\n/** Build dispatch parts from input by resolving mentions, paths, and applying size constraints asynchronously */\nexport async function buildDispatchParts(input: BuildDispatchPartsInput): Promise<DispatchPartsOutcome> {\n const readMention = input.readSandboxMention ?? readSandboxMention\n const resolveMentionPath = input.resolveMentionPath ?? input.resolveAttachmentPath\n const forcePath = input.forcePath ?? false\n const mentions = input.mentions ?? []\n const requestMaxBytes = input.requestMaxBytes ?? DISPATCH_REQUEST_MAX_BYTES\n const structuralReserveBytes = input.structuralReserveBytes ?? DISPATCH_STRUCTURAL_RESERVE_BYTES\n const maxParts = input.maxParts ?? DISPATCH_MAX_PARTS\n\n const parts: PromptInputPart[] = [{ type: 'text', text: input.text }]\n // Absolute in-box paths already emitted — a file both attached and mentioned\n // (or mentioned twice) rides as a single media part.\n const emittedAbsPaths = new Set<string>()\n\n const flattenedForSizing = flattenHistory(input.text, input.history)\n // May go negative when history/systemPrompt alone are large — every\n // attachment then fails the `runningInline + cost <= inlineBudget` check\n // below and uses a local file reference, which does not draw on this inline\n // budget.\n const inlineBudget =\n requestMaxBytes\n - base64WireLen(byteLen(flattenedForSizing))\n - byteLen(JSON.stringify(input.systemPrompt))\n - input.profileWireBytes\n - structuralReserveBytes\n\n let runningInline = 0\n\n // Stable input order: attachments dispatch in the order the user attached\n // them, and the pointer block in `input.text` already names them in that\n // same order.\n for (const attachment of input.attachments) {\n if (!attachment.path) {\n return { succeeded: false, error: `attachment path must be non-empty: ${attachment.name}` }\n }\n\n // The reader crosses an external boundary (store/coordinator) — a rejection\n // there must land in the typed outcome, not escape as a thrown error the\n // caller would misattribute to stream init.\n let read: Awaited<ReturnType<ReadAttachmentFn>>\n try {\n read = await input.readAttachment(input.scopeId, attachment.path)\n } catch (err) {\n return {\n succeeded: false,\n error: `attachment store read failed: ${attachment.path} — ${err instanceof Error ? err.message : String(err)}`,\n }\n }\n if (!read.ok) return { succeeded: false, error: read.reason }\n\n const base64 = readResultToBase64(read)\n if (base64 === undefined) {\n return { succeeded: false, error: `attachment store read produced no content: ${attachment.path}` }\n }\n\n const mediaType = attachment.mediaType ?? read.mediaType\n if (attachment.type === 'image' && !mediaType) {\n return { succeeded: false, error: `attachment is missing a mediaType required for an image data URI: ${attachment.path}` }\n }\n\n const absPath = input.resolveAttachmentPath(attachment.path)\n emittedAbsPaths.add(absPath)\n\n if (attachment.type === 'image') {\n const inlinePart: PromptInputPart = {\n type: 'image',\n filename: attachment.name,\n mediaType,\n url: `data:${mediaType};base64,${base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n if (!forcePath && runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n } else {\n parts.push({ type: 'image', filename: attachment.name, mediaType, path: absPath })\n }\n continue\n }\n\n // File parts use the current filename + URL contract. A file that is too\n // large to inline keeps its existing in-box file through a file:// URL.\n const fileMediaType = mediaType ?? 'application/octet-stream'\n const inlinePart: PromptInputPart = {\n type: 'file',\n filename: attachment.name,\n mediaType: fileMediaType,\n url: `data:${fileMediaType};base64,${base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n if (!forcePath && runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n } else {\n const url = sandboxFileUrl(absPath)\n if (!url) {\n return { succeeded: false, error: `resolved attachment path must be absolute: ${absPath}` }\n }\n parts.push({ type: 'file', filename: attachment.name, mediaType: fileMediaType, url })\n }\n }\n\n if (mentions.length > 0 && !input.box) {\n return { succeeded: false, error: 'internal error: sandbox mentions require a box to read from' }\n }\n for (const mention of mentions) {\n if (!mention.path) {\n return { succeeded: false, error: `mention path must be non-empty: ${mention.name}` }\n }\n if (!isWorkspaceFileExportable(mention.path)) {\n return { succeeded: false, error: `mention file is not exportable: ${mention.path}` }\n }\n const absPath = resolveMentionPath(mention.path)\n if (emittedAbsPaths.has(absPath)) continue\n emittedAbsPaths.add(absPath)\n\n const isImage = mention.mentionKind === 'image'\n const mediaType = isImage ? mediaTypeForMentionPath(mention.path) : undefined\n\n // Every mention is stat'd first: it proves the file still exists (a deleted\n // mention fails the turn loud) and gives the size for the inline budget\n // decision without pulling bytes across the exec channel.\n let stat: SandboxMentionReadOutcome\n try {\n stat = await readMention(input.box!, absPath, { readBytes: false })\n } catch (err) {\n return { succeeded: false, error: `mention read failed: ${absPath} — ${err instanceof Error ? err.message : String(err)}` }\n }\n if (!stat.succeeded) return { succeeded: false, error: stat.error }\n\n // Only an image that projects within the remaining budget reads its bytes\n // to inline; every other mention (and a budget-exceeding image) ships\n // by local reference, so a large file is never base64'd unnecessarily.\n const projectedInlineCost = base64WireLen(stat.value.size)\n + byteLen(JSON.stringify({ type: 'image', filename: mention.name, mediaType: mediaType ?? '', url: '' }))\n if (isImage && mediaType && !forcePath && runningInline + projectedInlineCost <= inlineBudget) {\n let read: SandboxMentionReadOutcome\n try {\n read = await readMention(input.box!, absPath, { readBytes: true })\n } catch (err) {\n return { succeeded: false, error: `mention read failed: ${absPath} — ${err instanceof Error ? err.message : String(err)}` }\n }\n if (!read.succeeded) return { succeeded: false, error: read.error }\n if (!read.value.base64) return { succeeded: false, error: `mentioned image produced no bytes: ${absPath}` }\n const inlinePart: PromptInputPart = {\n type: 'image',\n filename: mention.name,\n mediaType,\n url: `data:${mediaType};base64,${read.value.base64}`,\n }\n const cost = byteLen(JSON.stringify(inlinePart))\n // Re-check against the actual serialized cost — the projection can\n // undershoot; a real overshoot demotes to a path part rather than 413ing.\n if (runningInline + cost <= inlineBudget) {\n parts.push(inlinePart)\n runningInline += cost\n continue\n }\n }\n\n if (isImage && mediaType) {\n parts.push({ type: 'image', filename: mention.name, mediaType, path: absPath })\n } else {\n const url = sandboxFileUrl(absPath)\n if (!url) {\n return { succeeded: false, error: `resolved mention path must be absolute: ${absPath}` }\n }\n parts.push({ type: 'file', filename: mention.name, url })\n }\n }\n\n for (const part of parts) {\n if (violatesUrlPathXor(part)) {\n return { succeeded: false, error: 'internal error: emitted media part violates the url/path exclusivity invariant' }\n }\n }\n\n // Final whole-request check sized against what actually crosses the wire: the\n // history-merged text part (the substrate folds `history` into `parts[0]`\n // before dispatch), the media parts, the system prompt, and the inlined\n // backend profile riding the same body — the same terms `inlineBudget` was\n // derived from.\n const textPartSize = base64WireLen(byteLen(flattenedForSizing))\n const mediaPartsSize = parts.slice(1).reduce((total, part) => total + byteLen(JSON.stringify(part)), 0)\n const systemPromptSize = byteLen(JSON.stringify(input.systemPrompt))\n if (textPartSize + mediaPartsSize + systemPromptSize + input.profileWireBytes + structuralReserveBytes > requestMaxBytes) {\n return { succeeded: false, error: 'dispatch parts exceed the sandbox proxy request cap even after path demotion' }\n }\n\n // The sidecar rejects the whole request past its parts-array cap; the caller\n // selects which media ride natively, so overflow here is a caller bug\n // surfaced loudly rather than a truncation.\n if (parts.length > maxParts) {\n return { succeeded: false, error: `dispatch parts exceed the sidecar per-request cap of ${maxParts}` }\n }\n\n return { succeeded: true, value: parts }\n}\n","import type { IntelligenceClient } from '@tangle-network/agent-runtime/intelligence'\nimport type { ChatTurnRouteProducer } from './turn-routes'\n\n/** Host-resolved metadata only. Neither input nor output content is exported. */\nexport interface ChatTurnIntelligenceOptions {\n client: () => Pick<IntelligenceClient, 'traceRun'>\n sessionId: string\n userId?: string\n workspaceId?: string\n /** Exact execution identity, when retained by the execution owner. */\n runId?: string\n traceId?: string\n /** Requested model. This is not a receipt for the model that served. */\n model?: string\n /** Existing transport cancellation evidence; grants no new abort authority. */\n signal?: AbortSignal\n /** Receives no exception text, which may contain private customer material. */\n onObservationError?: () => void\n}\n\n/**\n * Observe the source iterator without an execution loop, relay, queue or eager\n * pull. The source still owns cancellation and serializes its own operations.\n * Stream termination is not task correctness, reviewer acceptance or delivery.\n */\nexport function observeChatTurnStream<TEvent>(\n source: AsyncGenerator<TEvent, void, unknown>,\n options: ChatTurnIntelligenceOptions,\n): AsyncGenerator<TEvent, void, unknown> {\n const { client, sessionId, userId, workspaceId, runId, traceId, model, signal, onObservationError } = options\n let state: 'idle' | 'running' | 'terminal' = 'idle'\n let startedAt = 0\n let interrupted = false\n let observation: Promise<void> | undefined\n\n const finish = (termination: 'exhausted' | 'interrupted' | 'failed'): Promise<void> => {\n if (state !== 'running') return observation ?? Promise.resolve()\n state = 'terminal'\n const completedAt = Date.now()\n const observedTermination = signal?.aborted ? 'interrupted' : termination\n observation = Promise.resolve().then(async () => {\n await client().traceRun({\n ...(runId !== undefined ? { runId } : {}),\n ...(traceId !== undefined ? { traceId } : {}),\n ...(model !== undefined ? { model } : {}),\n labels: {\n 'tangle.sessionId': sessionId,\n ...(userId !== undefined ? { 'tangle.userId': userId } : {}),\n ...(workspaceId !== undefined ? { 'tangle.workspaceId': workspaceId } : {}),\n 'tangle.observation.kind': 'stream-lifecycle',\n 'tangle.stream.termination': observedTermination,\n 'tangle.started_at_ms': startedAt,\n 'tangle.completed_at_ms': completedAt,\n 'tangle.duration_ms': Math.max(0, completedAt - startedAt),\n },\n }, async () => undefined)\n }).catch(() => {\n // Even a diagnostic callback must not replace an execution error.\n try { onObservationError?.() } catch { /* Best-effort telemetry only. */ }\n })\n return observation\n }\n\n const invoke = async (\n method: 'next' | 'return' | 'throw',\n operation: () => ReturnType<typeof source.next>,\n ): Promise<Awaited<ReturnType<typeof source.next>>> => {\n if (state === 'idle') {\n // A pre-start close still reaches the source to release factory-owned\n // resources, but does not manufacture an executed-turn observation.\n if (method === 'next') {\n state = 'running'\n startedAt = Date.now()\n } else {\n state = 'terminal'\n }\n }\n try {\n const result = await operation()\n // Apply return intent in source settlement order. A queued return must\n // not relabel an earlier naturally completed next() as cancellation.\n if (method === 'return' && state === 'running') interrupted = true\n if (result.done) await finish(interrupted ? 'interrupted' : 'exhausted')\n return result\n } catch (error) {\n await finish('failed')\n throw error\n }\n }\n\n return {\n [Symbol.asyncIterator]() { return this },\n next(...args) { return invoke('next', () => source.next(...args)) },\n return(value) { return invoke('return', () => source.return(value)) },\n throw(error) { return invoke('throw', () => source.throw(error)) },\n async [Symbol.asyncDispose]() { await this.return(undefined) },\n }\n}\n\n/**\n * Adapter for product producer factories. It preserves every declared\n * ChatTurnRouteProducer projection, lazily bound to the original receiver,\n * including class-backed or frozen producers. No output is read.\n */\nexport async function produceChatTurnWithIntelligence(\n options: ChatTurnIntelligenceOptions & {\n produce: () => ChatTurnRouteProducer | Promise<ChatTurnRouteProducer>\n },\n): Promise<ChatTurnRouteProducer> {\n // Freeze scalar metadata before an asynchronous producer can change context.\n const snapshot: ChatTurnIntelligenceOptions = { ...options }\n const producer = await options.produce()\n return {\n stream: observeChatTurnStream(producer.stream, snapshot),\n finalText: () => producer.finalText(),\n get assistantParts() { return producer.assistantParts?.bind(producer) },\n get draftParts() { return producer.draftParts?.bind(producer) },\n get usage() { return producer.usage?.bind(producer) },\n get model() { return producer.model },\n get modelFailover() { return producer.modelFailover?.bind(producer) },\n get modelAttribution() { return producer.modelAttribution?.bind(producer) },\n }\n}\n"],"mappings":"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","names":["record","record","parts","record","hasUsage","record","base64","sum","written","result","sniff","sum","written","result","inlinePart","cost"]}
|