@copilotkit/aimock 1.41.0 → 1.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -1
- package/README.md +2 -1
- package/dist/a2a-mock.cjs +18 -11
- package/dist/a2a-mock.cjs.map +1 -1
- package/dist/a2a-mock.d.cts +1 -0
- package/dist/a2a-mock.d.cts.map +1 -1
- package/dist/a2a-mock.d.ts +1 -0
- package/dist/a2a-mock.d.ts.map +1 -1
- package/dist/a2a-mock.js +18 -11
- package/dist/a2a-mock.js.map +1 -1
- package/dist/agui-handler.d.ts.map +1 -1
- package/dist/agui-mock.cjs +2 -0
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +2 -0
- package/dist/agui-mock.js.map +1 -1
- package/dist/bedrock-converse.cjs +36 -2
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +37 -3
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +50 -13
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +51 -14
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +2 -2
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.d.cts.map +1 -1
- package/dist/byteplus-video.d.ts.map +1 -1
- package/dist/byteplus-video.js +2 -2
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +36 -7
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +8 -3
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +8 -3
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +36 -8
- package/dist/chaos.js.map +1 -1
- package/dist/cohere.cjs +58 -5
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +59 -6
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/elevenlabs-audio.cjs +36 -2
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.d.cts.map +1 -1
- package/dist/elevenlabs-audio.d.ts.map +1 -1
- package/dist/elevenlabs-audio.js +37 -3
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/embeddings.cjs +58 -3
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +59 -4
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +38 -2
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.d.cts.map +1 -1
- package/dist/fal-audio.d.ts.map +1 -1
- package/dist/fal-audio.js +39 -3
- package/dist/fal-audio.js.map +1 -1
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +9 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +9 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +19 -1
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +20 -2
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +15 -1
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +16 -2
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +70 -6
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +71 -7
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +2 -2
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.d.cts.map +1 -1
- package/dist/grok-video.d.ts.map +1 -1
- package/dist/grok-video.js +2 -2
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +160 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +152 -6
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +22 -4
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +23 -5
- package/dist/images.js.map +1 -1
- package/dist/llmock.cjs +15 -0
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +12 -0
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +12 -0
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +16 -1
- package/dist/llmock.js.map +1 -1
- package/dist/mcp-mock.cjs +11 -0
- package/dist/mcp-mock.cjs.map +1 -1
- package/dist/mcp-mock.d.cts.map +1 -1
- package/dist/mcp-mock.d.ts.map +1 -1
- package/dist/mcp-mock.js +11 -0
- package/dist/mcp-mock.js.map +1 -1
- package/dist/messages.cjs +18 -1
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +19 -2
- package/dist/messages.js.map +1 -1
- package/dist/moderation.cjs +67 -3
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts +1 -0
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts +1 -0
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +68 -4
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +75 -5
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +76 -6
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +4 -4
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.d.cts.map +1 -1
- package/dist/openrouter-video.d.ts.map +1 -1
- package/dist/openrouter-video.js +4 -4
- package/dist/openrouter-video.js.map +1 -1
- package/dist/rerank.cjs +65 -2
- package/dist/rerank.cjs.map +1 -1
- package/dist/rerank.d.cts +1 -1
- package/dist/rerank.d.cts.map +1 -1
- package/dist/rerank.d.ts +1 -1
- package/dist/rerank.d.ts.map +1 -1
- package/dist/rerank.js +66 -3
- package/dist/rerank.js.map +1 -1
- package/dist/responses.cjs +18 -1
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +19 -2
- package/dist/responses.js.map +1 -1
- package/dist/search.cjs +67 -3
- package/dist/search.cjs.map +1 -1
- package/dist/search.d.cts +1 -0
- package/dist/search.d.cts.map +1 -1
- package/dist/search.d.ts +1 -0
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +68 -4
- package/dist/search.js.map +1 -1
- package/dist/server.cjs +317 -13
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +320 -16
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +18 -1
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.d.cts.map +1 -1
- package/dist/speech.d.ts.map +1 -1
- package/dist/speech.js +19 -2
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +21 -2
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +21 -2
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +39 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +39 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/vector-mock.cjs +88 -0
- package/dist/vector-mock.cjs.map +1 -1
- package/dist/vector-mock.d.cts.map +1 -1
- package/dist/vector-mock.d.ts.map +1 -1
- package/dist/vector-mock.js +89 -1
- package/dist/vector-mock.js.map +1 -1
- package/dist/veo-video.cjs +2 -2
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.d.cts.map +1 -1
- package/dist/veo-video.d.ts.map +1 -1
- package/dist/veo-video.js +2 -2
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +19 -2
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +20 -3
- package/dist/video.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byteplus-video.cjs","names":["crypto","flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","serializeErrorResponse","isJSONResponse","resolveProgression","resolveUpstreamUrl","prepareEgressHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/byteplus-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, RecordConfig } from \"./types.js\";\nimport {\n isJSONResponse,\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 { 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 prepareEgressHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * BytePlus Ark (ModelArk / Seedance) async video task lifecycle mock. Submit\n * `POST /api/v3/contents/generations/tasks` returns `{ id }`; status\n * `GET /api/v3/contents/generations/tasks/{id}` polls\n * `queued → running → <recorded terminal status>`.\n *\n * THE FIXTURE FORMAT IS THE ARK TASK ENVELOPE ITSELF, stored as a\n * `RawJSONResponse` (`{ response: { json: … } }`) — NOT a `VideoResponse`. That\n * is the central decision of this surface and it is what lets `cancelled` and\n * `expired` (terminal states with no representation in `VideoResponse.status`)\n * record and replay natively, lets a recorded `error` object replay without\n * inventing a code, and lets `usage`, `framespersecond`, `draft_task_id` and any\n * field the vendor adds later survive because they were recorded rather than\n * enumerated.\n *\n * PROVENANCE OF EVERY WIRE FACT IN THIS FILE. One source for the TASK shapes —\n * create, poll, the `succeeded`/`failed` envelopes, the status tokens, the\n * unknown-task 404: the client library `@tanstack/ai-byteplus@0.3.4` — its\n * `wire-types.ts`, its request builders, and its author's note of live calls\n * made on 2026-07-31. That is a SECONDARY source. BytePlus's own documentation\n * is a client-rendered SPA and was never read. Exactly one layer is firsthand:\n * the keyless canary in `src/__tests__/drift/byteplus-video.drift.ts` does call\n * live Ark, and observed its AUTH-layer 401 error envelope on 2026-09-10 (see\n * the `OBSERVED_*` constants there). No SUCCESS or resource-layer response from\n * Ark has ever been observed here — the authenticated unknown-task canary is\n * still `skipIf`-gated for want of an `ARK_API_KEY` in repo secrets. So \"the\n * client library's six status tokens\" is a claim this repo can back, and \"the\n * vendor's documented six\" is not — comments below say the former. Where a fact is not even secondhand, it is named as unverified\n * rather than softened. A verification nobody performed is worse than an\n * admitted gap: it stops the next person from looking.\n *\n * The governing rule: THE MOCK NEVER AUTHORS A WIRE VALUE IT DID NOT OBSERVE.\n * On replay exactly three things are synthesized — the `id` rewrite, the\n * `queued`/`running` token on a NON-terminal poll, and withholding\n * `content`/`error`/`usage` until terminal. Nothing else. There is no default\n * error code, no default error message, no fabricated `usage`, and no\n * placeholder video url: a terminal fixture missing `content.video_url` gets a\n * named warn, not a substituted URL, because the consumer's error naming the\n * real problem beats a download of a URL aimock made up.\n *\n * `video.url` is served AS-IS — aimock does NOT proxy or capture video bytes.\n * Ark's `content.video_url` expires 24h after `updated_at`; replay warns once\n * when a fixture is served past that instant and still serves it.\n *\n * A fixture for this surface MUST carry `match.endpoint: \"video\"`. Without it\n * `router.ts`'s endpoint/response compatibility gate skips it (a `{ json }`\n * response is not a `VideoResponse`) before this handler ever sees it.\n */\n\n// ─── Wire constants ─────────────────────────────────────────────────────────\n\n/**\n * Ark's data plane lives under an `/api/v3` prefix ON THE ORIGIN, so\n * `record.providers.byteplus` is configured as an ORIGIN ONLY\n * (`https://ark.ap-southeast.bytepluses.com`) and this handler owns the whole\n * path — the same split `openrouter-video.ts` uses with `OPENROUTER_VIDEOS_PATH`.\n */\nexport const BYTEPLUS_VIDEO_TASKS_PATH = \"/api/v3/contents/generations/tasks\";\n\n/**\n * The two NON-terminal task states, per `@tanstack/ai-byteplus@0.3.4`'s\n * `wire-types.ts:38-44` — the client library's status union, not a BytePlus\n * document. This is the primitive: \"terminal\" is derived as everything else,\n * so a status token the client library does not list (whether BytePlus added\n * it or the library simply never had it) records instead of proxying forever.\n * See `isBytePlusTerminalStatus`.\n */\nconst BYTEPLUS_NON_TERMINAL_STATUSES = new Set([\"queued\", \"running\"]);\n\n/**\n * The six task states the client library declares. Anything outside this set is\n * a surprise — an authoring error, or a token the library does not cover.\n */\nconst BYTEPLUS_TASK_STATUSES = new Set([\n ...BYTEPLUS_NON_TERMINAL_STATUSES,\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/**\n * Terminal is the COMPLEMENT of the two non-terminal tokens, never a second\n * hardcoded list of the four terminal ones. An unrecognized status is treated\n * as terminal deliberately: a status absent from the client library's union is\n * far more likely to be an end state than an intermediate one, and guessing wrong\n * in that direction costs a captured fixture the operator can inspect, while\n * guessing wrong the other way costs a record run that polls until the client\n * times out and writes nothing.\n */\nfunction isBytePlusTerminalStatus(status: string): boolean {\n return !BYTEPLUS_NON_TERMINAL_STATUSES.has(status);\n}\n\n/** Ark output URLs expire 24h after the task produced them, anchored on `updated_at`. */\nconst BYTEPLUS_URL_TTL_SECONDS = 24 * 60 * 60;\n\n// ─── BytePlusVideoJobMap (TTL + bounded) ────────────────────────────────────\n\nexport const BYTEPLUS_VIDEO_MAX_ENTRIES = 10_000;\nconst BYTEPLUS_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\n/** Internal lifecycle position. The TERMINAL body comes from the envelope. */\ntype BytePlusVideoPhase = \"queued\" | \"running\" | \"terminal\";\n\n/** Latches so each authoring/staleness warn fires at most once per ENVELOPE. */\ninterface BytePlusWarnLatches {\n unknownStatus?: boolean;\n missingUrl?: boolean;\n expiredUrl?: boolean;\n nonTerminalEnvelope?: boolean;\n}\n\n/**\n * Warn latches keyed on the ENVELOPE OBJECT, not the job. A suite that replays\n * one aged fixture across a hundred tests mints a hundred jobs off the SAME\n * envelope object (the fixture's `response.json`), and a per-job latch would\n * emit a hundred identical lines — noise, not signal. The envelope is the thing\n * being complained about, so it owns the latch. WeakMap so a fixtures reset\n * drops the latches with the fixtures.\n */\nconst bytePlusWarnLatches = new WeakMap<Record<string, unknown>, BytePlusWarnLatches>();\n\nfunction warnLatchesFor(envelope: Record<string, unknown>): BytePlusWarnLatches {\n const existing = bytePlusWarnLatches.get(envelope);\n if (existing !== undefined) return existing;\n const fresh: BytePlusWarnLatches = {};\n bytePlusWarnLatches.set(envelope, fresh);\n return fresh;\n}\n\ninterface BytePlusVideoReplayJob {\n kind: \"replay\";\n id: string;\n phase: BytePlusVideoPhase;\n pollCount: number;\n pollsBeforeRunning: number;\n pollsBeforeTerminal: number;\n /** The recorded/authored Ark task envelope. Never mutated. */\n envelope: Record<string, unknown>;\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; on ANY terminal\n * status the envelope is captured as a fixture and the entry MUTATES into a\n * terminal replay job. All four terminal states are captured identically —\n * there is no warn-and-drop branch, which is the whole payoff of storing the\n * envelope rather than a `VideoResponse`.\n */\ninterface BytePlusVideoRecordJob {\n kind: \"record\";\n id: string;\n upstreamTaskId: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n}\n\nexport type BytePlusVideoJob = BytePlusVideoReplayJob | BytePlusVideoRecordJob;\n\ninterface BytePlusVideoEntry {\n job: BytePlusVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state. Mirrors GrokVideoJobMap / OpenRouterVideoJobMap: lazy\n * TTL eviction on `get`, FIFO eviction of the oldest entries on `set` when over\n * capacity, delete-before-set TTL refresh, monotonic world-generation counter\n * for reset-mid-flight detection, no background sweep timer.\n * Keys are `${testId}:${id}`.\n */\nexport class BytePlusVideoJobMap {\n private readonly entries = new Map<string, BytePlusVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): BytePlusVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > BYTEPLUS_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: BytePlusVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > BYTEPLUS_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - BYTEPLUS_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// ─── Match key ──────────────────────────────────────────────────────────────\n\ninterface BytePlusContentPart {\n type?: unknown;\n text?: unknown;\n image_url?: unknown;\n video_url?: unknown;\n audio_url?: unknown;\n role?: unknown;\n}\n\n/** The `{ url }` wrapper each media part carries, keyed by the part's `type`. */\nfunction mediaUrlOf(part: BytePlusContentPart): string | undefined {\n const holder =\n part.type === \"image_url\"\n ? part.image_url\n : part.type === \"video_url\"\n ? part.video_url\n : part.type === \"audio_url\"\n ? part.audio_url\n : undefined;\n if (holder === null || typeof holder !== \"object\") return undefined;\n const url = (holder as { url?: unknown }).url;\n return typeof url === \"string\" ? url : undefined;\n}\n\nfunction digest12(value: string): string {\n return crypto.createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 12);\n}\n\n/** Stable-enough serialisation of a part the media table could not resolve. */\nfunction unresolvedPartToken(raw: unknown): string {\n if (raw === null || typeof raw !== \"object\") return `part:${digest12(String(raw))}`;\n const part = raw as BytePlusContentPart;\n const role = typeof part.type === \"string\" && part.type ? part.type : \"part\";\n let serialised: string;\n try {\n serialised = JSON.stringify(raw) ?? String(raw);\n } catch {\n serialised = String(raw);\n }\n return `${role}:${digest12(serialised)}`;\n}\n\n/**\n * The synthetic user message a submit is matched on.\n *\n * Concatenated text of every `type: \"text\"` part, then ALWAYS\n * `[media: <role>:<sha256 first 12 hex>, …]` — one entry per non-text part in\n * array order, `[media: ]` when there are none. The separating newline is\n * present only when there IS text: a key with no text part is the bare\n * `[media: …]` suffix, unprefixed.\n *\n * WHY the digest: a Seedance image-to-video job carries NO text part, so a\n * text-only key would record `{ endpoint, model }` — a wildcard matching every\n * image-to-video job in the suite for that model. `persistFixture`'s\n * defective-fixture guard would not catch it either, because its `isEmptyMatch`\n * requires `endpoint` to be undefined too. Image-to-video, last-frame and\n * reference-media are first-class Seedance modes, so two recordings with the\n * same prompt and different first frames must stay distinguishable.\n *\n * WHY the marker is UNCONDITIONAL: this key lands in `match.userMessage`, which\n * the router SUBSTRING-matches by default (`useExactMatch = !!requestTransform`).\n * A conditional suffix makes the text-only key `\"a guitar\"` a prefix of the\n * image-to-video key `\"a guitar\\n[media: …]\"`, so a t2v recording silently\n * serves its own video_url to an i2v request that shares the prompt — a 200\n * with the wrong bytes. Emitting `[media: ]` for the text-only case makes\n * neither key a substring of the other. Prefixing would NOT fix it: the plain\n * text is still contained in the prefixed form.\n *\n * WHY every unrecognised part still contributes: `mediaUrlOf` resolves exactly\n * `image_url` / `video_url` / `audio_url` with a `{ url: string }` wrapper, and\n * the whole wire contract here is unverified against BytePlus. Dropping the\n * parts it cannot resolve reopened both failures above — an all-unrecognised\n * content array returned `\"\"` and minted the very wildcard this key exists to\n * prevent, and two different unrecognised parts collapsed onto one key. They\n * are digested over their own JSON instead, so they stay distinguishable\n * without the key ever carrying the part's payload.\n *\n * The digest is taken over the url string whether that is a public URL or a\n * multi-megabyte `data:` URI, and only 12 hex are kept — the match key stays\n * short and the fixture never grows a data URI.\n *\n * `role` is the declared role, else `first_frame` for an image (the default\n * `@tanstack/ai-byteplus@0.3.4` applies when a request omits the role — not a\n * BytePlus-documented default; nothing here was read from BytePlus), else the\n * part's `type` token, which is read off the request rather than drawn from any\n * role vocabulary.\n */\nexport function buildBytePlusMatchText(content: readonly unknown[]): string {\n const texts: string[] = [];\n const media: string[] = [];\n for (const raw of content) {\n const part = raw !== null && typeof raw === \"object\" ? (raw as BytePlusContentPart) : undefined;\n if (part?.type === \"text\") {\n if (typeof part.text === \"string\" && part.text) texts.push(part.text);\n continue;\n }\n const url = part === undefined ? undefined : mediaUrlOf(part);\n if (part === undefined || url === undefined) {\n media.push(unresolvedPartToken(raw));\n continue;\n }\n const role =\n typeof part.role === \"string\" && part.role\n ? part.role\n : part.type === \"image_url\"\n ? \"first_frame\"\n : String(part.type);\n media.push(`${role}:${digest12(url)}`);\n }\n const suffix = `[media: ${media.join(\", \")}]`;\n const text = texts.join(\"\\n\");\n return text ? `${text}\\n${suffix}` : suffix;\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Advance a replay job one poll. `queued → running → terminal` on poll-count\n * thresholds; no-op once terminal, which is what makes the extra post-terminal\n * poll the client library makes (getVideoStatus then getVideoUrl both hit this\n * endpoint) byte-identical to the one before it.\n */\nfunction advanceJob(job: BytePlusVideoReplayJob): void {\n if (job.phase === \"terminal\") return;\n job.pollCount += 1;\n if (job.phase === \"queued\" && job.pollCount >= job.pollsBeforeRunning) {\n job.phase = \"running\";\n }\n if (job.pollCount >= job.pollsBeforeTerminal) {\n job.phase = \"terminal\";\n }\n}\n\n// ─── Serialization ──────────────────────────────────────────────────────────\n\n/**\n * Ark's error envelope SHAPE, carrying a message only — never an invented code.\n *\n * KNOWN, DELIBERATE, UNRESOLVED DIVERGENCE. The live canary in\n * `src/__tests__/drift/byteplus-video.drift.ts` asserts that real Ark supplies a\n * string `error.code`, so a consumer that reads `error.code` off an aimock-\n * authored error (404 unknown task, 404 no fixture, 400 validation, 502 bad\n * envelope, 503 strict) sees `undefined` where live Ark would give it a token.\n *\n * We omit it rather than close the gap because we do not know the value FOR\n * THESE CLASSES. The canary is keyless and does run — it observed Ark's\n * AUTH-layer error body live on 2026-09-10 (`code: \"AuthenticationError\"`,\n * `type: \"Unauthorized\"`, `param: \"\"`; see the `OBSERVED_*` constants in the\n * drift file). None of the five error classes above is an auth error, so that\n * observation does not supply their codes, and `@tanstack/ai-byteplus` is not\n * installed here, so nobody has verified what the client does with a missing\n * `code` either. Minting one would put an aimock-authored token into a consumer's\n * assertions and into any fixture recorded past it, which is the failure this\n * module exists to avoid; the governing rule is that the mock never authors a\n * wire value it did not observe, and an honest absence beats a plausible\n * invention. Recorded fixtures are unaffected: a real `error.code` captured\n * from upstream replays verbatim.\n *\n * Also omitted, for the same reason and on the same terms: `type` and `param`.\n * Both ARE observed on the auth envelope, and neither is known for the five\n * classes above.\n *\n * To close it: observe a real Ark 404/400/5xx for each class (a real key is\n * needed to reach them), then carry the OBSERVED code per error class. Do not\n * guess one.\n */\nfunction arkErrorBody(message: string): string {\n return JSON.stringify({ error: { message } });\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/**\n * Build the poll body for a replay job.\n *\n * Non-terminal: `{ id, model?, status, created_at? }` — the status token is the\n * ONLY synthesized value, and `content`/`error`/`usage`/`updated_at` are\n * withheld. `created_at` is a property of the task and is true at every phase;\n * `updated_at` on a recorded envelope is the timestamp of the TERMINAL\n * transition, so emitting it mid-flight would assert a field COMBINATION this\n * design has never seen from Ark and cannot source from the client library\n * either (and a consumer diffing it across polls would see it constant).\n *\n * Terminal: the stored envelope VERBATIM with `id` rewritten. Its status is\n * whatever was recorded, including `cancelled` / `expired`.\n *\n * Returns `null` when the envelope cannot produce a poll body at all (not an\n * object, or no `status`) — an authoring error the caller answers with a 502.\n */\nexport function serializeBytePlusVideoTask(\n job: BytePlusVideoReplayJob,\n logger: HandlerDefaults[\"logger\"],\n): Record<string, unknown> | null {\n const envelope = job.envelope;\n const status = envelope.status;\n if (typeof status !== \"string\" || !status) return null;\n\n if (job.phase !== \"terminal\") {\n const body: Record<string, unknown> = { id: job.id, status: job.phase };\n if (envelope.model !== undefined) body.model = envelope.model;\n if (envelope.created_at !== undefined) body.created_at = envelope.created_at;\n return body;\n }\n\n // Terminal: recorded envelope verbatim, id stamped.\n const warned = warnLatchesFor(envelope);\n\n // The one authoring error UNIQUE to this surface. Every sibling video handler\n // SYNTHESIZES the terminal status, so a fixture cannot express \"terminal but\n // not terminal\"; storing the envelope verbatim makes it expressible for the\n // first time. The job has reached its terminal poll and will now serve this\n // same body forever, so the client polls to its own timeout with no\n // diagnostic: the status is one of the client library's six (unknownStatus stays\n // silent) and it is a non-empty string (the 502 authoring-error path stays\n // silent). Name it, the way every other authoring error here is named.\n if (!isBytePlusTerminalStatus(status) && !warned.nonTerminalEnvelope) {\n warned.nonTerminalEnvelope = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} has reached its terminal poll but its envelope ` +\n `carries the NON-TERMINAL status \"${status}\" — every later poll returns this same body, ` +\n `so the client will poll it forever and fail on its own timeout. Re-record the fixture, ` +\n `or author a terminal status (succeeded, failed, cancelled, expired)`,\n );\n }\n\n if (!BYTEPLUS_TASK_STATUSES.has(status) && !warned.unknownStatus) {\n warned.unknownStatus = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} carries status \"${status}\", which is not one of ` +\n `the six the BytePlus client library declares (queued, running, succeeded, failed, ` +\n `cancelled, expired) — serving it as authored, but that library's mapStatus() THROWS on ` +\n `a status it does not recognize`,\n );\n }\n\n const content = asRecord(envelope.content);\n const videoUrl = content?.video_url;\n if (status === \"succeeded\" && typeof videoUrl !== \"string\" && !warned.missingUrl) {\n warned.missingUrl = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} reports status \"succeeded\" but carries no ` +\n `content.video_url — the client will throw \"Video is not ready for download. Check ` +\n `status first.\" Add content.video_url to the fixture envelope`,\n );\n }\n\n // Gated on the SAME two facts the missing-url warn above is gated on: the\n // 24h TTL is a property of a succeeded task's OUTPUT url, so without a\n // `succeeded` status AND a url actually present there is nothing that can\n // have expired. Ungated, this told the operator that a `failed` /\n // `cancelled` / `expired` envelope — which carries no `content` at all —\n // \"has a content.video_url that expired\", a wire fact that is not true.\n const updatedAt = envelope.updated_at;\n if (\n status === \"succeeded\" &&\n typeof videoUrl === \"string\" &&\n typeof updatedAt === \"number\" &&\n Number.isFinite(updatedAt) &&\n !warned.expiredUrl\n ) {\n const expiresAt = updatedAt + BYTEPLUS_URL_TTL_SECONDS;\n if (expiresAt * 1000 < Date.now()) {\n warned.expiredUrl = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} has a content.video_url that expired at ` +\n `${new Date(expiresAt * 1000).toISOString()} (Ark output urls live 24h from ` +\n `updated_at) — serving it anyway; re-record the fixture to get a live url`,\n );\n }\n }\n\n return { ...envelope, id: job.id };\n}\n\n// ─── Request validation ─────────────────────────────────────────────────────\n\ninterface BytePlusVideoRequest {\n model?: unknown;\n content?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Structurally valid journal body for a field-validation 400. Mirrors\n * grok-video / openrouter-video: `model` stays a string and `messages` is an\n * empty array; underscore-prefixed keys are stripped so a request cannot spoof\n * handler-set discriminators.\n */\nfunction validationJournalBody(videoReq: BytePlusVideoRequest): ChatCompletionRequest {\n const rawModel = videoReq.model;\n const model =\n typeof rawModel === \"string\"\n ? rawModel\n : rawModel === undefined\n ? \"\"\n : JSON.stringify(rawModel);\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n return { ...sanitized, model, messages: [] };\n}\n\n// ─── POST /api/v3/contents/generations/tasks — submit ───────────────────────\n\nexport async function handleBytePlusVideoCreate(\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: BytePlusVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? BYTEPLUS_VIDEO_TASKS_PATH;\n const method = req.method ?? \"POST\";\n\n const fail = (status: number, message: string, body: ChatCompletionRequest | null): void => {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body,\n response: { status, fixture: null },\n });\n writeErrorResponse(res, status, arkErrorBody(message));\n };\n\n let videoReq: BytePlusVideoRequest;\n try {\n videoReq = JSON.parse(raw) as BytePlusVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n fail(400, `Malformed JSON: ${detail}`, null);\n return;\n }\n\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n fail(400, \"Request body must be a JSON object\", null);\n return;\n }\n\n const parsedBody = validationJournalBody(videoReq);\n\n // `model` is REQUIRED, never defaulted: the client library always sends it\n // (whether Ark itself rejects a request without it is unverified here), and a\n // defaulted model id would be both a value aimock never observed and a silent\n // mis-key of the fixture match. Rejecting is the safe side of that gap.\n if (typeof videoReq.model !== \"string\" || !videoReq.model) {\n fail(\n 400,\n videoReq.model === undefined\n ? \"Missing required parameter: 'model'\"\n : \"Invalid type for parameter: 'model' must be a non-empty string\",\n parsedBody,\n );\n return;\n }\n\n if (!Array.isArray(videoReq.content) || videoReq.content.length === 0) {\n fail(\n 400,\n videoReq.content === undefined\n ? \"Missing required parameter: 'content'\"\n : \"Invalid type for parameter: 'content' must be a non-empty array\",\n parsedBody,\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model,\n messages: [{ role: \"user\", content: buildBytePlusMatchText(videoReq.content) }],\n _endpointType: \"video\",\n _videoProvider: \"byteplus\",\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 journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n }\n\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 fixture\n ? \"fixture\"\n : resolveStrictMode(defaults.strict, req.headers)\n ? \"internal\"\n : defaults.record?.providers.byteplus\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!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(res, 503, arkErrorBody(strictMessage));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyBytePlusVideoSubmit({\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 // \"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 // Name the endpoint requirement: router.ts skips a `{ json }` fixture that\n // omits `match.endpoint`, so the commonest authoring mistake on this\n // surface presents as a bare no-match with nothing pointing at the cause.\n writeErrorResponse(\n res,\n 404,\n arkErrorBody(\n 'No byteplus fixture matched. A byteplus video fixture must carry match.endpoint: \"video\" ' +\n \"and a { json: <Ark task envelope> } response\",\n ),\n );\n return;\n }\n\n const worldGeneration = jobs.generation;\n const response = await resolveResponse(fixture, syntheticReq);\n\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 (!isJSONResponse(response)) {\n // A VideoResponse-shaped fixture is the other likely mistake: it is what\n // every OTHER video provider in this repo takes. Say so rather than\n // half-building a body out of it.\n if (res.destroyed || res.writableEnded) return;\n defaults.logger.warn(\n \"BytePlus video fixture matched but its response is not a { json: … } payload — this \" +\n \"surface stores the Ark task envelope verbatim, NOT a VideoResponse. Treating it as a \" +\n \"no-match\",\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n arkErrorBody(\n \"Matched byteplus fixture has a non-JSON response; expected { json: <Ark task envelope> }\",\n ),\n );\n return;\n }\n\n const envelope = asRecord(response.json);\n if (!envelope) {\n if (res.destroyed || res.writableEnded) return;\n defaults.logger.warn(\n \"BytePlus video fixture's json payload is not a JSON object — expected an Ark task envelope\",\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(res, 404, arkErrorBody(\"Matched byteplus fixture's json is not an object\"));\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 // The `cgt-` prefix is deliberate: it is the task-id prefix documented in\n // `@tanstack/ai-byteplus@0.3.4` (`src/video/wire-types.ts`, from that author's\n // own live calls — a SECONDARY source; this repo has not observed a real Ark\n // task id), so a consumer that pattern-matches the id cannot tell replay from\n // live.\n const id = `cgt-${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.bytePlusVideo);\n const job: BytePlusVideoReplayJob = {\n kind: \"replay\",\n id,\n phase: \"queued\",\n pollCount: 0,\n pollsBeforeRunning: progression.pollsBeforeInProgress,\n pollsBeforeTerminal: progression.pollsBeforeCompleted,\n envelope,\n };\n if (progression.pollsBeforeCompleted === 0) job.phase = \"terminal\";\n\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${id}`, job);\n } else {\n defaults.logger.warn(\n `BytePlus video submit resolved after a fixtures reset — not inserting job ${id} into the ` +\n `new world (its polls will 404)`,\n );\n }\n\n // NO testIdSuffix: the client encodeURIComponent()s the job id into the poll\n // path, so a `?testId=` suffix would be percent-encoded into the path segment\n // instead of surviving as a query string. A non-default testId must ride the\n // `x-test-id` header, which the adapter forwards via defaultHeaders.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id }));\n}\n\n// ─── GET /api/v3/contents/generations/tasks/{id} — status poll ──────────────\n\nexport async function handleBytePlusVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n id: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: BytePlusVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `${BYTEPLUS_VIDEO_TASKS_PATH}/${id}`;\n const method = req.method ?? \"GET\";\n const testId = getTestId(req);\n const key = `${testId}:${id}`;\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 const job = jobs.get(key);\n if (!job) {\n // Ark's error envelope SHAPE, with NO `code`. What Ark actually returns for\n // an aged-out task is UNVERIFIED: the client library reads a dotted\n // `error.code` off an open-ended vocabulary and splices it into the\n // consumer-visible error string, and the drift canary asserts a string\n // `code`. That canary does run, but what it observed live is Ark's AUTH\n // envelope, not a 404 for an unknown task — so this class's code remains\n // unobserved. Minting a code to fill the gap would put an aimock-authored\n // token into a consumer's assertion; see `arkErrorBody`.\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 arkErrorBody(\n `Unknown video task id \"${id}\" — no such job for this test scope. It was never created, ` +\n `it belongs to a different x-test-id, or it aged out of aimock's 1h job TTL`,\n ),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: video task ${id} 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 arkErrorBody(\n `Strict mode: video task ${id} is proxied live upstream (record mode) — nothing reaches ` +\n `an upstream under strict mode`,\n ),\n );\n return;\n }\n await proxyBytePlusVideoRecordPoll({\n req,\n res,\n job,\n key,\n testId,\n fixtures,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n advanceJob(job);\n jobs.set(key, job);\n\n const body = serializeBytePlusVideoTask(job, defaults.logger);\n if (!body) {\n defaults.logger.warn(\n `BytePlus video fixture for job ${id} has an envelope that is not a usable Ark task body ` +\n `(missing or non-string \"status\") — a fixture that cannot produce a poll body is an ` +\n `authoring error`,\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 502, fixture: null },\n });\n writeErrorResponse(\n res,\n 502,\n arkErrorBody(\n `Fixture envelope for video task ${id} carries no \"status\" — expected an Ark task envelope`,\n ),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\n// ─── Record mode: live interactive proxy (submit) ───────────────────────────\n\n/**\n * Proxy an unmatched submit to the configured upstream and answer the client\n * with a mock-rewritten `{ id }`. The upstream lifecycle is then driven\n * interactively by the client's own polls. Returns \"no_upstream\" when record\n * mode has no byteplus provider URL — the caller falls through to 404.\n */\nasync function proxyBytePlusVideoSubmit(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: BytePlusVideoJobMap;\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.byteplus;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"byteplus\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`BytePlus video submit proxy failed: ${msg}`);\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(res, 502, arkErrorBody(`Proxy to upstream failed: ${msg}`));\n return \"handled\";\n };\n\n // The configured base is an ORIGIN; this handler owns the whole path,\n // including the /api/v3 prefix.\n let submitUrl: URL;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, BYTEPLUS_VIDEO_TASKS_PATH);\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(`NO FIXTURE MATCH — proxying video submit to ${submitUrl.toString()}`);\n\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"byteplus\", record.providerKeys?.byteplus);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\n // Raw body, verbatim: Ark rejects an inapplicable option outright rather\n // than ignoring it, so every model-specific field must arrive untouched.\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 return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\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, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return \"handled\";\n }\n\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n\n let upstreamTaskId: string;\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 const envelope = asRecord(parsed);\n if (!envelope) return proxyError(\"Submit response is not a JSON object\");\n upstreamTaskId = String(envelope.id ?? \"\").trim();\n if (!upstreamTaskId) return proxyError(\"Submit response missing id\");\n }\n\n const upstreamPollingUrl = resolveUpstreamUrl(\n upstreamBase,\n `${BYTEPLUS_VIDEO_TASKS_PATH}/${encodeURIComponent(upstreamTaskId)}`,\n ).toString();\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const id = `cgt-${crypto.randomUUID()}`;\n const job: BytePlusVideoRecordJob = {\n kind: \"record\",\n id,\n upstreamTaskId,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${id}`, job);\n } else {\n defaults.logger.warn(\n `BytePlus video submit for upstream task ${upstreamTaskId} completed after a fixtures ` +\n `reset — not inserting the job into the new world (its polls will 404)`,\n );\n }\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: 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (status poll + eager capture) ──────\n\n/**\n * Proxy a status poll 1:1 and relay the result with the mock id substituted.\n *\n * An upstream 4xx is RELAYED VERBATIM rather than converted to 502 — a 404 in\n * particular, because the client has a deliberate 404 branch that keeps Ark's\n * own code/message on purpose, and converting it would make record mode behave\n * differently from live Ark for exactly the case that branch exists to serve.\n * 5xx and transport failures become 502.\n *\n * On ANY terminal status the rewritten body is relayed immediately and the\n * envelope is captured. All four terminal states are captured identically:\n * there is no warn-and-drop branch, so a task cancelled from the Ark console\n * mid-record yields an artifact rather than a warning and an empty directory.\n */\nasync function proxyBytePlusVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: BytePlusVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: BytePlusVideoJobMap;\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(`BytePlus video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(res, 502, arkErrorBody(`Proxy to upstream failed: ${msg}`));\n };\n\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 target = new URL(job.upstreamPollingUrl);\n const headers = prepareEgressHeaders(req, target, \"byteplus\", record.providerKeys?.byteplus);\n if (!headers) {\n proxyError(\"No configured provider credential\");\n return;\n }\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers,\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 // Relay EVERY 4xx verbatim (see the doc-comment): the client's 404 branch is\n // deliberate, and a 401/403 is a credential fact the operator must see.\n if (fetched.status >= 400 && fetched.status < 500) {\n logger.warn(\n `Upstream returned ${fetched.status} for video task ${job.upstreamTaskId} — relaying it verbatim`,\n );\n if (res.destroyed || res.writableEnded) return;\n journalProxy(fetched.status);\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return;\n }\n\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n\n let upstreamBody: Record<string, unknown>;\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 const envelope = asRecord(parsed);\n if (!envelope) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = envelope;\n }\n\n // The mock id is the ONLY rewrite; every other field passes through verbatim,\n // which is what makes this path robust to vendor additions.\n const relayBody: Record<string, unknown> = { ...upstreamBody, id: job.id };\n\n const relayJson = (body: Record<string, unknown>): void => {\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 // Terminal is derived from the NON-terminal pair, not from a second copy of\n // the four terminal tokens: a vendor-added end state is captured rather than\n // proxied until the client gives up.\n //\n // The status must FIRST clear what replay demands of it — a non-empty string,\n // the exact predicate `serializeBytePlusVideoTask` applies. Coercing instead\n // (`String(status ?? \"\")`) turned an absent status into `\"\"` and a numeric\n // `123` into `\"123\"`, and since terminal is the COMPLEMENT of the non-terminal\n // pair, both read as terminal. Capture then wrote an envelope replay cannot\n // serve and swapped the job to replay, so every later poll 502s on that same\n // envelope and a corrected upstream response is never fetched for the job —\n // and the unusable fixture is on disk. Treating it as NOT terminal relays it\n // verbatim and leaves the job upstream-backed, which is also what lets a\n // later well-formed response record normally.\n //\n // Scope: this is malformed-upstream robustness only. Ark is not known to emit\n // such a body, and non-2xx / non-JSON / non-object bodies are rejected above.\n const rawStatus = upstreamBody.status;\n const upstreamStatus = typeof rawStatus === \"string\" ? rawStatus : \"\";\n\n if (!upstreamStatus || !isBytePlusTerminalStatus(upstreamStatus)) {\n if (jobs.get(key) === job) jobs.set(key, job); // TTL refresh\n relayJson(relayBody);\n return;\n }\n\n if (record.proxyOnly) {\n if (jobs.get(key) === job) jobs.set(key, job); // TTL refresh\n relayJson(relayBody);\n return;\n }\n\n // THE capture gate, and the only one — map identity, checked once, here.\n //\n // It does two jobs at once. (a) It serializes two concurrent terminal polls:\n // both awaited upstream, and whichever resumes second finds that the first\n // already swapped the replay job into the map, so it relays without\n // capturing again. (b) It is the world-generation guard: a fixtures reset\n // clears the job map, so a job the map no longer holds belongs to a world\n // that is gone, and persisting would push a stale fixture into the NEXT\n // world's array. Either way the right answer is the same — relay what\n // upstream said, persist nothing, say nothing.\n //\n // There is NO await between here and the persist below, which is why one\n // check suffices. Two things previously sat on this path and could not fire:\n // a `job.capturing` flag (set and cleared inside a single tick, so never\n // observable by another poll) and a second copy of this identity check\n // inside the capture body, whose warning named a fixtures reset that this\n // check had already ruled out one statement earlier. Both are gone. If the\n // capture is ever made asynchronous, re-check identity immediately before\n // `persistFixture` — that is the point where it would start to matter.\n if (jobs.get(key) !== job) {\n relayJson(relayBody);\n return;\n }\n\n // Capture BEFORE relaying, so a persist failure can still ride an\n // X-AIMock-Record-Error header on this response — grok-video.ts:1105's\n // ordering. Nothing here downloads bytes: the capture is a synchronous\n // persist plus a map swap, so relaying after it costs the client one\n // filesystem write, and openrouter-video.ts:2521's \"the relay left before the\n // capture started\" constraint (which forced it to drop the header) does not\n // apply.\n captureBytePlusVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n res,\n });\n\n relayJson(relayBody);\n}\n\n/**\n * Capture a terminal record job into a fixture (PERSIST only — NO byte\n * download) and mutate the map entry into a terminal replay job.\n *\n * Runs BEFORE the caller relays, and is fully synchronous, so a persist failure\n * still reaches the client as an `X-AIMock-Record-Error` header rather than\n * only as a log line behind a 200 the client cannot distinguish from a clean\n * record.\n *\n * The upstream `id` is REMOVED at capture: replay stamps its own, and keeping a\n * real Ark task id in a committed artifact leaks it for no benefit.\n */\nfunction captureBytePlusVideoRecordFixture(args: {\n job: BytePlusVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: BytePlusVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n res: http.ServerResponse;\n}): void {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody, res } = args;\n const logger = defaults.logger;\n\n try {\n const { id: _discardedUpstreamId, ...envelope } = upstreamBody;\n void _discardedUpstreamId;\n\n const persistResult = persistFixture({\n record,\n providerKey: \"byteplus\",\n testId,\n fixture: { match: job.match, response: { json: envelope } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n\n jobs.set(key, {\n kind: \"replay\",\n id: job.id,\n phase: \"terminal\",\n pollCount: 0,\n pollsBeforeRunning: 0,\n pollsBeforeTerminal: 0,\n envelope,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `BytePlus video capture for task ${job.upstreamTaskId} failed unexpectedly (${msg}) — ` +\n `fixture not persisted; the job keeps proxying live`,\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"byteplus-video.cjs","names":["crypto","flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","serializeErrorResponse","isJSONResponse","resolveProgression","resolveUpstreamUrl","prepareEgressHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/byteplus-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, RecordConfig } from \"./types.js\";\nimport {\n isJSONResponse,\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 { 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 prepareEgressHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * BytePlus Ark (ModelArk / Seedance) async video task lifecycle mock. Submit\n * `POST /api/v3/contents/generations/tasks` returns `{ id }`; status\n * `GET /api/v3/contents/generations/tasks/{id}` polls\n * `queued → running → <recorded terminal status>`.\n *\n * THE FIXTURE FORMAT IS THE ARK TASK ENVELOPE ITSELF, stored as a\n * `RawJSONResponse` (`{ response: { json: … } }`) — NOT a `VideoResponse`. That\n * is the central decision of this surface and it is what lets `cancelled` and\n * `expired` (terminal states with no representation in `VideoResponse.status`)\n * record and replay natively, lets a recorded `error` object replay without\n * inventing a code, and lets `usage`, `framespersecond`, `draft_task_id` and any\n * field the vendor adds later survive because they were recorded rather than\n * enumerated.\n *\n * PROVENANCE OF EVERY WIRE FACT IN THIS FILE. One source for the TASK shapes —\n * create, poll, the `succeeded`/`failed` envelopes, the status tokens, the\n * unknown-task 404: the client library `@tanstack/ai-byteplus@0.3.4` — its\n * `wire-types.ts`, its request builders, and its author's note of live calls\n * made on 2026-07-31. That is a SECONDARY source. BytePlus's own documentation\n * is a client-rendered SPA and was never read. Exactly one layer is firsthand:\n * the keyless canary in `src/__tests__/drift/byteplus-video.drift.ts` does call\n * live Ark, and observed its AUTH-layer 401 error envelope on 2026-09-10 (see\n * the `OBSERVED_*` constants there). No SUCCESS or resource-layer response from\n * Ark has ever been observed here — the authenticated unknown-task canary is\n * still `skipIf`-gated for want of an `ARK_API_KEY` in repo secrets. So \"the\n * client library's six status tokens\" is a claim this repo can back, and \"the\n * vendor's documented six\" is not — comments below say the former. Where a fact is not even secondhand, it is named as unverified\n * rather than softened. A verification nobody performed is worse than an\n * admitted gap: it stops the next person from looking.\n *\n * The governing rule: THE MOCK NEVER AUTHORS A WIRE VALUE IT DID NOT OBSERVE.\n * On replay exactly three things are synthesized — the `id` rewrite, the\n * `queued`/`running` token on a NON-terminal poll, and withholding\n * `content`/`error`/`usage` until terminal. Nothing else. There is no default\n * error code, no default error message, no fabricated `usage`, and no\n * placeholder video url: a terminal fixture missing `content.video_url` gets a\n * named warn, not a substituted URL, because the consumer's error naming the\n * real problem beats a download of a URL aimock made up.\n *\n * `video.url` is served AS-IS — aimock does NOT proxy or capture video bytes.\n * Ark's `content.video_url` expires 24h after `updated_at`; replay warns once\n * when a fixture is served past that instant and still serves it.\n *\n * A fixture for this surface MUST carry `match.endpoint: \"video\"`. Without it\n * `router.ts`'s endpoint/response compatibility gate skips it (a `{ json }`\n * response is not a `VideoResponse`) before this handler ever sees it.\n */\n\n// ─── Wire constants ─────────────────────────────────────────────────────────\n\n/**\n * Ark's data plane lives under an `/api/v3` prefix ON THE ORIGIN, so\n * `record.providers.byteplus` is configured as an ORIGIN ONLY\n * (`https://ark.ap-southeast.bytepluses.com`) and this handler owns the whole\n * path — the same split `openrouter-video.ts` uses with `OPENROUTER_VIDEOS_PATH`.\n */\nexport const BYTEPLUS_VIDEO_TASKS_PATH = \"/api/v3/contents/generations/tasks\";\n\n/**\n * The two NON-terminal task states, per `@tanstack/ai-byteplus@0.3.4`'s\n * `wire-types.ts:38-44` — the client library's status union, not a BytePlus\n * document. This is the primitive: \"terminal\" is derived as everything else,\n * so a status token the client library does not list (whether BytePlus added\n * it or the library simply never had it) records instead of proxying forever.\n * See `isBytePlusTerminalStatus`.\n */\nconst BYTEPLUS_NON_TERMINAL_STATUSES = new Set([\"queued\", \"running\"]);\n\n/**\n * The six task states the client library declares. Anything outside this set is\n * a surprise — an authoring error, or a token the library does not cover.\n */\nconst BYTEPLUS_TASK_STATUSES = new Set([\n ...BYTEPLUS_NON_TERMINAL_STATUSES,\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/**\n * Terminal is the COMPLEMENT of the two non-terminal tokens, never a second\n * hardcoded list of the four terminal ones. An unrecognized status is treated\n * as terminal deliberately: a status absent from the client library's union is\n * far more likely to be an end state than an intermediate one, and guessing wrong\n * in that direction costs a captured fixture the operator can inspect, while\n * guessing wrong the other way costs a record run that polls until the client\n * times out and writes nothing.\n */\nfunction isBytePlusTerminalStatus(status: string): boolean {\n return !BYTEPLUS_NON_TERMINAL_STATUSES.has(status);\n}\n\n/** Ark output URLs expire 24h after the task produced them, anchored on `updated_at`. */\nconst BYTEPLUS_URL_TTL_SECONDS = 24 * 60 * 60;\n\n// ─── BytePlusVideoJobMap (TTL + bounded) ────────────────────────────────────\n\nexport const BYTEPLUS_VIDEO_MAX_ENTRIES = 10_000;\nconst BYTEPLUS_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\n/** Internal lifecycle position. The TERMINAL body comes from the envelope. */\ntype BytePlusVideoPhase = \"queued\" | \"running\" | \"terminal\";\n\n/** Latches so each authoring/staleness warn fires at most once per ENVELOPE. */\ninterface BytePlusWarnLatches {\n unknownStatus?: boolean;\n missingUrl?: boolean;\n expiredUrl?: boolean;\n nonTerminalEnvelope?: boolean;\n}\n\n/**\n * Warn latches keyed on the ENVELOPE OBJECT, not the job. A suite that replays\n * one aged fixture across a hundred tests mints a hundred jobs off the SAME\n * envelope object (the fixture's `response.json`), and a per-job latch would\n * emit a hundred identical lines — noise, not signal. The envelope is the thing\n * being complained about, so it owns the latch. WeakMap so a fixtures reset\n * drops the latches with the fixtures.\n */\nconst bytePlusWarnLatches = new WeakMap<Record<string, unknown>, BytePlusWarnLatches>();\n\nfunction warnLatchesFor(envelope: Record<string, unknown>): BytePlusWarnLatches {\n const existing = bytePlusWarnLatches.get(envelope);\n if (existing !== undefined) return existing;\n const fresh: BytePlusWarnLatches = {};\n bytePlusWarnLatches.set(envelope, fresh);\n return fresh;\n}\n\ninterface BytePlusVideoReplayJob {\n kind: \"replay\";\n id: string;\n phase: BytePlusVideoPhase;\n pollCount: number;\n pollsBeforeRunning: number;\n pollsBeforeTerminal: number;\n /** The recorded/authored Ark task envelope. Never mutated. */\n envelope: Record<string, unknown>;\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; on ANY terminal\n * status the envelope is captured as a fixture and the entry MUTATES into a\n * terminal replay job. All four terminal states are captured identically —\n * there is no warn-and-drop branch, which is the whole payoff of storing the\n * envelope rather than a `VideoResponse`.\n */\ninterface BytePlusVideoRecordJob {\n kind: \"record\";\n id: string;\n upstreamTaskId: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n}\n\nexport type BytePlusVideoJob = BytePlusVideoReplayJob | BytePlusVideoRecordJob;\n\ninterface BytePlusVideoEntry {\n job: BytePlusVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state. Mirrors GrokVideoJobMap / OpenRouterVideoJobMap: lazy\n * TTL eviction on `get`, FIFO eviction of the oldest entries on `set` when over\n * capacity, delete-before-set TTL refresh, monotonic world-generation counter\n * for reset-mid-flight detection, no background sweep timer.\n * Keys are `${testId}:${id}`.\n */\nexport class BytePlusVideoJobMap {\n private readonly entries = new Map<string, BytePlusVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): BytePlusVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > BYTEPLUS_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: BytePlusVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > BYTEPLUS_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - BYTEPLUS_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// ─── Match key ──────────────────────────────────────────────────────────────\n\ninterface BytePlusContentPart {\n type?: unknown;\n text?: unknown;\n image_url?: unknown;\n video_url?: unknown;\n audio_url?: unknown;\n role?: unknown;\n}\n\n/** The `{ url }` wrapper each media part carries, keyed by the part's `type`. */\nfunction mediaUrlOf(part: BytePlusContentPart): string | undefined {\n const holder =\n part.type === \"image_url\"\n ? part.image_url\n : part.type === \"video_url\"\n ? part.video_url\n : part.type === \"audio_url\"\n ? part.audio_url\n : undefined;\n if (holder === null || typeof holder !== \"object\") return undefined;\n const url = (holder as { url?: unknown }).url;\n return typeof url === \"string\" ? url : undefined;\n}\n\nfunction digest12(value: string): string {\n return crypto.createHash(\"sha256\").update(value).digest(\"hex\").slice(0, 12);\n}\n\n/** Stable-enough serialisation of a part the media table could not resolve. */\nfunction unresolvedPartToken(raw: unknown): string {\n if (raw === null || typeof raw !== \"object\") return `part:${digest12(String(raw))}`;\n const part = raw as BytePlusContentPart;\n const role = typeof part.type === \"string\" && part.type ? part.type : \"part\";\n let serialised: string;\n try {\n serialised = JSON.stringify(raw) ?? String(raw);\n } catch {\n serialised = String(raw);\n }\n return `${role}:${digest12(serialised)}`;\n}\n\n/**\n * The synthetic user message a submit is matched on.\n *\n * Concatenated text of every `type: \"text\"` part, then ALWAYS\n * `[media: <role>:<sha256 first 12 hex>, …]` — one entry per non-text part in\n * array order, `[media: ]` when there are none. The separating newline is\n * present only when there IS text: a key with no text part is the bare\n * `[media: …]` suffix, unprefixed.\n *\n * WHY the digest: a Seedance image-to-video job carries NO text part, so a\n * text-only key would record `{ endpoint, model }` — a wildcard matching every\n * image-to-video job in the suite for that model. `persistFixture`'s\n * defective-fixture guard would not catch it either, because its `isEmptyMatch`\n * requires `endpoint` to be undefined too. Image-to-video, last-frame and\n * reference-media are first-class Seedance modes, so two recordings with the\n * same prompt and different first frames must stay distinguishable.\n *\n * WHY the marker is UNCONDITIONAL: this key lands in `match.userMessage`, which\n * the router SUBSTRING-matches by default (`useExactMatch = !!requestTransform`).\n * A conditional suffix makes the text-only key `\"a guitar\"` a prefix of the\n * image-to-video key `\"a guitar\\n[media: …]\"`, so a t2v recording silently\n * serves its own video_url to an i2v request that shares the prompt — a 200\n * with the wrong bytes. Emitting `[media: ]` for the text-only case makes\n * neither key a substring of the other. Prefixing would NOT fix it: the plain\n * text is still contained in the prefixed form.\n *\n * WHY every unrecognised part still contributes: `mediaUrlOf` resolves exactly\n * `image_url` / `video_url` / `audio_url` with a `{ url: string }` wrapper, and\n * the whole wire contract here is unverified against BytePlus. Dropping the\n * parts it cannot resolve reopened both failures above — an all-unrecognised\n * content array returned `\"\"` and minted the very wildcard this key exists to\n * prevent, and two different unrecognised parts collapsed onto one key. They\n * are digested over their own JSON instead, so they stay distinguishable\n * without the key ever carrying the part's payload.\n *\n * The digest is taken over the url string whether that is a public URL or a\n * multi-megabyte `data:` URI, and only 12 hex are kept — the match key stays\n * short and the fixture never grows a data URI.\n *\n * `role` is the declared role, else `first_frame` for an image (the default\n * `@tanstack/ai-byteplus@0.3.4` applies when a request omits the role — not a\n * BytePlus-documented default; nothing here was read from BytePlus), else the\n * part's `type` token, which is read off the request rather than drawn from any\n * role vocabulary.\n */\nexport function buildBytePlusMatchText(content: readonly unknown[]): string {\n const texts: string[] = [];\n const media: string[] = [];\n for (const raw of content) {\n const part = raw !== null && typeof raw === \"object\" ? (raw as BytePlusContentPart) : undefined;\n if (part?.type === \"text\") {\n if (typeof part.text === \"string\" && part.text) texts.push(part.text);\n continue;\n }\n const url = part === undefined ? undefined : mediaUrlOf(part);\n if (part === undefined || url === undefined) {\n media.push(unresolvedPartToken(raw));\n continue;\n }\n const role =\n typeof part.role === \"string\" && part.role\n ? part.role\n : part.type === \"image_url\"\n ? \"first_frame\"\n : String(part.type);\n media.push(`${role}:${digest12(url)}`);\n }\n const suffix = `[media: ${media.join(\", \")}]`;\n const text = texts.join(\"\\n\");\n return text ? `${text}\\n${suffix}` : suffix;\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Advance a replay job one poll. `queued → running → terminal` on poll-count\n * thresholds; no-op once terminal, which is what makes the extra post-terminal\n * poll the client library makes (getVideoStatus then getVideoUrl both hit this\n * endpoint) byte-identical to the one before it.\n */\nfunction advanceJob(job: BytePlusVideoReplayJob): void {\n if (job.phase === \"terminal\") return;\n job.pollCount += 1;\n if (job.phase === \"queued\" && job.pollCount >= job.pollsBeforeRunning) {\n job.phase = \"running\";\n }\n if (job.pollCount >= job.pollsBeforeTerminal) {\n job.phase = \"terminal\";\n }\n}\n\n// ─── Serialization ──────────────────────────────────────────────────────────\n\n/**\n * Ark's error envelope SHAPE, carrying a message only — never an invented code.\n *\n * KNOWN, DELIBERATE, UNRESOLVED DIVERGENCE. The live canary in\n * `src/__tests__/drift/byteplus-video.drift.ts` asserts that real Ark supplies a\n * string `error.code`, so a consumer that reads `error.code` off an aimock-\n * authored error (404 unknown task, 404 no fixture, 400 validation, 502 bad\n * envelope, 503 strict) sees `undefined` where live Ark would give it a token.\n *\n * We omit it rather than close the gap because we do not know the value FOR\n * THESE CLASSES. The canary is keyless and does run — it observed Ark's\n * AUTH-layer error body live on 2026-09-10 (`code: \"AuthenticationError\"`,\n * `type: \"Unauthorized\"`, `param: \"\"`; see the `OBSERVED_*` constants in the\n * drift file). None of the five error classes above is an auth error, so that\n * observation does not supply their codes, and `@tanstack/ai-byteplus` is not\n * installed here, so nobody has verified what the client does with a missing\n * `code` either. Minting one would put an aimock-authored token into a consumer's\n * assertions and into any fixture recorded past it, which is the failure this\n * module exists to avoid; the governing rule is that the mock never authors a\n * wire value it did not observe, and an honest absence beats a plausible\n * invention. Recorded fixtures are unaffected: a real `error.code` captured\n * from upstream replays verbatim.\n *\n * Also omitted, for the same reason and on the same terms: `type` and `param`.\n * Both ARE observed on the auth envelope, and neither is known for the five\n * classes above.\n *\n * To close it: observe a real Ark 404/400/5xx for each class (a real key is\n * needed to reach them), then carry the OBSERVED code per error class. Do not\n * guess one.\n */\nfunction arkErrorBody(message: string): string {\n return JSON.stringify({ error: { message } });\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/**\n * Build the poll body for a replay job.\n *\n * Non-terminal: `{ id, model?, status, created_at? }` — the status token is the\n * ONLY synthesized value, and `content`/`error`/`usage`/`updated_at` are\n * withheld. `created_at` is a property of the task and is true at every phase;\n * `updated_at` on a recorded envelope is the timestamp of the TERMINAL\n * transition, so emitting it mid-flight would assert a field COMBINATION this\n * design has never seen from Ark and cannot source from the client library\n * either (and a consumer diffing it across polls would see it constant).\n *\n * Terminal: the stored envelope VERBATIM with `id` rewritten. Its status is\n * whatever was recorded, including `cancelled` / `expired`.\n *\n * Returns `null` when the envelope cannot produce a poll body at all (not an\n * object, or no `status`) — an authoring error the caller answers with a 502.\n */\nexport function serializeBytePlusVideoTask(\n job: BytePlusVideoReplayJob,\n logger: HandlerDefaults[\"logger\"],\n): Record<string, unknown> | null {\n const envelope = job.envelope;\n const status = envelope.status;\n if (typeof status !== \"string\" || !status) return null;\n\n if (job.phase !== \"terminal\") {\n const body: Record<string, unknown> = { id: job.id, status: job.phase };\n if (envelope.model !== undefined) body.model = envelope.model;\n if (envelope.created_at !== undefined) body.created_at = envelope.created_at;\n return body;\n }\n\n // Terminal: recorded envelope verbatim, id stamped.\n const warned = warnLatchesFor(envelope);\n\n // The one authoring error UNIQUE to this surface. Every sibling video handler\n // SYNTHESIZES the terminal status, so a fixture cannot express \"terminal but\n // not terminal\"; storing the envelope verbatim makes it expressible for the\n // first time. The job has reached its terminal poll and will now serve this\n // same body forever, so the client polls to its own timeout with no\n // diagnostic: the status is one of the client library's six (unknownStatus stays\n // silent) and it is a non-empty string (the 502 authoring-error path stays\n // silent). Name it, the way every other authoring error here is named.\n if (!isBytePlusTerminalStatus(status) && !warned.nonTerminalEnvelope) {\n warned.nonTerminalEnvelope = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} has reached its terminal poll but its envelope ` +\n `carries the NON-TERMINAL status \"${status}\" — every later poll returns this same body, ` +\n `so the client will poll it forever and fail on its own timeout. Re-record the fixture, ` +\n `or author a terminal status (succeeded, failed, cancelled, expired)`,\n );\n }\n\n if (!BYTEPLUS_TASK_STATUSES.has(status) && !warned.unknownStatus) {\n warned.unknownStatus = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} carries status \"${status}\", which is not one of ` +\n `the six the BytePlus client library declares (queued, running, succeeded, failed, ` +\n `cancelled, expired) — serving it as authored, but that library's mapStatus() THROWS on ` +\n `a status it does not recognize`,\n );\n }\n\n const content = asRecord(envelope.content);\n const videoUrl = content?.video_url;\n if (status === \"succeeded\" && typeof videoUrl !== \"string\" && !warned.missingUrl) {\n warned.missingUrl = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} reports status \"succeeded\" but carries no ` +\n `content.video_url — the client will throw \"Video is not ready for download. Check ` +\n `status first.\" Add content.video_url to the fixture envelope`,\n );\n }\n\n // Gated on the SAME two facts the missing-url warn above is gated on: the\n // 24h TTL is a property of a succeeded task's OUTPUT url, so without a\n // `succeeded` status AND a url actually present there is nothing that can\n // have expired. Ungated, this told the operator that a `failed` /\n // `cancelled` / `expired` envelope — which carries no `content` at all —\n // \"has a content.video_url that expired\", a wire fact that is not true.\n const updatedAt = envelope.updated_at;\n if (\n status === \"succeeded\" &&\n typeof videoUrl === \"string\" &&\n typeof updatedAt === \"number\" &&\n Number.isFinite(updatedAt) &&\n !warned.expiredUrl\n ) {\n const expiresAt = updatedAt + BYTEPLUS_URL_TTL_SECONDS;\n if (expiresAt * 1000 < Date.now()) {\n warned.expiredUrl = true;\n logger.warn(\n `BytePlus video fixture for job ${job.id} has a content.video_url that expired at ` +\n `${new Date(expiresAt * 1000).toISOString()} (Ark output urls live 24h from ` +\n `updated_at) — serving it anyway; re-record the fixture to get a live url`,\n );\n }\n }\n\n return { ...envelope, id: job.id };\n}\n\n// ─── Request validation ─────────────────────────────────────────────────────\n\ninterface BytePlusVideoRequest {\n model?: unknown;\n content?: unknown;\n [key: string]: unknown;\n}\n\n/**\n * Structurally valid journal body for a field-validation 400. Mirrors\n * grok-video / openrouter-video: `model` stays a string and `messages` is an\n * empty array; underscore-prefixed keys are stripped so a request cannot spoof\n * handler-set discriminators.\n */\nfunction validationJournalBody(videoReq: BytePlusVideoRequest): ChatCompletionRequest {\n const rawModel = videoReq.model;\n const model =\n typeof rawModel === \"string\"\n ? rawModel\n : rawModel === undefined\n ? \"\"\n : JSON.stringify(rawModel);\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n return { ...sanitized, model, messages: [] };\n}\n\n// ─── POST /api/v3/contents/generations/tasks — submit ───────────────────────\n\nexport async function handleBytePlusVideoCreate(\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: BytePlusVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? BYTEPLUS_VIDEO_TASKS_PATH;\n const method = req.method ?? \"POST\";\n\n const fail = (status: number, message: string, body: ChatCompletionRequest | null): void => {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body,\n response: { status, fixture: null },\n });\n writeErrorResponse(res, status, arkErrorBody(message));\n };\n\n let videoReq: BytePlusVideoRequest;\n try {\n videoReq = JSON.parse(raw) as BytePlusVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n fail(400, `Malformed JSON: ${detail}`, null);\n return;\n }\n\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n fail(400, \"Request body must be a JSON object\", null);\n return;\n }\n\n const parsedBody = validationJournalBody(videoReq);\n\n // `model` is REQUIRED, never defaulted: the client library always sends it\n // (whether Ark itself rejects a request without it is unverified here), and a\n // defaulted model id would be both a value aimock never observed and a silent\n // mis-key of the fixture match. Rejecting is the safe side of that gap.\n if (typeof videoReq.model !== \"string\" || !videoReq.model) {\n fail(\n 400,\n videoReq.model === undefined\n ? \"Missing required parameter: 'model'\"\n : \"Invalid type for parameter: 'model' must be a non-empty string\",\n parsedBody,\n );\n return;\n }\n\n if (!Array.isArray(videoReq.content) || videoReq.content.length === 0) {\n fail(\n 400,\n videoReq.content === undefined\n ? \"Missing required parameter: 'content'\"\n : \"Invalid type for parameter: 'content' must be a non-empty array\",\n parsedBody,\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model,\n messages: [{ role: \"user\", content: buildBytePlusMatchText(videoReq.content) }],\n _endpointType: \"video\",\n _videoProvider: \"byteplus\",\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 journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture\n ? \"fixture\"\n : resolveStrictMode(defaults.strict, req.headers)\n ? \"internal\"\n : defaults.record?.providers.byteplus\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!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(res, 503, arkErrorBody(strictMessage));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyBytePlusVideoSubmit({\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 // \"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 // Name the endpoint requirement: router.ts skips a `{ json }` fixture that\n // omits `match.endpoint`, so the commonest authoring mistake on this\n // surface presents as a bare no-match with nothing pointing at the cause.\n writeErrorResponse(\n res,\n 404,\n arkErrorBody(\n 'No byteplus fixture matched. A byteplus video fixture must carry match.endpoint: \"video\" ' +\n \"and a { json: <Ark task envelope> } response\",\n ),\n );\n return;\n }\n\n const worldGeneration = jobs.generation;\n const response = await resolveResponse(fixture, syntheticReq);\n\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 (!isJSONResponse(response)) {\n // A VideoResponse-shaped fixture is the other likely mistake: it is what\n // every OTHER video provider in this repo takes. Say so rather than\n // half-building a body out of it.\n if (res.destroyed || res.writableEnded) return;\n defaults.logger.warn(\n \"BytePlus video fixture matched but its response is not a { json: … } payload — this \" +\n \"surface stores the Ark task envelope verbatim, NOT a VideoResponse. Treating it as a \" +\n \"no-match\",\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n arkErrorBody(\n \"Matched byteplus fixture has a non-JSON response; expected { json: <Ark task envelope> }\",\n ),\n );\n return;\n }\n\n const envelope = asRecord(response.json);\n if (!envelope) {\n if (res.destroyed || res.writableEnded) return;\n defaults.logger.warn(\n \"BytePlus video fixture's json payload is not a JSON object — expected an Ark task envelope\",\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(res, 404, arkErrorBody(\"Matched byteplus fixture's json is not an object\"));\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 // The `cgt-` prefix is deliberate: it is the task-id prefix documented in\n // `@tanstack/ai-byteplus@0.3.4` (`src/video/wire-types.ts`, from that author's\n // own live calls — a SECONDARY source; this repo has not observed a real Ark\n // task id), so a consumer that pattern-matches the id cannot tell replay from\n // live.\n const id = `cgt-${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.bytePlusVideo);\n const job: BytePlusVideoReplayJob = {\n kind: \"replay\",\n id,\n phase: \"queued\",\n pollCount: 0,\n pollsBeforeRunning: progression.pollsBeforeInProgress,\n pollsBeforeTerminal: progression.pollsBeforeCompleted,\n envelope,\n };\n if (progression.pollsBeforeCompleted === 0) job.phase = \"terminal\";\n\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${id}`, job);\n } else {\n defaults.logger.warn(\n `BytePlus video submit resolved after a fixtures reset — not inserting job ${id} into the ` +\n `new world (its polls will 404)`,\n );\n }\n\n // NO testIdSuffix: the client encodeURIComponent()s the job id into the poll\n // path, so a `?testId=` suffix would be percent-encoded into the path segment\n // instead of surviving as a query string. A non-default testId must ride the\n // `x-test-id` header, which the adapter forwards via defaultHeaders.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id }));\n}\n\n// ─── GET /api/v3/contents/generations/tasks/{id} — status poll ──────────────\n\nexport async function handleBytePlusVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n id: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: BytePlusVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `${BYTEPLUS_VIDEO_TASKS_PATH}/${id}`;\n const method = req.method ?? \"GET\";\n const testId = getTestId(req);\n const key = `${testId}:${id}`;\n\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n req.url,\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 job = jobs.get(key);\n if (!job) {\n // Ark's error envelope SHAPE, with NO `code`. What Ark actually returns for\n // an aged-out task is UNVERIFIED: the client library reads a dotted\n // `error.code` off an open-ended vocabulary and splices it into the\n // consumer-visible error string, and the drift canary asserts a string\n // `code`. That canary does run, but what it observed live is Ark's AUTH\n // envelope, not a 404 for an unknown task — so this class's code remains\n // unobserved. Minting a code to fill the gap would put an aimock-authored\n // token into a consumer's assertion; see `arkErrorBody`.\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 arkErrorBody(\n `Unknown video task id \"${id}\" — no such job for this test scope. It was never created, ` +\n `it belongs to a different x-test-id, or it aged out of aimock's 1h job TTL`,\n ),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: video task ${id} 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 arkErrorBody(\n `Strict mode: video task ${id} is proxied live upstream (record mode) — nothing reaches ` +\n `an upstream under strict mode`,\n ),\n );\n return;\n }\n await proxyBytePlusVideoRecordPoll({\n req,\n res,\n job,\n key,\n testId,\n fixtures,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n advanceJob(job);\n jobs.set(key, job);\n\n const body = serializeBytePlusVideoTask(job, defaults.logger);\n if (!body) {\n defaults.logger.warn(\n `BytePlus video fixture for job ${id} has an envelope that is not a usable Ark task body ` +\n `(missing or non-string \"status\") — a fixture that cannot produce a poll body is an ` +\n `authoring error`,\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 502, fixture: null },\n });\n writeErrorResponse(\n res,\n 502,\n arkErrorBody(\n `Fixture envelope for video task ${id} carries no \"status\" — expected an Ark task envelope`,\n ),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\n// ─── Record mode: live interactive proxy (submit) ───────────────────────────\n\n/**\n * Proxy an unmatched submit to the configured upstream and answer the client\n * with a mock-rewritten `{ id }`. The upstream lifecycle is then driven\n * interactively by the client's own polls. Returns \"no_upstream\" when record\n * mode has no byteplus provider URL — the caller falls through to 404.\n */\nasync function proxyBytePlusVideoSubmit(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: BytePlusVideoJobMap;\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.byteplus;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"byteplus\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`BytePlus video submit proxy failed: ${msg}`);\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(res, 502, arkErrorBody(`Proxy to upstream failed: ${msg}`));\n return \"handled\";\n };\n\n // The configured base is an ORIGIN; this handler owns the whole path,\n // including the /api/v3 prefix.\n let submitUrl: URL;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, BYTEPLUS_VIDEO_TASKS_PATH);\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(`NO FIXTURE MATCH — proxying video submit to ${submitUrl.toString()}`);\n\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"byteplus\", record.providerKeys?.byteplus);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\n // Raw body, verbatim: Ark rejects an inapplicable option outright rather\n // than ignoring it, so every model-specific field must arrive untouched.\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 return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\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, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return \"handled\";\n }\n\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n\n let upstreamTaskId: string;\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 const envelope = asRecord(parsed);\n if (!envelope) return proxyError(\"Submit response is not a JSON object\");\n upstreamTaskId = String(envelope.id ?? \"\").trim();\n if (!upstreamTaskId) return proxyError(\"Submit response missing id\");\n }\n\n const upstreamPollingUrl = resolveUpstreamUrl(\n upstreamBase,\n `${BYTEPLUS_VIDEO_TASKS_PATH}/${encodeURIComponent(upstreamTaskId)}`,\n ).toString();\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const id = `cgt-${crypto.randomUUID()}`;\n const job: BytePlusVideoRecordJob = {\n kind: \"record\",\n id,\n upstreamTaskId,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${id}`, job);\n } else {\n defaults.logger.warn(\n `BytePlus video submit for upstream task ${upstreamTaskId} completed after a fixtures ` +\n `reset — not inserting the job into the new world (its polls will 404)`,\n );\n }\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: 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (status poll + eager capture) ──────\n\n/**\n * Proxy a status poll 1:1 and relay the result with the mock id substituted.\n *\n * An upstream 4xx is RELAYED VERBATIM rather than converted to 502 — a 404 in\n * particular, because the client has a deliberate 404 branch that keeps Ark's\n * own code/message on purpose, and converting it would make record mode behave\n * differently from live Ark for exactly the case that branch exists to serve.\n * 5xx and transport failures become 502.\n *\n * On ANY terminal status the rewritten body is relayed immediately and the\n * envelope is captured. All four terminal states are captured identically:\n * there is no warn-and-drop branch, so a task cancelled from the Ark console\n * mid-record yields an artifact rather than a warning and an empty directory.\n */\nasync function proxyBytePlusVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: BytePlusVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: BytePlusVideoJobMap;\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(`BytePlus video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(res, 502, arkErrorBody(`Proxy to upstream failed: ${msg}`));\n };\n\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 target = new URL(job.upstreamPollingUrl);\n const headers = prepareEgressHeaders(req, target, \"byteplus\", record.providerKeys?.byteplus);\n if (!headers) {\n proxyError(\"No configured provider credential\");\n return;\n }\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers,\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 // Relay EVERY 4xx verbatim (see the doc-comment): the client's 404 branch is\n // deliberate, and a 401/403 is a credential fact the operator must see.\n if (fetched.status >= 400 && fetched.status < 500) {\n logger.warn(\n `Upstream returned ${fetched.status} for video task ${job.upstreamTaskId} — relaying it verbatim`,\n );\n if (res.destroyed || res.writableEnded) return;\n journalProxy(fetched.status);\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return;\n }\n\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n\n let upstreamBody: Record<string, unknown>;\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 const envelope = asRecord(parsed);\n if (!envelope) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = envelope;\n }\n\n // The mock id is the ONLY rewrite; every other field passes through verbatim,\n // which is what makes this path robust to vendor additions.\n const relayBody: Record<string, unknown> = { ...upstreamBody, id: job.id };\n\n const relayJson = (body: Record<string, unknown>): void => {\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 // Terminal is derived from the NON-terminal pair, not from a second copy of\n // the four terminal tokens: a vendor-added end state is captured rather than\n // proxied until the client gives up.\n //\n // The status must FIRST clear what replay demands of it — a non-empty string,\n // the exact predicate `serializeBytePlusVideoTask` applies. Coercing instead\n // (`String(status ?? \"\")`) turned an absent status into `\"\"` and a numeric\n // `123` into `\"123\"`, and since terminal is the COMPLEMENT of the non-terminal\n // pair, both read as terminal. Capture then wrote an envelope replay cannot\n // serve and swapped the job to replay, so every later poll 502s on that same\n // envelope and a corrected upstream response is never fetched for the job —\n // and the unusable fixture is on disk. Treating it as NOT terminal relays it\n // verbatim and leaves the job upstream-backed, which is also what lets a\n // later well-formed response record normally.\n //\n // Scope: this is malformed-upstream robustness only. Ark is not known to emit\n // such a body, and non-2xx / non-JSON / non-object bodies are rejected above.\n const rawStatus = upstreamBody.status;\n const upstreamStatus = typeof rawStatus === \"string\" ? rawStatus : \"\";\n\n if (!upstreamStatus || !isBytePlusTerminalStatus(upstreamStatus)) {\n if (jobs.get(key) === job) jobs.set(key, job); // TTL refresh\n relayJson(relayBody);\n return;\n }\n\n if (record.proxyOnly) {\n if (jobs.get(key) === job) jobs.set(key, job); // TTL refresh\n relayJson(relayBody);\n return;\n }\n\n // THE capture gate, and the only one — map identity, checked once, here.\n //\n // It does two jobs at once. (a) It serializes two concurrent terminal polls:\n // both awaited upstream, and whichever resumes second finds that the first\n // already swapped the replay job into the map, so it relays without\n // capturing again. (b) It is the world-generation guard: a fixtures reset\n // clears the job map, so a job the map no longer holds belongs to a world\n // that is gone, and persisting would push a stale fixture into the NEXT\n // world's array. Either way the right answer is the same — relay what\n // upstream said, persist nothing, say nothing.\n //\n // There is NO await between here and the persist below, which is why one\n // check suffices. Two things previously sat on this path and could not fire:\n // a `job.capturing` flag (set and cleared inside a single tick, so never\n // observable by another poll) and a second copy of this identity check\n // inside the capture body, whose warning named a fixtures reset that this\n // check had already ruled out one statement earlier. Both are gone. If the\n // capture is ever made asynchronous, re-check identity immediately before\n // `persistFixture` — that is the point where it would start to matter.\n if (jobs.get(key) !== job) {\n relayJson(relayBody);\n return;\n }\n\n // Capture BEFORE relaying, so a persist failure can still ride an\n // X-AIMock-Record-Error header on this response — grok-video.ts:1105's\n // ordering. Nothing here downloads bytes: the capture is a synchronous\n // persist plus a map swap, so relaying after it costs the client one\n // filesystem write, and openrouter-video.ts:2521's \"the relay left before the\n // capture started\" constraint (which forced it to drop the header) does not\n // apply.\n captureBytePlusVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n res,\n });\n\n relayJson(relayBody);\n}\n\n/**\n * Capture a terminal record job into a fixture (PERSIST only — NO byte\n * download) and mutate the map entry into a terminal replay job.\n *\n * Runs BEFORE the caller relays, and is fully synchronous, so a persist failure\n * still reaches the client as an `X-AIMock-Record-Error` header rather than\n * only as a log line behind a 200 the client cannot distinguish from a clean\n * record.\n *\n * The upstream `id` is REMOVED at capture: replay stamps its own, and keeping a\n * real Ark task id in a committed artifact leaks it for no benefit.\n */\nfunction captureBytePlusVideoRecordFixture(args: {\n job: BytePlusVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: BytePlusVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n res: http.ServerResponse;\n}): void {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody, res } = args;\n const logger = defaults.logger;\n\n try {\n const { id: _discardedUpstreamId, ...envelope } = upstreamBody;\n void _discardedUpstreamId;\n\n const persistResult = persistFixture({\n record,\n providerKey: \"byteplus\",\n testId,\n fixture: { match: job.match, response: { json: envelope } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n\n jobs.set(key, {\n kind: \"replay\",\n id: job.id,\n phase: \"terminal\",\n pollCount: 0,\n pollsBeforeRunning: 0,\n pollsBeforeTerminal: 0,\n envelope,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `BytePlus video capture for task ${job.upstreamTaskId} failed unexpectedly (${msg}) — ` +\n `fixture not persisted; the job keeps proxying live`,\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byteplus-video.d.cts","names":[],"sources":["../src/byteplus-video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuFA;AA0CA;AAAiD;AAI1B;;;;;AAoCL;AAmBlB;;;;;AAcA;;;;;AAsIA;AA6NA;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"byteplus-video.d.cts","names":[],"sources":["../src/byteplus-video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuFA;AA0CA;AAAiD;AAI1B;;;;;AAoCL;AAmBlB;;;;;AAcA;;;;;AAsIA;AA6NA;;;;;;;;;;;AAyRA;;;;;;;;;;;;;;;;;;;;;cA/uBa,yBAAA;cA0CA,0BAAA;;KAIR,kBAAA;UA4BK,sBAAA;;;SAGD;;;;;YAKG;;;;;;;;;;UAWF,sBAAA;;;;;SAKD;;KAGG,gBAAA,GAAmB,yBAAyB;;;;;;;;cAc3C,mBAAA;;;;oBAQO;wBAUI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoHR,sBAAA;;;;;;;;;;;;;;;;;;;iBA6NM,yBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,+BACrB,sBACL;iBAgRmB,yBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,sCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,+BACrB,sBACL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byteplus-video.d.ts","names":[],"sources":["../src/byteplus-video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuFA;AA0CA;AAAiD;AAI1B;;;;;AAoCL;AAmBlB;;;;;AAcA;;;;;AAsIA;AA6NA;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"byteplus-video.d.ts","names":[],"sources":["../src/byteplus-video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AAuFA;AA0CA;AAAiD;AAI1B;;;;;AAoCL;AAmBlB;;;;;AAcA;;;;;AAsIA;AA6NA;;;;;;;;;;;AAyRA;;;;;;;;;;;;;;;;;;;;;cA/uBa,yBAAA;cA0CA,0BAAA;;KAIR,kBAAA;UA4BK,sBAAA;;;SAGD;;;;;YAKG;;;;;;;;;;UAWF,sBAAA;;;;;SAKD;;KAGG,gBAAA,GAAmB,yBAAyB;;;;;;;;cAc3C,mBAAA;;;;oBAQO;wBAUI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoHR,sBAAA;;;;;;;;;;;;;;;;;;;iBA6NM,yBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,+BACrB,sBACL;iBAgRmB,yBAAA,MACf,MAAA,CAAK,sBACL,MAAA,CAAK,sCAEA,oBACD,mBACC,uCACY,MAAA,CAAK,+BACrB,sBACL"}
|
package/dist/byteplus-video.js
CHANGED
|
@@ -427,7 +427,7 @@ async function handleBytePlusVideoCreate(req, res, raw, fixtures, journal, defau
|
|
|
427
427
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
428
428
|
defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
429
429
|
}
|
|
430
|
-
if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
|
|
430
|
+
if (applyChaos(res, fixture, defaults.chaos, req.headers, req.url, journal, {
|
|
431
431
|
method,
|
|
432
432
|
path,
|
|
433
433
|
headers: flattenHeaders(req.headers),
|
|
@@ -564,7 +564,7 @@ async function handleBytePlusVideoStatus(req, res, id, fixtures, journal, defaul
|
|
|
564
564
|
const method = req.method ?? "GET";
|
|
565
565
|
const testId = getTestId(req);
|
|
566
566
|
const key = `${testId}:${id}`;
|
|
567
|
-
if (applyChaos(res, null, defaults.chaos, req.headers, journal, {
|
|
567
|
+
if (applyChaos(res, null, defaults.chaos, req.headers, req.url, journal, {
|
|
568
568
|
method,
|
|
569
569
|
path,
|
|
570
570
|
headers: flattenHeaders(req.headers),
|