@copilotkit/aimock 1.37.4 → 1.38.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 +22 -0
- package/README.md +27 -9
- package/dist/agui-recorder.cjs +6 -0
- package/dist/agui-recorder.cjs.map +1 -1
- package/dist/agui-recorder.d.cts.map +1 -1
- package/dist/agui-recorder.d.ts.map +1 -1
- package/dist/agui-recorder.js +6 -0
- package/dist/agui-recorder.js.map +1 -1
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/api-key-auth.cjs +166 -0
- package/dist/api-key-auth.cjs.map +1 -0
- package/dist/api-key-auth.d.cts +17 -0
- package/dist/api-key-auth.d.cts.map +1 -0
- package/dist/api-key-auth.d.ts +17 -0
- package/dist/api-key-auth.d.ts.map +1 -0
- package/dist/api-key-auth.js +158 -0
- package/dist/api-key-auth.js.map +1 -0
- package/dist/cli.cjs +4 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/config-loader.cjs +4 -2
- package/dist/config-loader.cjs.map +1 -1
- package/dist/config-loader.d.cts +2 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts +2 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/config-loader.js +5 -3
- package/dist/config-loader.js.map +1 -1
- package/dist/fal-audio.cjs +2 -0
- 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 +2 -0
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +4 -1
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +4 -1
- package/dist/fal.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 +3 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +3 -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/grok-video.cjs +9 -2
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.js +10 -3
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +16 -22
- 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 +16 -21
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/llmock.cjs +14 -3
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +4 -2
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +4 -2
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +15 -5
- package/dist/llmock.js.map +1 -1
- package/dist/openrouter-chat.cjs +333 -0
- package/dist/openrouter-chat.cjs.map +1 -0
- package/dist/openrouter-chat.js +325 -0
- package/dist/openrouter-chat.js.map +1 -0
- package/dist/openrouter-video.cjs +24 -7
- 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 +25 -8
- package/dist/openrouter-video.js.map +1 -1
- package/dist/provider-auth.cjs +20 -0
- package/dist/provider-auth.cjs.map +1 -1
- package/dist/provider-auth.js +20 -1
- package/dist/provider-auth.js.map +1 -1
- package/dist/recorder.cjs +50 -8
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +50 -10
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +200 -89
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts +18 -3
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts +18 -3
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +199 -90
- package/dist/responses.js.map +1 -1
- package/dist/server.cjs +156 -66
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +157 -68
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +3 -0
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +8 -0
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +8 -0
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +3 -0
- package/dist/sse-writer.js.map +1 -1
- package/dist/stream-collapse.cjs +26 -0
- package/dist/stream-collapse.cjs.map +1 -1
- package/dist/stream-collapse.d.cts +7 -0
- package/dist/stream-collapse.d.cts.map +1 -1
- package/dist/stream-collapse.d.ts +7 -0
- package/dist/stream-collapse.d.ts.map +1 -1
- package/dist/stream-collapse.js +26 -0
- package/dist/stream-collapse.js.map +1 -1
- package/dist/transcription.cjs +49 -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 +50 -3
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +187 -5
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +187 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/veo-video.cjs +9 -2
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +10 -3
- package/dist/veo-video.js.map +1 -1
- package/dist/ws-realtime.cjs +306 -57
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts +1 -0
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +1 -0
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +307 -58
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +11 -6
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +12 -7
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +53 -19
package/dist/veo-video.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"veo-video.cjs","names":["flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","serializeErrorResponse","isVideoResponse","crypto","resolveProgression","testIdSuffix","resolveUpstreamUrl","buildForwardHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} from \"./types.js\";\nimport {\n isVideoResponse,\n isErrorResponse,\n serializeErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { resolveProgression } from \"./fal.js\";\nimport {\n buildFixtureMatch,\n buildForwardHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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 } else {\n defaults.logger.debug(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\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.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\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 (!isVideoResponse(response)) {\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: null },\n \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n const testId = getTestId(req);\n const key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} 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 geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\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 // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (res.destroyed || res.writableEnded) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\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 Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(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: VeoVideoJobMap;\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.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo 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(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers: buildForwardHeaders(req),\n body: raw,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo 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 let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures 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\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\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(`Veo video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\n const record = defaults.record;\n if (!record) {\n proxyError(\"record mode is no longer configured for an in-flight record job\");\n return;\n }\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers: buildForwardHeaders(req),\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n return;\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\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 let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (res.destroyed || res.writableEnded) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"veo-video.cjs","names":["flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","serializeErrorResponse","isVideoResponse","crypto","resolveProgression","testIdSuffix","resolveUpstreamUrl","prepareEgressHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} from \"./types.js\";\nimport {\n isVideoResponse,\n isErrorResponse,\n serializeErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { 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, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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 } else {\n defaults.logger.debug(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\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.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\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 (!isVideoResponse(response)) {\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: null },\n \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n const testId = getTestId(req);\n const key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} 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 geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\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 // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (res.destroyed || res.writableEnded) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\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 Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(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: VeoVideoJobMap;\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.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo 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(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"veo\", record.providerKeys?.veo);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\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 // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo 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 let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures 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\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\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(`Veo video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\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, \"veo\", record.providerKeys?.veo);\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 if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\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 let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (res.destroyed || res.writableEnded) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/veo-video.js
CHANGED
|
@@ -3,7 +3,7 @@ import { matchFixtureDiagnostic } from "./router.js";
|
|
|
3
3
|
import { writeErrorResponse } from "./sse-writer.js";
|
|
4
4
|
import { applyChaos } from "./chaos.js";
|
|
5
5
|
import { resolveUpstreamUrl } from "./url.js";
|
|
6
|
-
import { buildFixtureMatch,
|
|
6
|
+
import { buildFixtureMatch, persistFixture, prepareEgressHeaders, sanitizeHeaderValue } from "./recorder.js";
|
|
7
7
|
import { resolveProgression } from "./fal.js";
|
|
8
8
|
import { readEnvelopeText, testIdSuffix, upstreamTimeoutSignal } from "./video-proxy-shared.js";
|
|
9
9
|
import crypto from "node:crypto";
|
|
@@ -451,9 +451,11 @@ async function proxyVeoVideoSubmit(args) {
|
|
|
451
451
|
const worldGeneration = jobs.generation;
|
|
452
452
|
let fetched;
|
|
453
453
|
try {
|
|
454
|
+
const headers = prepareEgressHeaders(req, submitUrl, "veo", record.providerKeys?.veo);
|
|
455
|
+
if (!headers) return proxyError("No configured provider credential");
|
|
454
456
|
const upstreamRes = await fetch(submitUrl, {
|
|
455
457
|
method: "POST",
|
|
456
|
-
headers
|
|
458
|
+
headers,
|
|
457
459
|
body: raw,
|
|
458
460
|
signal: upstreamTimeoutSignal(record)
|
|
459
461
|
});
|
|
@@ -583,8 +585,13 @@ async function proxyVeoVideoRecordPoll(args) {
|
|
|
583
585
|
}
|
|
584
586
|
let fetched;
|
|
585
587
|
try {
|
|
588
|
+
const headers = prepareEgressHeaders(req, new URL(job.upstreamPollingUrl), "veo", record.providerKeys?.veo);
|
|
589
|
+
if (!headers) {
|
|
590
|
+
proxyError("No configured provider credential");
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
586
593
|
const upstreamRes = await fetch(job.upstreamPollingUrl, {
|
|
587
|
-
headers
|
|
594
|
+
headers,
|
|
588
595
|
signal: upstreamTimeoutSignal(record)
|
|
589
596
|
});
|
|
590
597
|
fetched = {
|
package/dist/veo-video.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"veo-video.js","names":[],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} from \"./types.js\";\nimport {\n isVideoResponse,\n isErrorResponse,\n serializeErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { resolveProgression } from \"./fal.js\";\nimport {\n buildFixtureMatch,\n buildForwardHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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 } else {\n defaults.logger.debug(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\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.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\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 (!isVideoResponse(response)) {\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: null },\n \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n const testId = getTestId(req);\n const key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} 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 geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\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 // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (res.destroyed || res.writableEnded) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\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 Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(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: VeoVideoJobMap;\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.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo 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(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers: buildForwardHeaders(req),\n body: raw,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo 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 let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures 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\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\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(`Veo video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\n const record = defaults.record;\n if (!record) {\n proxyError(\"record mode is no longer configured for an in-flight record job\");\n return;\n }\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers: buildForwardHeaders(req),\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n return;\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\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 let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (res.destroyed || res.writableEnded) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"veo-video.js","names":[],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} from \"./types.js\";\nimport {\n isVideoResponse,\n isErrorResponse,\n serializeErrorResponse,\n flattenHeaders,\n getTestId,\n resolveResponse,\n resolveStrictMode,\n strictOverrideField,\n getContext,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { 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, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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 } else {\n defaults.logger.debug(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\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.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\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 (!isVideoResponse(response)) {\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: null },\n \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n const testId = getTestId(req);\n const key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} 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 geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\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 // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (res.destroyed || res.writableEnded) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\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 Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(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: VeoVideoJobMap;\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.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo 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(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"veo\", record.providerKeys?.veo);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\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 // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo 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 let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures 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\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\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(`Veo video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\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, \"veo\", record.providerKeys?.veo);\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 if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\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 let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (res.destroyed || res.writableEnded) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|