{"version":3,"file":"openrouter-video.cjs","names":["DEFAULT_TEST_ID","clampTimeout","applyChaos","flattenHeaders","getTestId","resolveStrictMode","strictOverrideField","buildForwardHeaders","resolveUpstreamUrl","getContext","matchFixtureDiagnostic","strictNoMatchMessage","strictNoMatchLogLine","resolveResponse","isErrorResponse","serializeErrorResponse","isVideoResponse","crypto","resolveProgression","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/openrouter-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n  ChatCompletionRequest,\n  Fixture,\n  HandlerDefaults,\n  RecordConfig,\n  VideoResponse,\n} from \"./types.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n  isVideoResponse,\n  isErrorResponse,\n  serializeErrorResponse,\n  flattenHeaders,\n  getTestId,\n  resolveResponse,\n  resolveStrictMode,\n  strictOverrideField,\n  getContext,\n  strictNoMatchMessage,\n  strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { DEFAULT_TEST_ID } from \"./constants.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { resolveProgression } from \"./fal.js\";\nimport {\n  buildFixtureMatch,\n  buildForwardHeaders,\n  clampTimeout,\n  persistFixture,\n  sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\n\n/**\n * OpenRouter async video lifecycle mock (`/api/v1/videos`). Mirrors the\n * dedicated OpenRouter video-generation API: submit returns a job envelope,\n * status polls advance `pending → in_progress → completed | failed`, and a\n * `/content` endpoint serves the bytes. With `record.providers.openrouter`\n * configured, unmatched submits become a live interactive proxy: the submit\n * is forwarded upstream and answered with a mock-rewritten envelope, each\n * client poll is proxied upstream 1:1, and a completed job is captured\n * eagerly as a fixture (strict mode still wins over record).\n */\n\ninterface OpenRouterVideoRequest {\n  model?: string;\n  prompt?: string;\n  [key: string]: unknown;\n}\n\nconst DEFAULT_OPENROUTER_VIDEO_MODEL = \"bytedance/seedance-2.0\";\n\n// ─── OpenRouterVideoJobMap (TTL + bounded) ──────────────────────────────────\n\nexport const OPENROUTER_VIDEO_MAX_ENTRIES = 10_000;\nconst OPENROUTER_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype OpenRouterVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface OpenRouterVideoReplayJob {\n  kind: \"replay\";\n  jobId: string;\n  status: OpenRouterVideoStatus;\n  /** Number of status polls the caller has made against this job. */\n  pollCount: number;\n  /** Poll-count threshold for `pending → in_progress` transition. */\n  pollsBeforeInProgress: number;\n  /** Poll-count threshold for the transition to the terminal status. */\n  pollsBeforeCompleted: number;\n  /** The matched fixture's video object (terminal status, bytes, cost, error). */\n  video: VideoResponse[\"video\"];\n  /**\n   * Latch for the empty-`error` authoring warn on failed polls — a polling\n   * loop must surface the fixture defect once per job, not once per poll.\n   */\n  emptyErrorWarned?: boolean;\n  /**\n   * Latch for the per-download fixture-defect warns on the content endpoint\n   * (b64 corruption / empty b64 / ignored url). The job's `video` is\n   * immutable after submit, so the warn set is identical on every download —\n   * surface it once per job, not once per re-download.\n   */\n  contentWarnsLatched?: boolean;\n}\n\n/**\n * A job whose lifecycle is proxied live upstream (record mode, no fixture\n * matched at submit). Every client poll is forwarded 1:1 to\n * `upstreamPollingUrl`; when the upstream reports a terminal status the\n * entry is captured as a fixture and MUTATED into a terminal\n * OpenRouterVideoReplayJob — so the content endpoint serves it like any\n * replay job. Under `record.proxyOnly` nothing is ever captured or mutated:\n * the job stays `kind: \"record\"` (terminal statuses included) and the\n * content endpoint live-proxies the stored upstream `unsigned_urls`.\n */\ninterface OpenRouterVideoRecordJob {\n  kind: \"record\";\n  /** The mock-issued jobId the client polls with. */\n  jobId: string;\n  /**\n   * Last upstream status relayed to the client. Terminal values occur under\n   * proxyOnly (the job stays a live proxy forever) and during the capturing\n   * window — `completed` is set synchronously with `capturing` so the content\n   * endpoint can live-proxy downloads while the eager capture is in flight.\n   * Outside those two cases the capturing path mutates the entry into a\n   * replay job at the terminal poll.\n   */\n  status: OpenRouterVideoStatus;\n  /** Upstream's own job id (recorded as the fixture's video.id). */\n  upstreamJobId: string;\n  /** Origin-validated absolute upstream URL proxied on every client poll. */\n  upstreamPollingUrl: string;\n  /**\n   * Match snapshot built at submit time (post requestTransform). Its `model`\n   * is the submitted model as recorded by the standard model-normalization\n   * rules — date suffixes stripped unless `recordFullModelVersion`; model-less\n   * submits record the assumed default model.\n   */\n  match: Fixture[\"match\"];\n  /**\n   * Upstream content URLs from a completed poll, stored under proxyOnly and\n   * during the capturing window so the content endpoint can live-proxy the\n   * bytes (never cached). Stored UNFILTERED — positions must line up with the\n   * indexes the rewritten relay handed the client — so entries may be\n   * non-strings; the content proxy skips unusable entries at use time.\n   */\n  upstreamUnsignedUrls?: unknown[];\n  /**\n   * Set synchronously before the first await of the eager-capture sequence so\n   * a concurrent completed poll relays the upstream body instead of starting\n   * a second capture/persist.\n   */\n  capturing?: boolean;\n  /**\n   * Latches for the per-poll upstream-defect warns in rewriteRecordPollBody\n   * (mirrors emptyErrorWarned): a polling loop must surface each defect once\n   * per job, not once per poll.\n   */\n  nonArrayUrlsWarned?: boolean;\n  badCostWarned?: boolean;\n}\n\nexport type OpenRouterVideoJob = OpenRouterVideoReplayJob | OpenRouterVideoRecordJob;\n\ninterface OpenRouterVideoEntry {\n  job: OpenRouterVideoJob;\n  createdAt: number;\n}\n\n/**\n * Per-testId job state for the OpenRouter video handler. Mirrors\n * FalQueueStateMap (fal.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, no background sweep timer.\n * Deliberate divergences from FalQueueStateMap:\n *   - lifetime: this map is per-server-instance (cleared on server\n *     close/reset) while FalQueueStateMap is module-global;\n *   - `set()` refreshes: delete-before-set moves a re-inserted key to the\n *     back of the FIFO order AND resets its createdAt, so a refreshed entry\n *     gets a fresh TTL — FalQueueStateMap's plain overwrite keeps the\n *     original insertion slot and is never used as a TTL refresh.\n * Keys are `${testId}:${jobId}`.\n */\nexport class OpenRouterVideoJobMap {\n  private readonly entries = new Map<string, OpenRouterVideoEntry>();\n  private worldGeneration = 0;\n\n  /**\n   * Monotonic world-generation counter, incremented by `clear()` (a fixtures\n   * reset). Identity checks (`jobs.get(key) === job`) can only guard\n   * continuations whose job was ALREADY inserted — a continuation that has\n   * not inserted yet (the submit proxy's upstream fetch) captures this value\n   * before its await and compares after, so it can detect that the world it\n   * belongs to was reset mid-flight and skip seeding the new world.\n   */\n  get generation(): number {\n    return this.worldGeneration;\n  }\n\n  get(key: string): OpenRouterVideoJob | undefined {\n    const entry = this.entries.get(key);\n    if (!entry) return undefined;\n    if (Date.now() - entry.createdAt > OPENROUTER_VIDEO_TTL_MS) {\n      this.entries.delete(key);\n      return undefined;\n    }\n    return entry.job;\n  }\n\n  set(key: string, job: OpenRouterVideoJob): void {\n    // Delete-before-set so a refreshed key moves to the BACK of the Map's\n    // insertion order. A plain overwrite keeps the original insertion slot,\n    // so the FIFO eviction below would treat a freshly TTL-refreshed entry\n    // as the oldest and could evict it under capacity pressure — breaking\n    // the documented \"each successful proxied poll refreshes the TTL\"\n    // guarantee.\n    this.entries.delete(key);\n    this.entries.set(key, { job, createdAt: Date.now() });\n    if (this.entries.size > OPENROUTER_VIDEO_MAX_ENTRIES) {\n      const excess = this.entries.size - OPENROUTER_VIDEO_MAX_ENTRIES;\n      const iter = this.entries.keys();\n      for (let i = 0; i < excess; i++) {\n        const next = iter.next();\n        if (!next.done) this.entries.delete(next.value);\n      }\n    }\n  }\n\n  delete(key: string): boolean {\n    return this.entries.delete(key);\n  }\n\n  clear(): void {\n    this.entries.clear();\n    this.worldGeneration++;\n  }\n\n  get size(): number {\n    return this.entries.size;\n  }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state. handleOpenRouterVideoCreate warns when a \"processing\" fixture is\n * coerced this way.\n */\nfunction terminalStatus(job: OpenRouterVideoReplayJob): OpenRouterVideoStatus {\n  return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics).\n */\nfunction advanceJob(job: OpenRouterVideoReplayJob): void {\n  if (job.status === \"completed\" || job.status === \"failed\") return;\n\n  job.pollCount += 1;\n  if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n    job.status = \"in_progress\";\n  } else if (job.pollCount >= job.pollsBeforeCompleted) {\n    job.status = terminalStatus(job);\n  }\n}\n\n/**\n * First non-empty value of a possibly array-typed, possibly comma-joined\n * header. An empty header value or a leading-comma list (\", host\") would\n * otherwise yield \"\" — triggering a spurious rejection warn and discarding\n * valid later entries — so empty segments are skipped.\n */\nfunction firstForwardedValue(header: string | string[] | undefined): string | undefined {\n  const raw = Array.isArray(header) ? header.join(\",\") : header;\n  if (raw === undefined) return undefined;\n  for (const segment of raw.split(\",\")) {\n    const trimmed = segment.trim();\n    if (trimmed) return trimmed;\n  }\n  return undefined;\n}\n\n// Conservative host[:port] shape for x-forwarded-host. Spaces, slashes,\n// userinfo, or any other URL-structure character would corrupt (or smuggle\n// paths into) the generated URLs the value is interpolated into. Underscores\n// are admitted: the value feeds URL-string interpolation, not DNS validation,\n// and underscore hostnames are routine in docker-compose/k8s networks\n// (e.g. my_project_aimock:4010).\nconst FORWARDED_HOST_RE = /^[a-zA-Z0-9._-]+(:\\d+)?$/;\n// Bracketed IPv6 literal host[:port], e.g. [::1] or [::1]:8080 — the bare\n// RE above cannot admit \":\" inside the host without also admitting junk.\nconst FORWARDED_HOST_IPV6_RE = /^\\[[0-9a-fA-F:.]+\\](:\\d+)?$/;\n\nfunction requestBase(req: http.IncomingMessage, logger: Logger): string {\n  // Honor x-forwarded-proto and x-forwarded-host so generated URLs survive a\n  // TLS-terminating or host-rewriting proxy in front of the mock. First\n  // non-empty value wins on comma-joined lists.\n  const candidate = firstForwardedValue(req.headers[\"x-forwarded-proto\"])?.toLowerCase();\n  // Allowlist http/https — any other value (ws, junk header data) falls back.\n  const proto = candidate === \"http\" || candidate === \"https\" ? candidate : \"http\";\n  // Like the proto allowlist, a forwarded host that doesn't look like a bare\n  // host[:port] (or a bracketed IPv6 literal) falls back to the Host header —\n  // with a warn, so a misconfigured proxy isn't silently ignored.\n  const fwdHost = firstForwardedValue(req.headers[\"x-forwarded-host\"]);\n  // The Host fallback gets the same host[:port] validation — a junk Host\n  // (e.g. \"evil.com/path\") could otherwise smuggle URL structure into the\n  // generated URLs. No warn: a missing/odd Host is transport-level noise,\n  // unlike a misconfigured proxy's x-forwarded-host.\n  const rawHost = req.headers.host;\n  let host =\n    rawHost !== undefined &&\n    (FORWARDED_HOST_RE.test(rawHost) || FORWARDED_HOST_IPV6_RE.test(rawHost))\n      ? rawHost\n      : \"localhost\";\n  if (fwdHost !== undefined) {\n    if (FORWARDED_HOST_RE.test(fwdHost) || FORWARDED_HOST_IPV6_RE.test(fwdHost)) {\n      host = fwdHost;\n    } else {\n      logger.warn(\n        `x-forwarded-host value rejected, falling back to Host header: ${JSON.stringify(fwdHost.slice(0, 100))}`,\n      );\n    }\n  }\n  return `${proto}://${host}`;\n}\n\n/**\n * Query-string suffix embedding the request's testId into generated URLs\n * (polling_url, unsigned_urls). The @openrouter/sdk fetches these URLs with\n * standard Authorization but no aimock-specific headers (no x-test-id) — so\n * the testId must travel in the URL for getTestId's `?testId=` fallback to\n * resolve the right job scope. The default testId is omitted to keep\n * single-tenant URLs clean.\n */\nfunction testIdSuffix(testId: string, sep: \"?\" | \"&\"): string {\n  return testId === DEFAULT_TEST_ID ? \"\" : `${sep}testId=${encodeURIComponent(testId)}`;\n}\n\n/** Default upstream timeout for the live lifecycle proxy (matches recorder.ts). */\nconst DEFAULT_UPSTREAM_TIMEOUT_MS = 30_000;\n\n/**\n * Abort signal for the SMALL-JSON upstream fetches on this surface (submit,\n * status poll, models listing), honoring `record.upstreamTimeoutMs` with the\n * same clamp conventions as recorder.ts (non-finite / non-positive values\n * fall back to the 30s default). Note the nuance (documented on\n * RecordConfig.upstreamTimeoutMs): these envelope-sized fetches use the value\n * as a TOTAL deadline via AbortSignal.timeout — indistinguishable from a\n * socket-idle timeout for small bodies. The byte-bearing content fetches use\n * `fetchHeadersWithTimeout` + `readBodyIdle` instead, which implement true\n * idle semantics. An abort rejects the fetch and surfaces through the\n * caller's existing failure path (502 proxy_error on submit/poll, models\n * synthesis fallback).\n */\nfunction upstreamTimeoutSignal(record: RecordConfig | undefined): AbortSignal {\n  return AbortSignal.timeout(clampTimeout(record?.upstreamTimeoutMs, DEFAULT_UPSTREAM_TIMEOUT_MS));\n}\n\n/**\n * Fetch a byte-bearing upstream URL with `record.upstreamTimeoutMs` gating\n * only the HEADERS: the abort timer is cleared the moment the response head\n * arrives, so a long — but steadily progressing — body download is never\n * killed by a total deadline. Body progress is governed separately by\n * `readBodyIdle` (between-chunk idle semantics).\n */\nasync function fetchHeadersWithTimeout(\n  url: URL,\n  headers: Record<string, string>,\n  record: RecordConfig | undefined,\n): Promise<Response> {\n  const timeoutMs = clampTimeout(record?.upstreamTimeoutMs, DEFAULT_UPSTREAM_TIMEOUT_MS);\n  const controller = new AbortController();\n  const timer = setTimeout(\n    () => controller.abort(new Error(`Upstream response headers timed out after ${timeoutMs}ms`)),\n    timeoutMs,\n  );\n  try {\n    return await fetch(url, { headers, signal: controller.signal });\n  } finally {\n    clearTimeout(timer);\n  }\n}\n\n/**\n * Buffer a fetch Response body with IDLE-based timeout semantics: a timer\n * clamped from `record.bodyTimeoutMs` (30s default — same clamp as\n * recorder.ts) is re-armed for every chunk, so a steadily-dripping body of\n * any total duration completes and only a silent mid-body stall rejects.\n * When `cap` > 0 the byte count is enforced DURING the read: on exceed the\n * stream is cancelled and `{ overCap: true }` is returned with NOTHING\n * oversized retained in memory — the cap is a memory guard as much as a\n * disk guard.\n */\nasync function readBodyIdle(\n  res: Response,\n  record: RecordConfig | undefined,\n  cap = 0,\n): Promise<{ overCap: false; buf: Buffer } | { overCap: true; bytesRead: number }> {\n  const body = res.body;\n  if (!body) return { overCap: false, buf: Buffer.alloc(0) };\n  const idleMs = clampTimeout(record?.bodyTimeoutMs, DEFAULT_UPSTREAM_TIMEOUT_MS);\n  const reader = body.getReader();\n  const chunks: Buffer[] = [];\n  let total = 0;\n  try {\n    for (;;) {\n      let idleTimer: NodeJS.Timeout | undefined;\n      // A stream error landing AFTER the idle timeout has already won the\n      // race must never become an unhandledRejection (process crash) — attach\n      // a no-op rejection handler to the read promise BEFORE racing.\n      // Promise.race subscribes to its inputs too, so this is deliberate\n      // defense-in-depth pinning the invariant against a refactor that races\n      // the read differently; the race below still observes the rejection\n      // normally when the read loses first.\n      const readPromise = reader.read();\n      readPromise.catch(() => {});\n      const result = await Promise.race([\n        readPromise,\n        new Promise<never>((_, reject) => {\n          idleTimer = setTimeout(\n            () => reject(new Error(`Upstream response body idle for ${idleMs}ms`)),\n            idleMs,\n          );\n        }),\n      ]).finally(() => clearTimeout(idleTimer));\n      if (result.done) break;\n      total += result.value.byteLength;\n      if (cap > 0 && total > cap) {\n        return { overCap: true, bytesRead: total };\n      }\n      chunks.push(Buffer.from(result.value));\n    }\n  } finally {\n    // Idle expiry and the over-cap early return leave the stream open —\n    // release it. After a normal completion this is a no-op.\n    void reader.cancel().catch(() => {});\n  }\n  return { overCap: false, buf: Buffer.concat(chunks) };\n}\n\n/**\n * Cap on an upstream ERROR body (401/403 relays) buffered by the content\n * proxy. Error bodies are envelope-sized JSON in practice — 64 KB is generous\n * — and unlike the video bytes they are buffered in full before the relay, so\n * an unbounded read would be a memory hole on a hostile upstream.\n */\nconst OPENROUTER_VIDEO_ERROR_BODY_CAP = 64 * 1024;\n\n/**\n * Cap on the small-JSON upstream envelope bodies (submit, status poll, models\n * listing) buffered before parse/relay. Envelopes are KB-scale in practice —\n * 1 MB is generous headroom even for a large models listing — and, like the\n * error-body cap above, they are buffered in full, so an unbounded `text()`\n * would be a memory hole on a hostile upstream.\n */\nconst OPENROUTER_VIDEO_ENVELOPE_BODY_CAP = 1024 * 1024;\n\n/**\n * Bounded read of a small-JSON upstream envelope body (submit, status poll,\n * models listing): readBodyIdle's idle semantics plus the envelope cap. An\n * over-cap body is a hard failure — the throw surfaces through each caller's\n * existing failure path (502 proxy_error on submit/poll, models synthesis\n * fallback) without retaining anything oversized in memory.\n */\nasync function readEnvelopeText(res: Response, record: RecordConfig | undefined): Promise<string> {\n  const read = await readBodyIdle(res, record, OPENROUTER_VIDEO_ENVELOPE_BODY_CAP);\n  if (read.overCap) {\n    throw new Error(\n      `Upstream envelope body exceeded ${OPENROUTER_VIDEO_ENVELOPE_BODY_CAP} bytes (read aborted at ${read.bytesRead})`,\n    );\n  }\n  return read.buf.toString(\"utf8\");\n}\n\n/**\n * Rewrite an upstream poll body for relay to the client. Every field is\n * relayed verbatim EXCEPT the ones that would let the client escape the mock\n * or learn upstream identifiers:\n *   - `id` → the mock jobId;\n *   - a present `polling_url` → the mock's poll URL (testId embedded);\n *   - a present `unsigned_urls` array → an array of the SAME length whose\n *     element i is the mock content URL with `?index=i`. On post-capture\n *     REPLAYS the mock content endpoint serves the index-0 bytes for every\n *     index (only index 0 is captured); during the capture window and under\n *     proxyOnly the index selects the live-proxied upstream URL.\n * `usage` passes through untouched: a non-number `usage.cost` warns but is\n * never coerced or dropped, and no usage is ever invented.\n */\nfunction rewriteRecordPollBody(args: {\n  upstreamBody: Record<string, unknown>;\n  job: OpenRouterVideoRecordJob;\n  req: http.IncomingMessage;\n  testId: string;\n  logger: Logger;\n}): Record<string, unknown> {\n  const { upstreamBody, job, req, testId, logger } = args;\n  const base = requestBase(req, logger);\n  const body: Record<string, unknown> = { ...upstreamBody, id: job.jobId };\n  if (upstreamBody.polling_url !== undefined) {\n    body.polling_url = `${base}/api/v1/videos/${job.jobId}${testIdSuffix(testId, \"?\")}`;\n  }\n  if (Array.isArray(upstreamBody.unsigned_urls)) {\n    body.unsigned_urls = upstreamBody.unsigned_urls.map(\n      (_url, i) =>\n        `${base}/api/v1/videos/${job.jobId}/content?index=${i}${testIdSuffix(testId, \"&\")}`,\n    );\n  } else if (upstreamBody.unsigned_urls !== undefined) {\n    // A non-array unsigned_urls cannot be index-rewritten — relaying it\n    // verbatim would leak the upstream value to the client, the exact escape\n    // this rewrite exists to prevent. Strip it with a warn (the any-value\n    // treatment polling_url gets above). Latched once per job (mirroring\n    // emptyErrorWarned): a polling loop must not spam the warn.\n    delete body.unsigned_urls;\n    if (!job.nonArrayUrlsWarned) {\n      job.nonArrayUrlsWarned = true;\n      logger.warn(\n        `Upstream video job ${job.upstreamJobId} reported a non-array unsigned_urls (${JSON.stringify(upstreamBody.unsigned_urls).slice(0, 100)}) — stripped from the relay`,\n      );\n    }\n  }\n  const usage = upstreamBody.usage;\n  if (usage !== null && typeof usage === \"object\" && !Array.isArray(usage)) {\n    const cost = (usage as Record<string, unknown>).cost;\n    if (cost !== undefined && typeof cost !== \"number\" && !job.badCostWarned) {\n      job.badCostWarned = true;\n      logger.warn(\n        `Upstream video job ${job.upstreamJobId} reported a non-number usage.cost (${JSON.stringify(cost).slice(0, 50)}) — passing it through untouched`,\n      );\n    }\n  }\n  return body;\n}\n\n/**\n * Synthesizes a structurally valid journal body for field-validation 400s.\n * JournalEntry.body is typed `ChatCompletionRequest | null`, so the raw\n * parsed body cannot be journaled as-is — journal consumers may walk\n * `body.messages`. The result is ChatCompletionRequest-shaped: `model` is a\n * string (a non-string value is JSON-encoded so the field stays a string\n * without dropping what the caller sent) and `messages` is an empty array\n * (there is no validated prompt to wrap). Raw request fields (including\n * `prompt`) are preserved via the index signature only on this validation\n * path — the success path's syntheticReq is built from scratch and does not\n * carry them.\n */\nfunction validationJournalBody(videoReq: OpenRouterVideoRequest): ChatCompletionRequest {\n  const rawModel = videoReq.model;\n  const model =\n    typeof rawModel === \"string\"\n      ? rawModel\n      : rawModel === undefined\n        ? \"\"\n        : JSON.stringify(rawModel);\n  // Underscore-prefixed keys (`_endpointType`, `_context`, ...) are reserved\n  // for handler-set discriminators that journal consumers treat as trusted —\n  // strip them from the raw client body so a request cannot spoof them.\n  const sanitized = Object.fromEntries(\n    Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n  );\n  return { ...sanitized, model, messages: [] };\n}\n\n// ─── GET /api/v1/videos/{jobId} — status poll ───────────────────────────────\n\nexport async function handleOpenRouterVideoStatus(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  jobId: string,\n  fixtures: Fixture[],\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n  jobs: OpenRouterVideoJobMap,\n): Promise<void> {\n  setCorsHeaders(res);\n  const path = req.url ?? `/api/v1/videos/${jobId}`;\n  const method = req.method ?? \"GET\";\n\n  // Chaos rolls BEFORE the job lookup, so it cannot know whether the poll\n  // would have been proxied (record job) or served locally — the label stays\n  // \"internal\" even in record mode, and a chaos-dropped poll never reaches\n  // the upstream.\n  if (\n    applyChaos(\n      res,\n      null,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: null },\n      \"internal\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  const testId = getTestId(req);\n  const job = jobs.get(`${testId}:${jobId}`);\n\n  if (!job) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 404, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      404,\n      JSON.stringify({ error: { message: `Video job ${jobId} not found`, code: 404 } }),\n    );\n    return;\n  }\n\n  if (job.kind === \"record\") {\n    // Strict means nothing reaches an upstream — a record job's polls are\n    // pure upstream proxies, so an effective-strict request is refused with\n    // the strict 503 family (same gate as the models proxy; the submit-time\n    // gate cannot help a job that was created under a strict-off override).\n    if (resolveStrictMode(defaults.strict, req.headers)) {\n      defaults.logger.error(\n        `STRICT: video job ${jobId} is proxied live upstream (record mode) — refusing the upstream poll`,\n      );\n      journal.add({\n        method,\n        path,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        response: {\n          status: 503,\n          fixture: null,\n          ...strictOverrideField(defaults.strict, req.headers),\n        },\n      });\n      writeErrorResponse(\n        res,\n        503,\n        JSON.stringify({\n          error: {\n            message: `Strict mode: video job ${jobId} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n            type: \"invalid_request_error\",\n            code: \"no_fixture_match\",\n          },\n        }),\n      );\n      return;\n    }\n    await proxyOpenRouterVideoRecordPoll({\n      req,\n      res,\n      job,\n      key: `${testId}:${jobId}`,\n      testId,\n      fixtures,\n      journal,\n      defaults,\n      jobs,\n      method,\n      path,\n    });\n    return;\n  }\n\n  // Guard BEFORE advancing or journaling (file convention): a client that\n  // disconnected while the poll was queued gets neither a write nor a\n  // journal entry — and consumes no progression step or TTL refresh, so the\n  // next LIVE poll observes the state this one would have consumed.\n  if (res.destroyed || res.writableEnded) return;\n  advanceJob(job);\n  // Refresh the TTL on every replay poll, mirroring the record-job proxy\n  // path: the delete-before-set semantics also move the entry to the back of\n  // the FIFO eviction order, so an actively-polled job survives both the TTL\n  // and capacity pressure however long the client keeps polling.\n  jobs.set(`${testId}:${jobId}`, job);\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: { status: 200, fixture: null },\n  });\n\n  const body: Record<string, unknown> = { id: job.jobId, status: job.status };\n  if (job.status === \"completed\") {\n    body.unsigned_urls = [\n      `${requestBase(req, defaults.logger)}/api/v1/videos/${job.jobId}/content?index=0${testIdSuffix(testId, \"&\")}`,\n    ];\n    body.usage = { cost: job.video.cost ?? 0 };\n  } else if (job.status === \"failed\") {\n    if (job.video.error === \"\" && !job.emptyErrorWarned) {\n      // An explicit-but-empty error is an authoring mistake — warn (mirroring\n      // the empty-b64 warn) instead of serving an empty failure reason. Once\n      // per job: a polling loop on a failed job must not spam the warn.\n      job.emptyErrorWarned = true;\n      defaults.logger.warn(\n        `Video fixture for job ${job.jobId} has an empty error message — using the default`,\n      );\n    }\n    body.error = job.video.error || \"Video generation failed\";\n  }\n\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(JSON.stringify(body));\n}\n\n// ─── GET /api/v1/videos/{jobId}/content — download ──────────────────────────\n\n// Minimal valid-prefix MP4 placeholder served when a completed fixture has no\n// `b64` payload: a bare 24-byte `ftyp` box (major brand isom, minor 0x200,\n// compatible brands isom + mp42). Enough for clients that sniff the container\n// signature without requiring real video bytes in every fixture.\nconst PLACEHOLDER_MP4 = Buffer.from([\n  0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d, 0x00, 0x00, 0x02, 0x00,\n  0x69, 0x73, 0x6f, 0x6d, 0x6d, 0x70, 0x34, 0x32,\n]);\n\n// The `index` query param is accepted but ignored (jobs are single-video), and\n// fetching content deliberately does NOT advance job state — clients only\n// learn the content URL from a completed status poll (API fidelity; diverges\n// from fal's advance-on-result queue semantics). Exception: a completed\n// kind:\"record\" job — reachable under record.proxyOnly AND during the eager\n// capture window — live-proxies the stored upstream unsigned_urls instead,\n// honoring the index.\nexport async function handleOpenRouterVideoContent(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  jobId: string,\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n  jobs: OpenRouterVideoJobMap,\n): Promise<void> {\n  setCorsHeaders(res);\n  const path = req.url ?? `/api/v1/videos/${jobId}/content`;\n  const method = req.method ?? \"GET\";\n\n  if (\n    applyChaos(\n      res,\n      null,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: null },\n      \"internal\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  // The real endpoint requires Bearer auth even though the unsigned URL is\n  // otherwise self-contained — the @openrouter/sdk fetches it with the key.\n  // RFC 7235 auth schemes are case-insensitive; the credential must be\n  // non-empty.\n  const authorization = req.headers.authorization;\n  if (!authorization || !/^bearer\\s+\\S/i.test(authorization)) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 401, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      401,\n      JSON.stringify({ error: { message: \"No auth credentials found\", code: 401 } }),\n    );\n    return;\n  }\n\n  const testId = getTestId(req);\n  const job = jobs.get(`${testId}:${jobId}`);\n\n  if (!job) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 404, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      404,\n      JSON.stringify({ error: { message: `Video job ${jobId} not found`, code: 404 } }),\n    );\n    return;\n  }\n\n  if (job.status !== \"completed\") {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 400, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: {\n          message: `Video job ${jobId} is not completed (status: ${job.status})`,\n          code: 400,\n        },\n      }),\n    );\n    return;\n  }\n\n  const queryIdx = path.indexOf(\"?\");\n  const indexParam =\n    queryIdx === -1 ? null : new URLSearchParams(path.slice(queryIdx + 1)).get(\"index\");\n\n  // A completed record job is reachable under record.proxyOnly (live proxy\n  // forever) and during the capturing window (a concurrent poll relayed the\n  // terminal body while the eager capture is still in flight). Live-proxy the\n  // stored upstream URL in both cases (the bytes are never cached).\n  if (job.kind === \"record\") {\n    // Strict means nothing reaches an upstream — gate the live content proxy\n    // exactly like the models proxy and the record-job poll path.\n    if (resolveStrictMode(defaults.strict, req.headers)) {\n      defaults.logger.error(\n        `STRICT: video job ${jobId} content is proxied live upstream (record mode) — refusing the upstream fetch`,\n      );\n      journal.add({\n        method,\n        path,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        response: {\n          status: 503,\n          fixture: null,\n          ...strictOverrideField(defaults.strict, req.headers),\n        },\n      });\n      writeErrorResponse(\n        res,\n        503,\n        JSON.stringify({\n          error: {\n            message: `Strict mode: video job ${jobId} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n            type: \"invalid_request_error\",\n            code: \"no_fixture_match\",\n          },\n        }),\n      );\n      return;\n    }\n    await proxyOpenRouterVideoRecordContent({\n      req,\n      res,\n      job,\n      key: `${testId}:${jobId}`,\n      indexParam,\n      journal,\n      defaults,\n      jobs,\n      method,\n      path,\n    });\n    return;\n  }\n\n  // `index` is accepted but ignored (jobs are single-video) — warn when a\n  // present value asks for anything other than index 0, since the caller is\n  // silently getting index-0 bytes either way.\n  if (indexParam !== null && Number(indexParam) !== 0) {\n    defaults.logger.warn(\n      `Video content request for job ${jobId} asked for index=${indexParam} — the index param is ignored (jobs are single-video); serving index 0`,\n    );\n  }\n\n  let bytes: Buffer;\n  if (job.video.b64) {\n    bytes = Buffer.from(job.video.b64, \"base64\");\n    // Node's base64 decoder is lenient — invalid characters are skipped and\n    // the first \"=\" terminates the decode — so a corrupt payload silently\n    // truncates instead of erroring. Compare the decoded byte count against\n    // what the sanitized input length should yield (every 4 chars → 3 bytes,\n    // floor for a partial final group) and warn on mismatch. Sanitization\n    // mirrors the decoder: whitespace stripped, base64url normalized, and\n    // everything from the first \"=\" on dropped (counting post-padding\n    // characters as data chars would false-flag concatenated-but-valid\n    // payloads like \"QQ==QQ==\", which Node decodes as just their first\n    // group). A length check — rather than byte-exact re-encode equality —\n    // tolerates valid non-canonical base64 whose final character carries\n    // nonzero discarded trailing bits (e.g. \"QR\" re-encodes as \"QQ\") while\n    // still catching skipped-character corruption. The decode is served as-is.\n    const sanitized = job.video.b64\n      .replace(/\\s+/g, \"\")\n      .replace(/-/g, \"+\")\n      .replace(/_/g, \"/\")\n      .replace(/=.*$/, \"\");\n    const expectedBytes = Math.floor((sanitized.length * 3) / 4);\n    if (bytes.length === 0 && !job.contentWarnsLatched) {\n      // Padding-only payloads (e.g. \"=\", \"====\") are truthy but sanitize to\n      // \"\" and decode to 0 bytes — dodging both the empty-string warn and\n      // the length-mismatch check below. Warn whenever a non-empty b64\n      // decodes to nothing; the zero-byte body is still served as-is.\n      defaults.logger.warn(\n        `Video fixture b64 for job ${jobId} decoded to zero bytes — the fixture is not controlling the served content`,\n      );\n    }\n    if (sanitized.length % 4 === 1) {\n      // A length ≡ 1 (mod 4) is base64 the mismatch check cannot catch: the\n      // floor formula agrees with Node's lenient decode whether the payload\n      // is genuinely truncated or merely contains invalid characters the\n      // sanitizer does not strip (e.g. \"AAAA!\" decodes fully).\n      if (!job.contentWarnsLatched) {\n        defaults.logger.warn(\n          `Video fixture b64 for job ${jobId} has length ≡ 1 (mod 4) after sanitization (${sanitized.length} chars) — payload is malformed or contains invalid characters`,\n        );\n      }\n    } else if (bytes.length !== expectedBytes && !job.contentWarnsLatched) {\n      defaults.logger.warn(\n        `Video fixture b64 for job ${jobId} decoded to ${bytes.length} bytes where its length implies ${expectedBytes} — likely corrupt base64`,\n      );\n    }\n  } else {\n    if (job.video.b64 === \"\" && !job.contentWarnsLatched) {\n      // An explicit-but-empty b64 is indistinguishable from an absent one to\n      // the truthiness check above — warn so the author learns the fixture\n      // is not controlling the served bytes.\n      defaults.logger.warn(\n        `Video fixture for job ${jobId} has an empty b64 — serving the placeholder MP4`,\n      );\n    }\n    if (job.video.url && !job.contentWarnsLatched) {\n      // Every other coercion on this surface warns — so does dropping the\n      // author's url. The real OpenRouter content endpoint serves bytes, not\n      // a redirect, so the mock has nothing to do with a url-only fixture.\n      defaults.logger.warn(\n        `Video fixture for job ${jobId} sets video.url but no b64 — url is ignored on the OpenRouter content endpoint; use b64 to control the served bytes (serving the placeholder MP4)`,\n      );\n    }\n    bytes = PLACEHOLDER_MP4;\n  }\n  // Latch the fixture-defect warns above once per job: video is immutable\n  // after submit, so re-downloads would only repeat the identical warn set.\n  job.contentWarnsLatched = true;\n\n  // Guard BEFORE journaling (file convention): a client that disconnected\n  // before the write gets neither a response nor a journal entry.\n  if (res.destroyed || res.writableEnded) return;\n  // Refresh the TTL on a successful serve, mirroring the replay poll path: a\n  // client stepping through downloads (or re-fetching) keeps the job alive\n  // however long the session runs. delete-before-set (jobs.set) also moves\n  // the entry to the back of the FIFO eviction order.\n  jobs.set(`${testId}:${jobId}`, job);\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: { status: 200, fixture: null },\n  });\n\n  // Always video/mp4 — the real endpoint serves video/mp4 even when the\n  // client (e.g. the Speakeasy-generated @openrouter/sdk) sends\n  // Accept: application/octet-stream.\n  res.writeHead(200, { \"Content-Type\": \"video/mp4\", \"Content-Length\": bytes.length });\n  res.end(bytes);\n}\n\n/**\n * Live-proxy a content download for a completed record job — reachable under\n * proxyOnly (forever) and during the eager-capture window: the stored\n * upstream `unsigned_urls[index || 0]` is fetched with the same same-origin\n * Bearer-forwarding gate as the eager-capture path (headers gated by\n * `upstreamTimeoutMs`, body by `bodyTimeoutMs` idle semantics), and the bytes\n * are STREAMED to the client as `video/mp4` (matching the real API regardless\n * of the upstream's content-type) — chunks are relayed as they arrive, so the\n * whole video is never buffered in memory. An upstream 401/403 passes through\n * verbatim (real-API fidelity); other pre-relay failures 502 (a mid-stream\n * stall can only abort the already-committed 200). No state is mutated and\n * nothing is cached — repeated downloads hit the upstream every time.\n * Journaled source:\"proxy\" when the relay commits. Refuses with 502 before\n * contacting the upstream when record mode was disabled mid-flight. A\n * committed relay refreshes the job's TTL (identity-guarded) — a client\n * working through a long download list must not lose the job mid-way.\n */\nasync function proxyOpenRouterVideoRecordContent(args: {\n  req: http.IncomingMessage;\n  res: http.ServerResponse;\n  job: OpenRouterVideoRecordJob;\n  key: string;\n  indexParam: string | null;\n  journal: Journal;\n  defaults: HandlerDefaults;\n  jobs: OpenRouterVideoJobMap;\n  method: string;\n  path: string;\n}): Promise<void> {\n  const { req, res, job, key, indexParam, journal, defaults, jobs, method, path } = args;\n  const logger = defaults.logger;\n\n  const proxyError = (msg: string): void => {\n    logger.error(`OpenRouter video content proxy failed: ${msg}`);\n    // Guard BEFORE journaling (file convention): a disconnected client gets\n    // neither a write nor a journal entry.\n    if (res.destroyed || res.writableEnded) return;\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: {\n        status: 502,\n        fixture: null,\n        source: \"proxy\",\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n    writeErrorResponse(\n      res,\n      502,\n      JSON.stringify({\n        error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n      }),\n    );\n  };\n\n  // Recording can be disabled mid-flight (LLMock.disableRecording) — an\n  // orphaned record job must fail loudly BEFORE contacting the upstream\n  // (and before forwarding the client's Bearer anywhere), mirroring the\n  // record-job poll gate. The snapshot below is used for every later read\n  // (same discipline as the poll path): the gate and its consumers must see\n  // ONE config object even if the defaults are swapped mid-relay.\n  const record = defaults.record;\n  if (!record) {\n    proxyError(\"record mode is no longer configured for an in-flight record job\");\n    return;\n  }\n\n  const urls = job.upstreamUnsignedUrls ?? [];\n  const parsedIndex = indexParam === null ? 0 : Number(indexParam);\n  const index = Number.isInteger(parsedIndex) && parsedIndex >= 0 ? parsedIndex : 0;\n  if (indexParam !== null && index !== parsedIndex) {\n    // Mirror the replay path's warn convention: a coerced index silently\n    // changes which bytes the caller gets.\n    logger.warn(\n      `Video content request for job ${job.jobId} has an unusable index=${indexParam} — serving index 0`,\n    );\n  }\n  // The stored array is UNFILTERED (positions align with the rewritten relay\n  // indexes), so the addressed entry may be a non-string — skip unusable\n  // entries at use time, warning before the index-0 fallback.\n  const addressed = urls[index];\n  let target = typeof addressed === \"string\" && addressed ? addressed : undefined;\n  if (target === undefined && index !== 0) {\n    // Two distinct upstream defects share this fallback — name the right one:\n    // an index past the array is the CLIENT asking for more videos than the\n    // upstream reported, while an in-range-but-unusable entry is the UPSTREAM\n    // having reported a non-string/empty URL at that position.\n    if (index >= urls.length) {\n      logger.warn(\n        `Video content request for job ${job.jobId} asked for index=${index} but the upstream reported only ${urls.length} unsigned_urls — serving index 0`,\n      );\n    } else {\n      logger.warn(\n        `Video content request for job ${job.jobId} asked for index=${index} but the upstream's unsigned_urls[${index}] is unusable (non-string or empty) — serving index 0`,\n      );\n    }\n    const first = urls[0];\n    target = typeof first === \"string\" && first ? first : undefined;\n  }\n  if (!target) {\n    // Name the actual condition: an empty array is the upstream reporting no\n    // URLs at all, while a non-empty array whose entry 0 is unusable is a\n    // different upstream defect (the index>0 warns above draw the same line).\n    proxyError(\n      urls.length === 0\n        ? `Upstream job ${job.upstreamJobId} completed without usable unsigned_urls`\n        : `Upstream job ${job.upstreamJobId} completed but its unsigned_urls[0] is unusable (non-string or empty)`,\n    );\n    return;\n  }\n\n  let contentUrl: URL;\n  try {\n    contentUrl = new URL(target);\n  } catch {\n    proxyError(`Upstream unsigned_urls[${index}] is not a valid URL (${target.slice(0, 100)})`);\n    return;\n  }\n\n  // The client's Bearer is forwarded ONLY to the configured provider origin —\n  // the same gate as the eager-capture path.\n  const providerBase = record.providers.openrouter;\n  let providerOrigin: string;\n  try {\n    providerOrigin = new URL(providerBase ?? job.upstreamPollingUrl).origin;\n  } catch {\n    try {\n      providerOrigin = new URL(job.upstreamPollingUrl).origin;\n    } catch {\n      proxyError(\n        `Cannot determine the provider origin (invalid provider URL and polling URL) — refusing to forward credentials`,\n      );\n      return;\n    }\n  }\n  const headers = buildForwardHeaders(req);\n  if (contentUrl.origin !== providerOrigin) {\n    delete headers.authorization;\n    logger.warn(\n      `Upstream unsigned_urls[${index}] origin ${contentUrl.origin} differs from the provider origin ${providerOrigin} — fetching content WITHOUT the client's Authorization header`,\n    );\n  }\n\n  // Headers gated by upstreamTimeoutMs, body by bodyTimeoutMs IDLE semantics\n  // — a steadily-downloading large video must never be killed by a total\n  // deadline (see fetchHeadersWithTimeout).\n  let contentRes: Response;\n  try {\n    contentRes = await fetchHeadersWithTimeout(contentUrl, headers, record);\n  } catch (err) {\n    proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n    return;\n  }\n  if (contentRes.status === 401 || contentRes.status === 403) {\n    // Real-API fidelity: an upstream auth rejection is the client's\n    // problem (bad or expired Bearer) — relay the upstream status and body\n    // instead of wrapping them in a generic 502 proxy_error. The body read\n    // is bounded like every other upstream body on this surface (the header\n    // timer was cleared when the head arrived, so a bare arrayBuffer() here\n    // could hang forever): idle semantics from `record.bodyTimeoutMs` plus a\n    // small hard cap. Documented choices — a mid-body STALL 502s (the\n    // upstream status cannot be relayed faithfully without its body), while\n    // an OVER-CAP body relays the upstream status with an empty body\n    // (status fidelity preserved; an error body past 64 KB is pathological).\n    let errBody: Buffer;\n    try {\n      const read = await readBodyIdle(contentRes, record, OPENROUTER_VIDEO_ERROR_BODY_CAP);\n      if (read.overCap) {\n        logger.warn(\n          `Upstream ${contentRes.status} error body for job ${job.upstreamJobId} exceeded ${OPENROUTER_VIDEO_ERROR_BODY_CAP} bytes — relaying the status with an empty body`,\n        );\n        errBody = Buffer.alloc(0);\n      } else {\n        errBody = read.buf;\n      }\n    } catch (err) {\n      proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n      return;\n    }\n    logger.warn(\n      `Upstream content host rejected the download for job ${job.upstreamJobId} (${contentRes.status}) — relaying the upstream status`,\n    );\n    if (res.destroyed || res.writableEnded) return;\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: {\n        status: contentRes.status,\n        fixture: null,\n        source: \"proxy\",\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n    res.writeHead(contentRes.status, {\n      \"Content-Type\": contentRes.headers.get(\"content-type\") ?? \"application/json\",\n    });\n    res.end(errBody);\n    return;\n  }\n  if (!contentRes.ok) {\n    // Include a bounded body sample so the failure is diagnosable (every\n    // other proxy error on this surface carries a body snippet). Bounded\n    // exactly like the 401/403 relay above: idle semantics + the error cap.\n    let sample = \"\";\n    try {\n      const read = await readBodyIdle(contentRes, record, OPENROUTER_VIDEO_ERROR_BODY_CAP);\n      if (!read.overCap) sample = read.buf.toString(\"utf8\").slice(0, 200);\n    } catch {\n      // Body unreadable — the status alone still names the failure.\n    }\n    proxyError(`Content ${contentRes.status}${sample ? `: ${sample}` : \"\"}`);\n    return;\n  }\n\n  // Guard BEFORE journaling: a client that disconnected mid-fetch gets\n  // neither a write nor a journal entry — the journaled 200 must reflect a\n  // response that actually left.\n  if (res.destroyed || res.writableEnded) {\n    void contentRes.body?.cancel().catch(() => {});\n    return;\n  }\n  // Journal when the relay COMMITS (the status line is about to hit the\n  // wire), then STREAM the body chunk-by-chunk: the whole video is never\n  // buffered in memory and the first bytes reach the client while the\n  // upstream is still sending. Journal-accuracy choice: a mid-stream abort\n  // (upstream stall / client disconnect) still happened as a 200 on the wire\n  // — just truncated — so journaling at commit time stays accurate to what\n  // left, whereas journaling only on completion would drop truncated relays\n  // entirely. Aborts are additionally logged below.\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: {\n      status: 200,\n      fixture: null,\n      source: \"proxy\",\n      ...strictOverrideField(defaults.strict, req.headers),\n    },\n  });\n  // TTL refresh at commit time, identity-guarded like every post-await map\n  // touch: an actively-downloading job must not be evicted between fetches.\n  if (jobs.get(key) === job) {\n    jobs.set(key, job);\n  }\n  // Always video/mp4 — matching both the real API and the replay path above.\n  // No Content-Length: the bytes are relayed as they arrive (chunked).\n  res.writeHead(200, { \"Content-Type\": \"video/mp4\" });\n\n  const body = contentRes.body;\n  if (!body) {\n    res.end();\n    return;\n  }\n  // Same IDLE semantics as readBodyIdle: the timer is re-armed per chunk, so\n  // a steadily-dripping body of any total duration completes and only a\n  // silent mid-body stall aborts.\n  const idleMs = clampTimeout(record.bodyTimeoutMs, DEFAULT_UPSTREAM_TIMEOUT_MS);\n  const reader = body.getReader();\n  try {\n    for (;;) {\n      let idleTimer: NodeJS.Timeout | undefined;\n      // Same orphaned-read hygiene as readBodyIdle: a stream error landing\n      // after the idle timeout won the race must never become an\n      // unhandledRejection (see the readBodyIdle comment for the full\n      // reasoning — deliberate defense-in-depth).\n      const readPromise = reader.read();\n      readPromise.catch(() => {});\n      const result = await Promise.race([\n        readPromise,\n        new Promise<never>((_, reject) => {\n          idleTimer = setTimeout(\n            () => reject(new Error(`Upstream response body idle for ${idleMs}ms`)),\n            idleMs,\n          );\n        }),\n      ]).finally(() => clearTimeout(idleTimer));\n      if (result.done) break;\n      if (res.destroyed) return; // client went away mid-stream — stop relaying\n      if (!res.write(Buffer.from(result.value))) {\n        // Backpressure: wait for drain, bailing on close so a client that\n        // disconnects mid-stall cannot wedge the relay — and BOUND the wait\n        // with the same clamped bodyTimeoutMs: a stalled-OPEN client (socket\n        // alive, never reading) would otherwise wedge the relay and pin the\n        // upstream reader forever.\n        const drained = await new Promise<boolean>((resolve) => {\n          const cleanup = (): void => {\n            res.off(\"drain\", onDrain);\n            res.off(\"close\", onClose);\n            clearTimeout(drainTimer);\n          };\n          const onDrain = (): void => {\n            cleanup();\n            resolve(true);\n          };\n          const onClose = (): void => {\n            cleanup();\n            // The loop's res.destroyed check stops the relay — though the\n            // upstream reader's release can lag by up to one idle period\n            // (the next read must resolve or idle-expire before the\n            // destroyed check runs).\n            resolve(true);\n          };\n          const drainTimer = setTimeout(() => {\n            cleanup();\n            resolve(false);\n          }, idleMs);\n          res.once(\"drain\", onDrain);\n          res.once(\"close\", onClose);\n        });\n        if (!drained) {\n          logger.warn(\n            `OpenRouter video content relay for job ${job.upstreamJobId} aborted: the client stopped reading for ${idleMs}ms — destroying the response and releasing the upstream`,\n          );\n          res.destroy(); // the finally below releases the upstream reader\n          return;\n        }\n      }\n    }\n    res.end();\n  } catch (err) {\n    // The headers are already on the wire — a 502 is no longer possible.\n    // Destroy the response so the client observes a truncated transfer\n    // instead of a hang, and log the abort (the journaled 200 stands: a 200\n    // status line did leave — see the journal-accuracy note above).\n    logger.error(\n      `OpenRouter video content relay aborted mid-stream: ${err instanceof Error ? err.message : String(err)}`,\n    );\n    res.destroy();\n  } finally {\n    // Stall abort and the client-gone return leave the stream open — release\n    // it. After a normal completion this is a no-op.\n    void reader.cancel().catch(() => {});\n  }\n}\n\n// ─── GET /api/v1/videos/models — model listing ──────────────────────────────\n\nconst DEFAULT_OPENROUTER_VIDEO_MODELS = [DEFAULT_OPENROUTER_VIDEO_MODEL, \"openai/sora-2\"];\n\nfunction modelEntry(id: string): Record<string, unknown> {\n  return {\n    id,\n    name: id,\n    supported_durations: [4, 8],\n    supported_resolutions: [\"720p\", \"1080p\"],\n    supported_aspect_ratios: [\"16:9\", \"9:16\", \"1:1\"],\n    supported_frame_images: [],\n    supported_sizes: [],\n    generate_audio: false,\n    seed: true,\n    pricing_skus: [],\n  };\n}\n\n/**\n * Synthesizes the OpenRouter video model listing from loaded fixtures —\n * video-endpoint fixtures with a string `match.model` (mirrors the Ollama\n * `/api/tags` synthesis in server.ts). Falls back to a default model set when\n * no video fixtures are loaded. Note video models do not appear in the plain\n * `/api/v1/models` listing on the real API, hence the dedicated route.\n *\n * With `record.providers.openrouter` configured the listing is proxied\n * upstream instead and relayed verbatim on success (journaled\n * source:\"proxy\", never recorded as a fixture); an upstream failure warns\n * and falls back to the synthesis below. Strict mode disables the proxy —\n * a strict request is always served the synthesized listing (strict means\n * \"nothing reaches an upstream\").\n */\nexport async function handleOpenRouterVideoModels(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  fixtures: Fixture[],\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n  setCorsHeaders(res);\n  const path = req.url ?? \"/api/v1/videos/models\";\n  const method = req.method ?? \"GET\";\n\n  if (\n    applyChaos(\n      res,\n      null,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: null },\n      \"internal\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  // Snapshot (file discipline): the gate and its consumers below must see ONE\n  // config object even if the defaults are swapped mid-request.\n  const record = defaults.record;\n  const upstreamBase = record?.providers.openrouter;\n  let proxyAttemptFailed = false;\n  if (upstreamBase && !resolveStrictMode(defaults.strict, req.headers)) {\n    // The try covers ONLY the upstream fetch: a throw from the journal/relay\n    // writes below must not be misattributed to the upstream (it would warn\n    // \"proxy failed\", double-journal, and attempt a second response via the\n    // synthesis fallback).\n    let relay: { text: string; contentType: string } | undefined;\n    try {\n      const target = resolveUpstreamUrl(upstreamBase, \"/api/v1/videos/models\");\n      const upstreamRes = await fetch(target, {\n        headers: buildForwardHeaders(req),\n        signal: upstreamTimeoutSignal(record),\n      });\n      const text = await readEnvelopeText(upstreamRes, record);\n      if (!upstreamRes.ok) {\n        throw new Error(`Models ${upstreamRes.status}: ${text.slice(0, 200)}`);\n      }\n      relay = {\n        text,\n        contentType: upstreamRes.headers.get(\"content-type\") ?? \"application/json\",\n      };\n    } catch (err) {\n      proxyAttemptFailed = true;\n      const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n      defaults.logger.warn(\n        `OpenRouter video models proxy failed (${msg}) — falling back to the synthesized listing`,\n      );\n    }\n    if (relay) {\n      // Guard BEFORE journaling (file convention): a client that\n      // disconnected while the upstream fetch was in flight gets neither a\n      // write nor a journal entry — and never the synthesis fallback.\n      if (res.destroyed || res.writableEnded) return;\n      journal.add({\n        method,\n        path,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        // strictOverrideField on every journaled response (the file-wide\n        // convention — proxied entries included): a proxied path is reachable\n        // with a strict-ON server default only via a per-request strict-OFF\n        // override, which journal consumers must see.\n        response: {\n          status: 200,\n          fixture: null,\n          source: \"proxy\",\n          ...strictOverrideField(defaults.strict, req.headers),\n        },\n      });\n      // Verbatim relay — listings are metadata, never recorded as fixtures.\n      res.writeHead(200, { \"Content-Type\": relay.contentType });\n      res.end(relay.text);\n      return;\n    }\n  }\n\n  const modelIds = new Set<string>();\n  let sawVideoFixture = false;\n  for (const f of fixtures) {\n    if (f.match.endpoint === \"video\") {\n      sawVideoFixture = true;\n      if (f.match.model && typeof f.match.model === \"string\") {\n        modelIds.add(f.match.model);\n      }\n    }\n  }\n  if (modelIds.size === 0 && sawVideoFixture) {\n    // Video fixtures are loaded but none has a string match.model (e.g. all\n    // RegExp models or onVideo registrations) — the listing silently serves\n    // the default set, which can surprise fixture authors.\n    defaults.logger.warn(\n      \"No video fixture contributes a string model — serving the default video model set\",\n    );\n  }\n  const ids = modelIds.size > 0 ? [...modelIds] : DEFAULT_OPENROUTER_VIDEO_MODELS;\n\n  // Guard BEFORE journaling (file convention): the synthesis is reachable\n  // after an AWAITED-but-failed proxy attempt above, so the client may have\n  // disconnected while that fetch was in flight — it gets neither a write\n  // nor a journal entry.\n  if (res.destroyed || res.writableEnded) return;\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    // A synthesis serving as the fallback for a FAILED proxy attempt is\n    // labeled source:\"internal\" so journal consumers can tell it apart from\n    // a relay that actually came from the upstream. A synthesis whose proxy\n    // was never attempted — strict-suppressed or plain no-record — omits\n    // source (the surface's existing convention); a strict override that\n    // suppressed the proxy is surfaced via strictOverrideField like every\n    // other strict-influenced journal entry on this surface.\n    response: {\n      status: 200,\n      fixture: null,\n      ...(proxyAttemptFailed ? { source: \"internal\" as const } : {}),\n      ...strictOverrideField(defaults.strict, req.headers),\n    },\n  });\n\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(JSON.stringify({ data: ids.map((id) => modelEntry(id)) }));\n}\n\n// ─── POST /api/v1/videos — submit ───────────────────────────────────────────\n\nexport async function handleOpenRouterVideoCreate(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  raw: string,\n  fixtures: Fixture[],\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n  jobs: OpenRouterVideoJobMap,\n): Promise<void> {\n  setCorsHeaders(res);\n  const path = req.url ?? \"/api/v1/videos\";\n  const method = req.method ?? \"POST\";\n\n  let videoReq: OpenRouterVideoRequest;\n  try {\n    videoReq = JSON.parse(raw) as OpenRouterVideoRequest;\n  } catch (parseErr) {\n    const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 400, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: {\n          message: `Malformed JSON: ${detail}`,\n          type: \"invalid_request_error\",\n          code: \"invalid_json\",\n        },\n      }),\n    );\n    return;\n  }\n\n  // Reject bodies that parsed but are not a JSON object (null, arrays,\n  // numbers, strings) before touching any fields — mirrors fal's parseBody\n  // guard so callers get a 400 instead of a raw TypeError 500.\n  if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 400, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: {\n          message: \"Request body must be a JSON object\",\n          type: \"invalid_request_error\",\n        },\n      }),\n    );\n    return;\n  }\n\n  // Field-validation 400s journal the parsed body (unlike the malformed-JSON\n  // and non-object paths above, where there is no meaningful object to log).\n  const parsedBody = validationJournalBody(videoReq);\n\n  if (typeof videoReq.prompt !== \"string\" || !videoReq.prompt) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: parsedBody,\n      response: { status: 400, fixture: null },\n    });\n    // Distinguish an absent prompt from one that is present but unusable\n    // (non-string or empty) — \"missing\" would be wrong for the latter. The\n    // invalid-type message mirrors the model check below.\n    const message =\n      videoReq.prompt === undefined\n        ? \"Missing required parameter: 'prompt'\"\n        : \"Invalid type for parameter: 'prompt' must be a non-empty string\";\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: { message, type: \"invalid_request_error\" },\n      }),\n    );\n    return;\n  }\n\n  // An empty-string model is as unusable as a non-string one — it matches no\n  // fixture and is not a real model id — so both get the same 400.\n  if (videoReq.model !== undefined && (typeof videoReq.model !== \"string\" || !videoReq.model)) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: parsedBody,\n      response: { status: 400, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: {\n          message: \"Invalid type for parameter: 'model' must be a non-empty string\",\n          type: \"invalid_request_error\",\n        },\n      }),\n    );\n    return;\n  }\n\n  const syntheticReq: ChatCompletionRequest = {\n    // Model-less submits assume the default model — a fixture restricted to\n    // DEFAULT_OPENROUTER_VIDEO_MODEL will match them.\n    model: videoReq.model ?? DEFAULT_OPENROUTER_VIDEO_MODEL,\n    messages: [{ role: \"user\", content: videoReq.prompt }],\n    _endpointType: \"video\",\n    _context: getContext(req),\n  };\n\n  const testId = getTestId(req);\n  const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n    fixtures,\n    syntheticReq,\n    journal.getFixtureMatchCountsForTest(testId),\n    defaults.requestTransform,\n  );\n\n  if (fixture) {\n    // Match count increments BEFORE applyChaos below by design (mirrors\n    // handleCompletions): a chaos-dropped submit still consumes the fixture's\n    // sequence slot.\n    journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n    defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n  } else {\n    const snippet = videoReq.prompt.slice(0, 80);\n    defaults.logger.debug(\n      `No fixture matched for request (model=${syntheticReq.model}, msg=\"${snippet}\")`,\n    );\n  }\n\n  // Chaos deliberately rolls AFTER body validation and fixture matching\n  // (mirrors handleCompletions) — unlike the GET endpoints above, where chaos\n  // rolls first. Divergence from the generic record path (server.ts):\n  // EVERY firing chaos action here suppresses the would-be proxy entirely —\n  // the submit never reaches the upstream and nothing is recorded. On the\n  // generic path that is true only for drop/disconnect; a chaos \"malformed\"\n  // there still proxies upstream and swaps the relay body via the\n  // beforeWriteResponse hook (the fixture records what upstream really\n  // said), whereas here \"malformed\" synthesizes a mock body with no\n  // upstream contact. The roll is still LABELED \"proxy\" below because that\n  // is what the request would have been had chaos not fired.\n  if (\n    applyChaos(\n      res,\n      fixture,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n      // An unmatched submit is proxied upstream when record mode has an\n      // openrouter provider configured AND strict would not win (strict 503s\n      // before any proxy attempt) — label that chaos roll \"proxy\". A strict\n      // no-match, or no record/provider, is served internally.\n      fixture\n        ? \"fixture\"\n        : resolveStrictMode(defaults.strict, req.headers)\n          ? \"internal\"\n          : defaults.record?.providers.openrouter\n            ? \"proxy\"\n            : \"internal\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  if (!fixture) {\n    // Strict mode wins over record: a strict no-match must fail loudly with\n    // 503 rather than silently recording a new fixture.\n    const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n    if (effectiveStrict) {\n      const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n      defaults.logger.error(strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));\n      journal.add({\n        method,\n        path,\n        headers: flattenHeaders(req.headers),\n        body: syntheticReq,\n        response: {\n          status: 503,\n          fixture: null,\n          ...strictOverrideField(defaults.strict, req.headers),\n        },\n      });\n      writeErrorResponse(\n        res,\n        503,\n        JSON.stringify({\n          error: {\n            message: strictMessage,\n            type: \"invalid_request_error\",\n            code: \"no_fixture_match\",\n          },\n        }),\n      );\n      return;\n    }\n\n    if (defaults.record) {\n      const outcome = await proxyOpenRouterVideoSubmit({\n        req,\n        res,\n        raw,\n        syntheticReq,\n        record: defaults.record,\n        journal,\n        defaults,\n        jobs,\n        method,\n        path,\n      });\n      if (outcome === \"handled\") return;\n      // outcome === \"no_upstream\" — fall through to 404 (fal convention).\n    }\n\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: {\n        status: 404,\n        fixture: null,\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n    writeErrorResponse(\n      res,\n      404,\n      JSON.stringify({ error: { message: \"No fixture matched\", code: 404 } }),\n    );\n    return;\n  }\n\n  // World-generation snapshot (mirrors the record-submit guard): a fixtures\n  // reset can land while the fixture's ResponseFactory below is awaited, and\n  // the job insertion at the bottom must not seed the NEW world with this\n  // pre-reset submit's job.\n  const worldGeneration = jobs.generation;\n  const response = await resolveResponse(fixture, syntheticReq);\n\n  // Guards BEFORE journaling on every branch below (file convention):\n  // resolveResponse awaits the fixture's ResponseFactory, which can be\n  // arbitrarily slow — a client that disconnected meanwhile gets neither a\n  // write nor a journal entry.\n  if (isErrorResponse(response)) {\n    if (res.destroyed || res.writableEnded) return;\n    const status = response.status ?? 500;\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: { status, fixture },\n    });\n    writeErrorResponse(res, status, serializeErrorResponse(response), {\n      retryAfter: response.retryAfter,\n    });\n    return;\n  }\n\n  if (!isVideoResponse(response)) {\n    if (res.destroyed || res.writableEnded) return;\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: { status: 500, fixture },\n    });\n    writeErrorResponse(\n      res,\n      500,\n      JSON.stringify({\n        error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n      }),\n    );\n    return;\n  }\n\n  if (res.destroyed || res.writableEnded) return;\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: syntheticReq,\n    response: { status: 200, fixture },\n  });\n\n  // A fixture authored with any non-terminal status — \"processing\" or a\n  // status outside the union entirely (JSON fixtures bypass the compile-time\n  // check) — has no terminal state to converge on; terminalStatus coerces it\n  // to completed. Keep the behavior (jobs always terminate) but surface the\n  // coercion. Widen to string first: the runtime value may not be in the union.\n  const fixtureStatus: string = response.video.status;\n  if (fixtureStatus === \"processing\") {\n    defaults.logger.warn(\n      `Video fixture has status \"processing\" — treated as completed for /api/v1/videos jobs`,\n    );\n  } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n    defaults.logger.warn(\n      `Video fixture has unknown status \"${fixtureStatus}\" — treating as completed for /api/v1/videos jobs`,\n    );\n  }\n\n  const jobId = crypto.randomUUID();\n  const progression = resolveProgression(defaults.openRouterVideo);\n  const job: OpenRouterVideoReplayJob = {\n    kind: \"replay\",\n    jobId,\n    status: \"pending\",\n    pollCount: 0,\n    pollsBeforeInProgress: progression.pollsBeforeInProgress,\n    pollsBeforeCompleted: progression.pollsBeforeCompleted,\n    // Shallow-copy so later mutation of the fixture/factory response object\n    // cannot retroactively change an in-flight job's stored video.\n    video: { ...response.video },\n  };\n  // Default 0/0 progression seeds the job terminal at submit (mirrors fal's\n  // COMPLETED-on-submit initial status) — content is downloadable with zero\n  // polls; the first poll merely reports the already-terminal status. The\n  // submit envelope still reports \"pending\" like the real API.\n  if (progression.pollsBeforeCompleted === 0) {\n    job.status = terminalStatus(job);\n  }\n  if (jobs.generation === worldGeneration) {\n    jobs.set(`${testId}:${jobId}`, job);\n  } else {\n    // A fixtures reset landed while the ResponseFactory was awaited — the\n    // world this submit belongs to is gone. Mirror the record-submit guard:\n    // skip the insertion but still relay the envelope below (the client's\n    // polls will 404, the same observable outcome as TTL eviction — warned\n    // here so the 404s are attributable).\n    defaults.logger.warn(\n      `OpenRouter video submit resolved after a fixtures reset — not inserting job ${jobId} into the new world (its polls will 404)`,\n    );\n  }\n\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(\n    JSON.stringify({\n      id: jobId,\n      polling_url: `${requestBase(req, defaults.logger)}/api/v1/videos/${jobId}${testIdSuffix(testId, \"?\")}`,\n      status: \"pending\",\n    }),\n  );\n}\n\n// ─── Record mode: live interactive proxy (submit) ───────────────────────────\n\nconst OPENROUTER_VIDEOS_PATH = \"/api/v1/videos\";\n\n/**\n * Proxy an unmatched video submit to the configured OpenRouter upstream and\n * answer the client with a mock-rewritten envelope: a fresh aimock jobId and\n * a polling_url pointing back at the mock (testId embedded). The upstream\n * lifecycle is then driven interactively by the client's own polls — unlike\n * fal's synchronous queue walk, nothing is polled server-side at submit.\n *\n * Returns \"no_upstream\" when record mode has no openrouter provider URL —\n * the caller falls through to its 404 branch (fal convention).\n */\nasync function proxyOpenRouterVideoSubmit(args: {\n  req: http.IncomingMessage;\n  res: http.ServerResponse;\n  raw: string;\n  syntheticReq: ChatCompletionRequest;\n  record: RecordConfig;\n  journal: Journal;\n  defaults: HandlerDefaults;\n  jobs: OpenRouterVideoJobMap;\n  method: string;\n  path: string;\n}): Promise<\"handled\" | \"no_upstream\"> {\n  const { req, res, raw, syntheticReq, record, journal, defaults, jobs, method, path } = args;\n\n  const upstreamBase = record.providers.openrouter;\n  if (!upstreamBase) {\n    defaults.logger.warn(`No upstream URL configured for provider \"openrouter\" — cannot proxy`);\n    return \"no_upstream\";\n  }\n\n  const proxyError = (msg: string): \"handled\" => {\n    defaults.logger.error(`OpenRouter video submit proxy failed: ${msg}`);\n    // Guard BEFORE journaling (file convention): a disconnected client gets\n    // neither a write nor a journal entry.\n    if (res.destroyed || res.writableEnded) return \"handled\";\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: {\n        status: 502,\n        fixture: null,\n        source: \"proxy\",\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n    writeErrorResponse(\n      res,\n      502,\n      JSON.stringify({\n        error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n      }),\n    );\n    return \"handled\";\n  };\n\n  let submitUrl: URL;\n  let upstreamOrigin: string;\n  try {\n    submitUrl = resolveUpstreamUrl(upstreamBase, OPENROUTER_VIDEOS_PATH);\n    upstreamOrigin = new URL(upstreamBase).origin;\n  } catch (err) {\n    const msg = err instanceof Error ? err.message : String(err);\n    return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n  }\n\n  defaults.logger.warn(\n    `NO FIXTURE MATCH — proxying video submit to ${upstreamBase}${OPENROUTER_VIDEOS_PATH}`,\n  );\n\n  // World-generation snapshot: a fixtures reset landing during the upstream\n  // fetch below clears the job map — the insertion guard after the fetch\n  // compares against this value (identity checks cannot help here: the job\n  // has not been inserted yet).\n  const worldGeneration = jobs.generation;\n\n  let fetched: { status: number; contentType: string | null; text: string };\n  try {\n    const upstreamRes = await fetch(submitUrl, {\n      method: \"POST\",\n      headers: buildForwardHeaders(req),\n      body: raw,\n      signal: upstreamTimeoutSignal(record),\n    });\n    fetched = {\n      status: upstreamRes.status,\n      contentType: upstreamRes.headers.get(\"content-type\"),\n      text: await readEnvelopeText(upstreamRes, record),\n    };\n  } catch (err) {\n    const msg = err instanceof Error ? err.message : \"Unknown proxy error\";\n    return proxyError(msg);\n  }\n\n  if (fetched.status === 401 || fetched.status === 403) {\n    // Real-API fidelity (mirrors the poll and content paths): an upstream\n    // auth rejection is the client's problem (bad or expired Bearer) — relay\n    // the upstream status and body verbatim instead of wrapping them in a\n    // generic 502 proxy_error. No job is created; a retried submit with\n    // fixed credentials proxies again.\n    defaults.logger.warn(\n      `Upstream rejected the video submit (${fetched.status}) — relaying the upstream status`,\n    );\n    if (res.destroyed || res.writableEnded) return \"handled\";\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: {\n        status: fetched.status,\n        fixture: null,\n        source: \"proxy\",\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n    res.writeHead(fetched.status, {\n      \"Content-Type\": fetched.contentType ?? \"application/json\",\n    });\n    res.end(fetched.text);\n    return \"handled\";\n  }\n\n  let upstreamJobId: string;\n  let envelope: Record<string, unknown>;\n  {\n    if (fetched.status < 200 || fetched.status >= 300) {\n      return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n    }\n    let parsed: unknown;\n    try {\n      parsed = JSON.parse(fetched.text);\n    } catch {\n      return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n    }\n    if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n      return proxyError(\"Submit response is not a JSON object\");\n    }\n    envelope = parsed as Record<string, unknown>;\n    upstreamJobId = String(envelope.id ?? \"\").trim();\n    if (!upstreamJobId) {\n      return proxyError(\"Submit response missing id\");\n    }\n  }\n\n  // Origin-validate upstream's polling_url before adopting it: every client\n  // poll forwards the client's Authorization header to this URL, so an\n  // envelope nominating a foreign host must not receive it. Off-origin (or\n  // missing/unparseable) polling_urls fall back to the constructed path on\n  // the configured provider origin.\n  let upstreamPollingUrl = resolveUpstreamUrl(\n    upstreamBase,\n    `${OPENROUTER_VIDEOS_PATH}/${encodeURIComponent(upstreamJobId)}`,\n  ).toString();\n  const envPolling = envelope.polling_url;\n  if (typeof envPolling === \"string\" && envPolling) {\n    try {\n      const parsedPolling = new URL(envPolling);\n      if (parsedPolling.origin === upstreamOrigin) {\n        upstreamPollingUrl = parsedPolling.toString();\n      } else {\n        defaults.logger.warn(\n          `Upstream polling_url origin ${parsedPolling.origin} differs from the configured provider origin ${upstreamOrigin} — using the constructed poll URL instead`,\n        );\n      }\n    } catch {\n      defaults.logger.warn(\n        `Upstream polling_url is not a valid URL (${String(envPolling).slice(0, 100)}) — using the constructed poll URL instead`,\n      );\n    }\n  }\n\n  const testId = getTestId(req);\n  const matchRequest = defaults.requestTransform\n    ? defaults.requestTransform(syntheticReq)\n    : syntheticReq;\n  const jobId = crypto.randomUUID();\n  const job: OpenRouterVideoRecordJob = {\n    kind: \"record\",\n    jobId,\n    status: \"pending\",\n    upstreamJobId,\n    upstreamPollingUrl,\n    match: buildFixtureMatch(matchRequest, record),\n  };\n  if (jobs.generation === worldGeneration) {\n    jobs.set(`${testId}:${jobId}`, job);\n  } else {\n    // A fixtures reset landed while the upstream submit was in flight — the\n    // world this submit belongs to is gone. Seeding the NEW world with a\n    // stale record job would resurrect pre-reset state, so skip the\n    // insertion but still relay the envelope below (the client's polls will\n    // 404, the same observable outcome as TTL eviction — warned here so the\n    // 404s are attributable).\n    defaults.logger.warn(\n      `OpenRouter video submit for upstream job ${upstreamJobId} completed after a fixtures reset — not inserting the job into the new world (its polls will 404)`,\n    );\n  }\n\n  // Guard BEFORE journaling (file convention): a client that disconnected\n  // while the upstream submit was in flight gets neither a write nor a\n  // journal entry. The job entry above stays — TTL eviction reaps it.\n  if (res.destroyed || res.writableEnded) return \"handled\";\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: syntheticReq,\n    response: {\n      status: 200,\n      fixture: null,\n      source: \"proxy\",\n      ...strictOverrideField(defaults.strict, req.headers),\n    },\n  });\n\n  // Same envelope shape as the replay path: mock jobId, mock polling_url\n  // (testId embedded for header-less polls), \"pending\" for API fidelity.\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(\n    JSON.stringify({\n      id: jobId,\n      polling_url: `${requestBase(req, defaults.logger)}/api/v1/videos/${jobId}${testIdSuffix(testId, \"?\")}`,\n      status: \"pending\",\n    }),\n  );\n  return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (status poll + eager capture) ──────\n\n/**\n * Default cap on the DECODED byte size embedded as `b64` in a recorded\n * fixture (32 MB). Override per-server via\n * `record.openRouterVideo.maxContentBytes` (0 = unlimited; negative or\n * non-integer values are treated as the default, with a createServer warn).\n * The cap protects disk AND memory: a capture whose upstream response\n * DECLARES an over-cap Content-Length is skipped without downloading, while\n * a response with no declared length is read as a stream with the byte count\n * enforced during the read — on exceed the download aborts with nothing\n * oversized retained in memory. In both cases the fixture is persisted\n * without `b64` and the same-session job serves the placeholder MP4.\n */\nexport const OPENROUTER_VIDEO_DEFAULT_MAX_CONTENT_BYTES = 32 * 1024 * 1024;\n\n/**\n * Proxy a status poll for a record-mode job 1:1 to the upstream and relay\n * the result with mock-rewritten identifiers (rewriteRecordPollBody: id,\n * polling_url, unsigned_urls; everything else verbatim). When the upstream\n * reports `completed`, the rewritten body is relayed IMMEDIATELY and the\n * eager capture (server-side fetch of unsigned_urls[0], forwarding the\n * polling client's Bearer ONLY same-origin; persist; replay mutation) runs\n * DETACHED in the background — see captureOpenRouterVideoRecordFixture — so\n * an SDK poller is never blocked on a multi-minute download; the content\n * handler (and every later poll) serves the job locally once the capture\n * lands. `failed` persists a failed fixture synchronously on the relaying\n * poll. Every post-await map mutation is identity-guarded\n * (`jobs.get(key) === job`) so a stale response resolving after a concurrent\n * poll's capture replaced the entry can never resurrect the detached record\n * object over the terminal replay job.\n * `cancelled`/`expired` (not representable in VideoResponse.status) pass\n * through with a warn and are never recorded. Under `record.proxyOnly`\n * nothing is captured, persisted, or mutated: terminal polls relay the\n * rewritten upstream body, the upstream unsigned_urls are stored on the\n * record job for the content endpoint to live-proxy, and every later poll\n * keeps proxying upstream. Each successful proxied poll of a record job\n * refreshes its TTL so a long generation cannot be evicted mid-recording.\n */\nasync function proxyOpenRouterVideoRecordPoll(args: {\n  req: http.IncomingMessage;\n  res: http.ServerResponse;\n  job: OpenRouterVideoRecordJob;\n  key: string;\n  testId: string;\n  fixtures: Fixture[];\n  journal: Journal;\n  defaults: HandlerDefaults;\n  jobs: OpenRouterVideoJobMap;\n  method: string;\n  path: string;\n}): Promise<void> {\n  const { req, res, job, key, testId, fixtures, journal, defaults, jobs, method, path } = args;\n  const logger = defaults.logger;\n\n  const journalProxy = (status: number): void => {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: {\n        status,\n        fixture: null,\n        source: \"proxy\",\n        ...strictOverrideField(defaults.strict, req.headers),\n      },\n    });\n  };\n\n  const proxyError = (msg: string): void => {\n    logger.error(`OpenRouter video poll proxy failed: ${msg}`);\n    // Guard BEFORE journaling (file convention): a disconnected client gets\n    // neither a write nor a journal entry.\n    if (res.destroyed || res.writableEnded) return;\n    journalProxy(502);\n    writeErrorResponse(\n      res,\n      502,\n      JSON.stringify({\n        error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n      }),\n    );\n  };\n\n  // Recording can be disabled mid-flight (LLMock.disableRecording) — an\n  // orphaned record job must fail loudly on ANY poll, before contacting the\n  // upstream, rather than silently relaying non-terminal statuses and only\n  // erroring at the terminal poll.\n  const record = defaults.record;\n  if (!record) {\n    proxyError(\"record mode is no longer configured for an in-flight record job\");\n    return;\n  }\n\n  let fetched: { status: number; contentType: string | null; text: string };\n  try {\n    const upstreamRes = await fetch(job.upstreamPollingUrl, {\n      headers: buildForwardHeaders(req),\n      // The locally captured `record` — not defaults.record, which is the\n      // same object today but would silently diverge if the defaults were\n      // ever swapped between the gate above and this fetch.\n      signal: upstreamTimeoutSignal(record),\n    });\n    fetched = {\n      status: upstreamRes.status,\n      contentType: upstreamRes.headers.get(\"content-type\"),\n      text: await readEnvelopeText(upstreamRes, record),\n    };\n  } catch (err) {\n    proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n    return;\n  }\n\n  if (fetched.status === 401 || fetched.status === 403) {\n    // Real-API fidelity (mirrors the content path): an upstream auth\n    // rejection is the client's problem (bad or expired Bearer) — relay the\n    // upstream status and body verbatim instead of wrapping them in a\n    // generic 502 proxy_error. The job is untouched: a later poll with fixed\n    // credentials proxies again.\n    logger.warn(\n      `Upstream rejected the status poll for job ${job.upstreamJobId} (${fetched.status}) — relaying the upstream status`,\n    );\n    if (res.destroyed || res.writableEnded) return;\n    journalProxy(fetched.status);\n    res.writeHead(fetched.status, {\n      \"Content-Type\": fetched.contentType ?? \"application/json\",\n    });\n    res.end(fetched.text);\n    return;\n  }\n\n  let upstreamBody: Record<string, unknown>;\n  {\n    if (fetched.status < 200 || fetched.status >= 300) {\n      proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n      return;\n    }\n    let parsed: unknown;\n    try {\n      parsed = JSON.parse(fetched.text);\n    } catch {\n      proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n      return;\n    }\n    if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n      proxyError(\"Status response is not a JSON object\");\n      return;\n    }\n    upstreamBody = parsed as Record<string, unknown>;\n  }\n\n  const relayJson = (body: Record<string, unknown>): void => {\n    // Guard BEFORE journaling: a client that disconnected while the upstream\n    // poll was in flight gets neither a write nor a journal entry — the\n    // journaled 200 must reflect a response that actually left.\n    if (res.destroyed || res.writableEnded) return;\n    journalProxy(200);\n    res.writeHead(200, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify(body));\n  };\n\n  // Mock-rewritten relay body — used by EVERY branch below so no upstream\n  // URL-bearing field (polling_url, unsigned_urls) can escape the mock.\n  const relayBody = rewriteRecordPollBody({ upstreamBody, job, req, testId, logger });\n\n  const upstreamStatus = String(upstreamBody.status ?? \"\");\n\n  if (upstreamStatus === \"pending\" || upstreamStatus === \"in_progress\") {\n    // Identity guard on EVERY post-await mutation/re-insert: the upstream\n    // fetch above may have resolved AFTER a concurrent poll's capture\n    // replaced the map entry with a terminal replay job (or after TTL\n    // eviction). Mutating or re-inserting the detached dispatch-time\n    // reference would regress the terminal job back to a live proxy — the\n    // body is still relayed either way.\n    if (jobs.get(key) === job) {\n      // Identity alone is NOT enough during the capture window (and under\n      // proxyOnly after a terminal poll): the SAME record object stays in\n      // the map with status \"completed\"/\"failed\", so a stale non-terminal\n      // response resolving late would regress it and reopen the content\n      // endpoint's 400 window. Skip the status write once capturing or\n      // terminal; the TTL refresh below still applies.\n      if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n        job.status = upstreamStatus;\n      }\n      // Refresh the TTL: an actively-polled record job must not be evicted\n      // mid-recording however long the generation takes.\n      jobs.set(key, job);\n    }\n    relayJson(relayBody);\n    return;\n  }\n\n  if (upstreamStatus === \"completed\") {\n    if (record.proxyOnly) {\n      // Proxy-only: no fixtures, no in-memory caching, no replay mutation.\n      // The job stays kind:\"record\"; the upstream unsigned_urls are stored so\n      // the content endpoint can live-proxy the bytes (id mapping is\n      // inherently stateful — the bytes never are). Stored UNFILTERED so the\n      // live proxy's indexes stay aligned with the rewritten relay's.\n      // Identity-guarded like every post-await mutation in this function.\n      if (jobs.get(key) === job) {\n        const urls = upstreamBody.unsigned_urls;\n        job.status = \"completed\";\n        // Refresh the stash only from an array body (the capture-window twin\n        // below guards this exact case): a later completed poll that omits or\n        // corrupts unsigned_urls must not clobber a usable stash with\n        // undefined.\n        if (Array.isArray(urls)) {\n          job.upstreamUnsignedUrls = [...urls];\n        }\n        jobs.set(key, job); // TTL refresh\n      }\n      relayJson(relayBody);\n      return;\n    }\n\n    if (job.capturing || jobs.get(key) !== job) {\n      // A concurrent poll already entered the capture sequence (capturing\n      // latched), or this dispatch-time reference is detached — the map entry\n      // was replaced by the finished capture's replay job (or TTL-evicted)\n      // while the upstream fetch above was in flight. Relay the rewritten\n      // body either way, but NEVER re-insert the stale object (it would\n      // resurrect a permanent live proxy OVER the terminal replay job) and\n      // never start a second capture/persist.\n      if (jobs.get(key) === job) {\n        // Refresh the stored upstream URLs from THIS poll's body (mirroring\n        // the proxyOnly branch): upstream unsigned URLs can rotate between\n        // polls (signed-by-time CDN links), so in-window content\n        // live-proxies must use the freshest set. Non-array bodies keep the\n        // existing stash — a defective later poll must not clobber a usable\n        // one.\n        const freshUrls = upstreamBody.unsigned_urls;\n        if (Array.isArray(freshUrls)) {\n          job.upstreamUnsignedUrls = [...freshUrls];\n        }\n        jobs.set(key, job); // TTL refresh, like every other successful proxied poll\n      }\n      relayJson(relayBody);\n      return;\n    }\n    const urls = upstreamBody.unsigned_urls;\n    // Open the capturing window SYNCHRONOUSLY, before the first await of the\n    // capture sequence: `capturing` is the double-capture guard, and the\n    // terminal status + stashed upstream unsigned_urls make the job fully\n    // observable as completed while the capture is in flight — a client that\n    // follows this (or a concurrent) poll's relayed content URL gets a\n    // live-proxied download instead of a 400.\n    job.capturing = true;\n    job.status = \"completed\";\n    // Refresh the stash only from an array body (the proxyOnly and\n    // capture-window siblings above guard this exact case): a later completed\n    // poll that omits or corrupts unsigned_urls must not clobber a usable\n    // stash with undefined.\n    if (Array.isArray(urls)) {\n      job.upstreamUnsignedUrls = [...urls];\n    }\n    jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n    // Relay the completed body IMMEDIATELY: a real video download can take\n    // minutes, and an SDK poller blocked on it would time out. The capture\n    // (download + persist + replay mutation) runs DETACHED below; the\n    // capturing window keeps the job serving correctly meanwhile.\n    relayJson(relayBody);\n\n    // Detached eager capture. Handles every failure internally and never\n    // rejects; closes the capturing window in its finally.\n    void captureOpenRouterVideoRecordFixture({\n      req,\n      job,\n      key,\n      testId,\n      fixtures,\n      defaults,\n      jobs,\n      record,\n      upstreamBody,\n    });\n    return;\n  }\n\n  if (upstreamStatus === \"failed\") {\n    if (record.proxyOnly) {\n      // Proxy-only: relay the rewritten failure body, persist nothing, keep\n      // the job a live proxy. Identity-guarded like every post-await\n      // mutation in this function.\n      if (jobs.get(key) === job) {\n        job.status = \"failed\";\n        jobs.set(key, job); // TTL refresh\n      }\n      relayJson(relayBody);\n      return;\n    }\n\n    if (jobs.get(key) !== job) {\n      // Concurrency guard: a concurrent poll at \"failed\" already persisted\n      // the failure fixture and replaced the entry while this poll's\n      // upstream fetch was in flight (everything below the fetch is\n      // synchronous, so the first poll to resume wins atomically). Relay\n      // without persisting a duplicate fixture or re-registering it.\n      // This identity check ALSO covers a fixtures reset landing during the\n      // upstream fetch: performFixturesReset clears the job map, so a\n      // cleared world fails the check and the stale failure fixture never\n      // pollutes the next world's fixtures array — valid because everything\n      // from here to persistFixture below is synchronous (no interleaving\n      // point between check and persist).\n      relayJson(relayBody);\n      return;\n    }\n\n    const rawError = upstreamBody.error;\n    let error: string | undefined;\n    if (typeof rawError === \"string\" && rawError) {\n      error = rawError;\n    } else if (rawError !== null && typeof rawError === \"object\" && !Array.isArray(rawError)) {\n      // Canonical envelope shape ({ error: { message, code } }) — extract the\n      // message like the recorder's error-fixture detection does.\n      const message = (rawError as Record<string, unknown>).message;\n      if (typeof message === \"string\" && message) {\n        error = message;\n      }\n    }\n    if (error === undefined && rawError !== undefined && rawError !== null) {\n      logger.warn(\n        `Upstream video job ${job.upstreamJobId} failed with an unusable error value (${JSON.stringify(rawError).slice(0, 100)}) — recording the fixture without an error message (replay serves the default)`,\n      );\n    }\n    const video: VideoResponse[\"video\"] = {\n      id: job.upstreamJobId,\n      status: \"failed\",\n      ...(error !== undefined ? { error } : {}),\n    };\n    const persistResult = persistFixture({\n      record,\n      providerKey: \"openrouter\",\n      testId,\n      fixture: { match: job.match, response: { video } },\n      fixtures,\n      logger,\n    });\n    if (persistResult.kind === \"failed\" && !res.headersSent) {\n      res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n    }\n    jobs.set(key, {\n      kind: \"replay\",\n      jobId: job.jobId,\n      status: \"failed\",\n      pollCount: 0,\n      pollsBeforeInProgress: 0,\n      pollsBeforeCompleted: 0,\n      video: { ...video },\n    });\n    // Passthrough of the upstream failure body, identifiers rewritten.\n    relayJson(relayBody);\n    return;\n  }\n\n  // cancelled / expired / anything else: terminal upstream states that are\n  // not representable in VideoResponse.status — pass through (identifiers\n  // rewritten), record nothing, and keep proxying any further polls live.\n  logger.warn(\n    `Upstream video job ${job.upstreamJobId} reported status \"${upstreamStatus}\" — not representable in fixture video.status; passing through without recording`,\n  );\n  // Identity-guarded TTL refresh — the job keeps proxying live, but a\n  // detached reference is never re-inserted over a replaced entry.\n  if (jobs.get(key) === job) {\n    jobs.set(key, job);\n  }\n  relayJson(relayBody);\n}\n\n/**\n * Detached eager-capture sequence for a completed record job. Runs AFTER the\n * triggering completed poll has been relayed (the client is never blocked on\n * a potentially multi-minute video download): fetches `unsigned_urls[0]`\n * server-side (the polling client's Bearer is forwarded ONLY same-origin),\n * persists the fixture, and mutates the map entry into a terminal replay job.\n * Every failure is handled internally and the returned promise NEVER\n * rejects. Failure semantics: a capture FAILURE — no usable\n * unsigned_urls, an invalid content URL, or a content fetch that errors on\n * headers, status, or body — persists NOTHING and leaves the job a live\n * proxy, so the next completed poll retries the capture (each failure\n * warns). The ONLY degraded persist is the over-cap path (declared or\n * streamed): retrying would always re-exceed the cap, so the b64-less\n * fixture + placeholder is the deliberate, permanent outcome. The capturing\n * window opened by the caller is closed in the finally when the map still\n * holds this record job; on the success path the entry was just replaced and\n * the detached record object deliberately KEEPS capturing=true so a\n * concurrent poll that read it before the replacement still early-relays\n * instead of starting a second capture.\n */\nasync function captureOpenRouterVideoRecordFixture(args: {\n  req: http.IncomingMessage;\n  job: OpenRouterVideoRecordJob;\n  key: string;\n  testId: string;\n  fixtures: Fixture[];\n  defaults: HandlerDefaults;\n  jobs: OpenRouterVideoJobMap;\n  record: RecordConfig;\n  upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n  const { req, job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n  const logger = defaults.logger;\n  const urls = upstreamBody.unsigned_urls;\n\n  const warnings: string[] = [];\n  let capturedB64: string | undefined;\n  try {\n    const usage = upstreamBody.usage;\n    const rawCost =\n      usage !== null && typeof usage === \"object\" && !Array.isArray(usage)\n        ? (usage as Record<string, unknown>).cost\n        : undefined;\n    const cost = typeof rawCost === \"number\" ? rawCost : undefined;\n\n    // Sanitized like the poll-threshold configs: a negative/non-integer cap\n    // is treated as the default (createServer warns at startup).\n    const rawCap = record.openRouterVideo?.maxContentBytes;\n    const cap =\n      rawCap !== undefined && Number.isInteger(rawCap) && rawCap >= 0\n        ? rawCap\n        : OPENROUTER_VIDEO_DEFAULT_MAX_CONTENT_BYTES;\n\n    const urlArray = Array.isArray(urls) ? urls : undefined;\n    if (urlArray && urlArray.length > 1) {\n      logger.warn(\n        `Upstream video job ${job.upstreamJobId} reported ${urlArray.length} unsigned_urls — only index 0 is captured; post-capture replays serve the index-0 bytes for every index`,\n      );\n    }\n    const firstRaw = urlArray?.[0];\n    const firstUrl = typeof firstRaw === \"string\" && firstRaw ? firstRaw : undefined;\n    if (!firstUrl) {\n      // Capture FAILURE: persist nothing, leave the job a live\n      // proxy (the finally clears `capturing`) so the next completed poll —\n      // whose upstream body may carry usable URLs — retries. Distinguish\n      // \"no unsigned_urls at all\" from \"unsigned_urls present but [0] is\n      // unusable\" (non-string, or an empty string) — the two point at\n      // different upstream defects.\n      if (urlArray && urlArray.length > 0) {\n        logger.warn(\n          `Upstream video job ${job.upstreamJobId} completed with an unusable unsigned_urls[0] (${JSON.stringify(firstRaw).slice(0, 100)}) — capture skipped, nothing persisted; the next completed poll retries`,\n        );\n      } else {\n        logger.warn(\n          `Upstream video job ${job.upstreamJobId} completed without unsigned_urls — capture skipped, nothing persisted; the next completed poll retries`,\n        );\n      }\n      return;\n    }\n    let contentUrl: URL;\n    try {\n      contentUrl = new URL(firstUrl);\n    } catch {\n      // Capture failure — persist nothing, retry on the next completed poll.\n      logger.warn(\n        `Upstream unsigned_urls[0] is not a valid URL (${firstUrl.slice(0, 100)}) — capture skipped, nothing persisted; the next completed poll retries`,\n      );\n      return;\n    }\n    {\n      // The client's Bearer is forwarded ONLY to the configured provider\n      // origin — an off-origin content host (CDN or a hostile envelope)\n      // must not receive it.\n      const providerBase = record.providers.openrouter;\n      let providerOrigin: string | undefined;\n      try {\n        providerOrigin = new URL(providerBase ?? job.upstreamPollingUrl).origin;\n      } catch {\n        try {\n          providerOrigin = new URL(job.upstreamPollingUrl).origin;\n        } catch {\n          // Double parse failure (unreachable in practice — the polling URL\n          // was origin-validated at submit). The relay has already left, so\n          // there is no response to 502 — log, persist nothing, and let the\n          // finally close the capturing window (the job stays a live\n          // proxy; a later poll retries the capture).\n          logger.error(\n            `OpenRouter video capture for job ${job.upstreamJobId} aborted: cannot determine the provider origin (invalid provider URL and polling URL) — refusing to forward credentials`,\n          );\n          return;\n        }\n      }\n      const headers = buildForwardHeaders(req);\n      if (contentUrl.origin !== providerOrigin) {\n        delete headers.authorization;\n        logger.warn(\n          `Upstream unsigned_urls[0] origin ${contentUrl.origin} differs from the provider origin ${providerOrigin} — fetching content WITHOUT the client's Authorization header`,\n        );\n      }\n      try {\n        // Headers gated by upstreamTimeoutMs; the body streams under\n        // bodyTimeoutMs IDLE semantics so a long steady download (a >30s\n        // video render) is never aborted by a total deadline.\n        const contentRes = await fetchHeadersWithTimeout(contentUrl, headers, record);\n        if (!contentRes.ok) {\n          // Bounded body sample (idle semantics + the error cap, like the\n          // content proxy's failure path) — a bare status is not diagnosable.\n          let sample = \"\";\n          try {\n            const read = await readBodyIdle(contentRes, record, OPENROUTER_VIDEO_ERROR_BODY_CAP);\n            if (!read.overCap) sample = read.buf.toString(\"utf8\").slice(0, 200);\n          } catch {\n            // Body unreadable — the status alone still names the failure.\n          }\n          throw new Error(`Content ${contentRes.status}${sample ? `: ${sample}` : \"\"}`);\n        }\n        const declaredLength = Number(contentRes.headers.get(\"content-length\") ?? NaN);\n        if (cap > 0 && Number.isFinite(declaredLength) && declaredLength > cap) {\n          // Memory guard: the upstream DECLARED an over-cap size — skip the\n          // download entirely instead of buffering it just to discard it.\n          // Over-cap is the ONE degraded persist (see the function doc): a\n          // retry would always re-exceed the cap, so the b64-less fixture is\n          // deliberate and permanent.\n          void contentRes.body?.cancel().catch(() => {});\n          logger.warn(\n            `Captured video for job ${job.upstreamJobId} declares Content-Length ${declaredLength} — over maxContentBytes (${cap}); skipping the download; fixture persisted WITHOUT b64 (content serves the placeholder MP4)`,\n          );\n          warnings.push(\n            `Declared content length (${declaredLength} bytes) exceeded maxContentBytes (${cap}) — download skipped, b64 omitted`,\n          );\n        } else {\n          // The cap is enforced DURING the streamed read: on exceed the\n          // read aborts and nothing oversized is retained in memory —\n          // the same-session job serves the placeholder, exactly like\n          // the declared-length skip above (the same deliberate over-cap\n          // persist).\n          const read = await readBodyIdle(contentRes, record, cap);\n          if (read.overCap) {\n            logger.warn(\n              `Captured video for job ${job.upstreamJobId} streamed past maxContentBytes (${cap}) — download aborted at ${read.bytesRead} bytes; fixture persisted WITHOUT b64 (content serves the placeholder MP4)`,\n            );\n            warnings.push(\n              `Captured video exceeded maxContentBytes (${cap}) — download aborted, b64 omitted`,\n            );\n          } else {\n            capturedB64 = read.buf.toString(\"base64\");\n          }\n        }\n      } catch (err) {\n        // Capture failure (headers, status, or body) — transient by nature:\n        // persist nothing, leave the job live, warn; the next completed poll\n        // retries (was: persist a b64-less fixture forever).\n        const msg = err instanceof Error ? err.message : \"Unknown capture error\";\n        logger.warn(\n          `OpenRouter video content capture failed (${msg}) — nothing persisted; the job keeps proxying live and the next completed poll retries`,\n        );\n        return;\n      }\n    }\n\n    const video: VideoResponse[\"video\"] = {\n      id: job.upstreamJobId,\n      status: \"completed\",\n      ...(capturedB64 !== undefined ? { b64: capturedB64 } : {}),\n      ...(cost !== undefined ? { cost } : {}),\n    };\n    // World-generation guard: a fixtures reset (POST\n    // /__aimock/reset/fixtures) landing during the multi-second capture\n    // above clears BOTH the fixtures array and the job map\n    // (performFixturesReset) — so map identity is a valid proxy for \"same\n    // world\": if `jobs.get(key)` no longer returns this job, the world this\n    // capture belongs to is gone and persisting would push a stale fixture\n    // into the NEXT world's array (and write a file the new world never\n    // asked for). Checked immediately before persistFixture with no await\n    // in between.\n    if (jobs.get(key) !== job) {\n      logger.warn(\n        `OpenRouter video capture for job ${job.upstreamJobId} discarded: the job map no longer holds this job (fixtures reset or TTL eviction mid-capture) — nothing persisted`,\n      );\n      return;\n    }\n    // A persist failure cannot ride an X-AIMock-Record-Error header here —\n    // the relay left before the capture started (the failed branch, which\n    // persists synchronously, still sets the header). persistFixture logs\n    // the failure; the replay mutation below still happens so the in-memory\n    // session serves the captured bytes.\n    persistFixture({\n      record,\n      providerKey: \"openrouter\",\n      testId,\n      fixture: { match: job.match, response: { video } },\n      fixtures,\n      warnings,\n      logger,\n    });\n\n    // Mutate the map entry into a terminal replay job: later polls and the\n    // content handler serve it locally from here on. An over-cap capture\n    // never keeps bytes in memory (the streamed read aborted at the cap),\n    // so the replayed content is the placeholder MP4. Identity-guarded: a\n    // TTL-evicted (or otherwise replaced) entry is never resurrected.\n    if (jobs.get(key) === job) {\n      jobs.set(key, {\n        kind: \"replay\",\n        jobId: job.jobId,\n        status: \"completed\",\n        pollCount: 0,\n        pollsBeforeInProgress: 0,\n        pollsBeforeCompleted: 0,\n        video: { ...video },\n      });\n    }\n  } catch (err) {\n    // Nothing may reject unhandled out of the detached capture. An\n    // unexpected throw (every expected failure is handled above) leaves the\n    // job a live proxy; a later completed poll retries the capture.\n    const msg = err instanceof Error ? err.message : String(err);\n    logger.error(\n      `OpenRouter video capture for job ${job.upstreamJobId} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n    );\n  } finally {\n    // Close the capturing window when the map still holds this record job\n    // (a capture step threw before the replay mutation) — a latched\n    // `capturing` would make every later poll early-relay and the job\n    // could never be captured. On the success path the entry was just\n    // replaced; the detached record object deliberately KEEPS\n    // capturing=true (see the function doc).\n    if (jobs.get(key) === job) {\n      job.capturing = false;\n    }\n  }\n}\n"],"mappings":"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"}