@koda-sl/baker-cli 0.444.0-dev.cac1750ec → 0.445.0-dev.71ff4e44a

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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/766ad107c42df28eeadaf0ac2f9089c8a9e200f6ba0b2b1a817b8d536cbdcc49/node_modules/safe-stable-stringify/index.js","../src/engine/index.ts","../src/engine/client/http.ts","../src/engine/client/backend-client.ts","../src/engine/client/env.ts","../src/engine/engine/errors.ts","../../canvas-contract/src/adPlacements.ts","../../canvas-contract/src/adPlatformFormats.ts","../../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/766ad107c42df28eeadaf0ac2f9089c8a9e200f6ba0b2b1a817b8d536cbdcc49/node_modules/safe-stable-stringify/esm/wrapper.js","../../canvas-contract/src/canonical.ts","../../canvas-contract/src/castName.ts","../../canvas-contract/src/clipWeight.ts","../../canvas-contract/src/frameRealism.ts","../../canvas-contract/src/credits.ts","../../canvas-contract/src/registry.ts","../../canvas-contract/src/videoCost.ts","../../canvas-contract/src/nodeCost.ts","../../canvas-contract/src/personRealism.ts","../../canvas-contract/src/validateParams.ts","../../canvas-contract/src/videoBlueprint.ts","../src/engine/lib/concurrency.ts","../src/engine/lib/json-walk.ts","../src/engine/lib/ulid.ts","../src/engine/schema/refs.ts","../src/engine/schema/primitives.ts","../src/engine/storage/remote-cache-store.ts","../src/engine/nodes/remote/upload.ts","../src/engine/schema/prompts.ts","../src/engine/storage/output-writer.ts","../src/engine/storage/asset-store.ts","../src/engine/storage/sha256.ts","../src/engine/engine/cache-key.ts","../src/engine/engine/credits.ts","../src/engine/schema/canvas.ts","../src/engine/engine/graph.ts","../src/engine/engine/prompt-limit.ts","../src/engine/engine/refs.ts","../src/engine/engine/scheduler.ts","../src/engine/engine/validator.ts","../src/engine/lib/url.ts","../src/engine/engine/define.ts","../src/engine/engine/didyoumean.ts","../src/engine/engine/lint/context.ts","../src/engine/engine/lint/rules/audio.ts","../src/engine/lib/prompt-profiles.ts","../src/engine/scaffold/lib/image-params.ts","../src/engine/scaffold/platform-safe-area.ts","../src/commands/ads/list-rows.ts","../src/viewer-profile.ts","../src/engine/scaffold/static-ad-spec.ts","../src/engine/scaffold/lib/elements.ts","../src/engine/scaffold/native-surface.ts","../src/engine/scaffold/review-platforms.ts","../src/engine/scaffold/static-copy.ts","../src/engine/scaffold/static-brief.ts","../src/engine/scaffold/static-sizes.ts","../src/engine/scaffold/static-adapt.ts","../src/engine/scaffold/static-topology.ts","../src/engine/engine/lint/rules/image.ts","../src/engine/engine/lint/rules/video-prompt.ts","../src/engine/engine/lint/rules/video-timeline.ts","../src/engine/lib/speech.ts","../src/engine/lib/spine-input.ts","../src/engine/engine/lint/registry.ts","../src/engine/engine/executor.ts","../src/engine/engine/registry.ts","../src/engine/nodes/ingest.ts","../../proxy/src/fetch.ts","../../proxy/src/fetchDecision.ts","../src/lib/externalFetch.ts","../src/lib/ytDlp.ts","../src/lib/vimeoCookie.ts","../src/lib/ytDlpFormat.ts","../src/lib/ytDlpPlayerClient.ts","../src/lib/ytDlpSignal.ts","../src/engine/nodes/remote/delegate.ts","../src/engine/nodes/local/audioTimeline.ts","../src/engine/nodes/local/lib/cli-runner.ts","../src/engine/nodes/local/avMux.ts","../src/engine/nodes/local/collect.ts","../src/engine/nodes/local/ffmpeg.ts","../src/engine/nodes/local/fontSpecimen.ts","../src/engine/nodes/local/lib/assets.ts","../src/engine/nodes/local/hyperframe.ts","../src/engine/engine/composition-hash.ts","../src/engine/engine/composition-meta.ts","../src/engine/nodes/local/lib/composition-stage.ts","../src/engine/nodes/local/lib/composition-static-check.ts","../src/engine/nodes/local/lib/hyperframe-check.ts","../src/engine/nodes/local/lib/caption-style.ts","../src/engine/nodes/local/lib/hyperframe-meta.ts","../src/engine/nodes/local/lib/templating.ts","../src/engine/nodes/local/lib/hyperframe-errors.ts","../src/engine/nodes/local/hyperframe-snapshot.ts","../src/engine/nodes/local/imagemagick.ts","../src/engine/nodes/local/staticNative.ts","../src/engine/nodes/local/lib/native-assets.ts","../src/engine/nodes/local/lib/static-review.ts","../src/engine/landing-library/visualHash.ts","../src/engine/nodes/local/lib/band-check.ts","../src/engine/nodes/local/lib/brand-colour.ts","../src/engine/nodes/local/lib/copy-match.ts","../src/engine/nodes/local/lib/must-show.ts","../src/engine/nodes/local/lib/render-review.ts","../src/engine/nodes/local/lib/text-boxes.ts","../src/engine/nodes/local/staticPlace.ts","../src/engine/nodes/remote/image.ts","../src/engine/nodes/remote/registry-params.ts","../src/engine/nodes/local/lib/extend-fit.ts","../src/engine/nodes/local/lib/safe-fit.ts","../src/engine/nodes/local/staticPrefilter.ts","../src/engine/nodes/local/lib/variant-prefilter.ts","../src/engine/nodes/local/staticSafeFit.ts","../src/engine/nodes/local/text.ts","../src/engine/nodes/local/videoConcat.ts","../src/engine/nodes/remote/audioVoiceConvert.ts","../src/engine/nodes/remote/dialogue.ts","../src/engine/nodes/remote/imageAspectAdapt.ts","../src/engine/nodes/remote/imageBackgroundRemove.ts","../src/engine/nodes/remote/imageDescribe.ts","../src/engine/nodes/remote/imageLayerize.ts","../src/engine/nodes/remote/imageReferenceSheet.ts","../src/engine/nodes/remote/imageSearch.ts","../src/engine/nodes/remote/imageSelect.ts","../src/engine/nodes/remote/music.ts","../src/engine/nodes/remote/soundEffect.ts","../src/engine/nodes/remote/textGenerate.ts","../src/engine/nodes/remote/tts.ts","../src/engine/nodes/remote/video.ts","../src/engine/nodes/remote/videoBackgroundRemove.ts","../src/engine/nodes/remote/videoDeconstruct.ts","../src/engine/nodes/remote/videoLipsync.ts","../src/engine/nodes/remote/videoTranscribe.ts","../src/engine/nodes/local/lib/ffmpeg.ts","../src/engine/lib/spell-number.ts","../src/engine/nodes/local/lib/script-word-timing.ts","../src/engine/nodes/remote/voiceSelect.ts","../src/engine/schema/catalog.ts","../src/engine/storage/cache-store.ts"],"sourcesContent":["'use strict'\n\nconst { hasOwnProperty } = Object.prototype\n\nconst stringify = configure()\n\n// @ts-expect-error\nstringify.configure = configure\n// @ts-expect-error\nstringify.stringify = stringify\n\n// @ts-expect-error\nstringify.default = stringify\n\n// @ts-expect-error used for named export\nexports.stringify = stringify\n// @ts-expect-error used for named export\nexports.configure = configure\n\nmodule.exports = stringify\n\n// eslint-disable-next-line no-control-regex\nconst strEscapeSequencesRegExp = /[\\u0000-\\u001f\\u0022\\u005c\\ud800-\\udfff]/\n\n// Escape C0 control characters, double quotes, the backslash and every code\n// unit with a numeric value in the inclusive range 0xD800 to 0xDFFF.\nfunction strEscape (str) {\n // Some magic numbers that worked out fine while benchmarking with v8 8.0\n if (str.length < 5000 && !strEscapeSequencesRegExp.test(str)) {\n return `\"${str}\"`\n }\n return JSON.stringify(str)\n}\n\nfunction sort (array, comparator) {\n // Insertion sort is very efficient for small input sizes, but it has a bad\n // worst case complexity. Thus, use native array sort for bigger values.\n if (array.length > 2e2 || comparator) {\n return array.sort(comparator)\n }\n for (let i = 1; i < array.length; i++) {\n const currentValue = array[i]\n let position = i\n while (position !== 0 && array[position - 1] > currentValue) {\n array[position] = array[position - 1]\n position--\n }\n array[position] = currentValue\n }\n return array\n}\n\nconst typedArrayPrototypeGetSymbolToStringTag =\n Object.getOwnPropertyDescriptor(\n Object.getPrototypeOf(\n Object.getPrototypeOf(\n new Int8Array()\n )\n ),\n Symbol.toStringTag\n ).get\n\nfunction isTypedArrayWithEntries (value) {\n return typedArrayPrototypeGetSymbolToStringTag.call(value) !== undefined && value.length !== 0\n}\n\nfunction stringifyTypedArray (array, separator, maximumBreadth) {\n if (array.length < maximumBreadth) {\n maximumBreadth = array.length\n }\n const whitespace = separator === ',' ? '' : ' '\n let res = `\"0\":${whitespace}${array[0]}`\n for (let i = 1; i < maximumBreadth; i++) {\n res += `${separator}\"${i}\":${whitespace}${array[i]}`\n }\n return res\n}\n\nfunction getCircularValueOption (options) {\n if (hasOwnProperty.call(options, 'circularValue')) {\n const circularValue = options.circularValue\n if (typeof circularValue === 'string') {\n return `\"${circularValue}\"`\n }\n if (circularValue == null) {\n return circularValue\n }\n if (circularValue === Error || circularValue === TypeError) {\n return {\n toString () {\n throw new TypeError('Converting circular structure to JSON')\n }\n }\n }\n throw new TypeError('The \"circularValue\" argument must be of type string or the value null or undefined')\n }\n return '\"[Circular]\"'\n}\n\nfunction getDeterministicOption (options) {\n let value\n if (hasOwnProperty.call(options, 'deterministic')) {\n value = options.deterministic\n if (typeof value !== 'boolean' && typeof value !== 'function') {\n throw new TypeError('The \"deterministic\" argument must be of type boolean or comparator function')\n }\n }\n return value === undefined ? true : value\n}\n\nfunction getBooleanOption (options, key) {\n let value\n if (hasOwnProperty.call(options, key)) {\n value = options[key]\n if (typeof value !== 'boolean') {\n throw new TypeError(`The \"${key}\" argument must be of type boolean`)\n }\n }\n return value === undefined ? true : value\n}\n\nfunction getPositiveIntegerOption (options, key) {\n let value\n if (hasOwnProperty.call(options, key)) {\n value = options[key]\n if (typeof value !== 'number') {\n throw new TypeError(`The \"${key}\" argument must be of type number`)\n }\n if (!Number.isInteger(value)) {\n throw new TypeError(`The \"${key}\" argument must be an integer`)\n }\n if (value < 1) {\n throw new RangeError(`The \"${key}\" argument must be >= 1`)\n }\n }\n return value === undefined ? Infinity : value\n}\n\nfunction getItemCount (number) {\n if (number === 1) {\n return '1 item'\n }\n return `${number} items`\n}\n\nfunction getUniqueReplacerSet (replacerArray) {\n const replacerSet = new Set()\n for (const value of replacerArray) {\n if (typeof value === 'string' || typeof value === 'number') {\n replacerSet.add(String(value))\n }\n }\n return replacerSet\n}\n\nfunction getStrictOption (options) {\n if (hasOwnProperty.call(options, 'strict')) {\n const value = options.strict\n if (typeof value !== 'boolean') {\n throw new TypeError('The \"strict\" argument must be of type boolean')\n }\n if (value) {\n return (value) => {\n let message = `Object can not safely be stringified. Received type ${typeof value}`\n if (typeof value !== 'function') message += ` (${value.toString()})`\n throw new Error(message)\n }\n }\n }\n}\n\nfunction configure (options) {\n options = { ...options }\n const fail = getStrictOption(options)\n if (fail) {\n if (options.bigint === undefined) {\n options.bigint = false\n }\n if (!('circularValue' in options)) {\n options.circularValue = Error\n }\n }\n const circularValue = getCircularValueOption(options)\n const bigint = getBooleanOption(options, 'bigint')\n const deterministic = getDeterministicOption(options)\n const comparator = typeof deterministic === 'function' ? deterministic : undefined\n const maximumDepth = getPositiveIntegerOption(options, 'maximumDepth')\n const maximumBreadth = getPositiveIntegerOption(options, 'maximumBreadth')\n\n function stringifyFnReplacer (key, parent, stack, replacer, spacer, indentation) {\n let value = parent[key]\n\n if (typeof value === 'object' && value !== null && typeof value.toJSON === 'function') {\n value = value.toJSON(key)\n }\n value = replacer.call(parent, key, value)\n\n switch (typeof value) {\n case 'string':\n return strEscape(value)\n case 'object': {\n if (value === null) {\n return 'null'\n }\n if (stack.indexOf(value) !== -1) {\n return circularValue\n }\n\n let res = ''\n let join = ','\n const originalIndentation = indentation\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Array]\"'\n }\n stack.push(value)\n if (spacer !== '') {\n indentation += spacer\n res += `\\n${indentation}`\n join = `,\\n${indentation}`\n }\n const maximumValuesToStringify = Math.min(value.length, maximumBreadth)\n let i = 0\n for (; i < maximumValuesToStringify - 1; i++) {\n const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n res += join\n }\n const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n if (value.length - 1 > maximumBreadth) {\n const removedKeys = value.length - maximumBreadth - 1\n res += `${join}\"... ${getItemCount(removedKeys)} not stringified\"`\n }\n if (spacer !== '') {\n res += `\\n${originalIndentation}`\n }\n stack.pop()\n return `[${res}]`\n }\n\n let keys = Object.keys(value)\n const keyLength = keys.length\n if (keyLength === 0) {\n return '{}'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Object]\"'\n }\n let whitespace = ''\n let separator = ''\n if (spacer !== '') {\n indentation += spacer\n join = `,\\n${indentation}`\n whitespace = ' '\n }\n const maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)\n if (deterministic && !isTypedArrayWithEntries(value)) {\n keys = sort(keys, comparator)\n }\n stack.push(value)\n for (let i = 0; i < maximumPropertiesToStringify; i++) {\n const key = keys[i]\n const tmp = stringifyFnReplacer(key, value, stack, replacer, spacer, indentation)\n if (tmp !== undefined) {\n res += `${separator}${strEscape(key)}:${whitespace}${tmp}`\n separator = join\n }\n }\n if (keyLength > maximumBreadth) {\n const removedKeys = keyLength - maximumBreadth\n res += `${separator}\"...\":${whitespace}\"${getItemCount(removedKeys)} not stringified\"`\n separator = join\n }\n if (spacer !== '' && separator.length > 1) {\n res = `\\n${indentation}${res}\\n${originalIndentation}`\n }\n stack.pop()\n return `{${res}}`\n }\n case 'number':\n return isFinite(value) ? String(value) : fail ? fail(value) : 'null'\n case 'boolean':\n return value === true ? 'true' : 'false'\n case 'undefined':\n return undefined\n case 'bigint':\n if (bigint) {\n return String(value)\n }\n // fallthrough\n default:\n return fail ? fail(value) : undefined\n }\n }\n\n function stringifyArrayReplacer (key, value, stack, replacer, spacer, indentation) {\n if (typeof value === 'object' && value !== null && typeof value.toJSON === 'function') {\n value = value.toJSON(key)\n }\n\n switch (typeof value) {\n case 'string':\n return strEscape(value)\n case 'object': {\n if (value === null) {\n return 'null'\n }\n if (stack.indexOf(value) !== -1) {\n return circularValue\n }\n\n const originalIndentation = indentation\n let res = ''\n let join = ','\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Array]\"'\n }\n stack.push(value)\n if (spacer !== '') {\n indentation += spacer\n res += `\\n${indentation}`\n join = `,\\n${indentation}`\n }\n const maximumValuesToStringify = Math.min(value.length, maximumBreadth)\n let i = 0\n for (; i < maximumValuesToStringify - 1; i++) {\n const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n res += join\n }\n const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n if (value.length - 1 > maximumBreadth) {\n const removedKeys = value.length - maximumBreadth - 1\n res += `${join}\"... ${getItemCount(removedKeys)} not stringified\"`\n }\n if (spacer !== '') {\n res += `\\n${originalIndentation}`\n }\n stack.pop()\n return `[${res}]`\n }\n stack.push(value)\n let whitespace = ''\n if (spacer !== '') {\n indentation += spacer\n join = `,\\n${indentation}`\n whitespace = ' '\n }\n let separator = ''\n for (const key of replacer) {\n const tmp = stringifyArrayReplacer(key, value[key], stack, replacer, spacer, indentation)\n if (tmp !== undefined) {\n res += `${separator}${strEscape(key)}:${whitespace}${tmp}`\n separator = join\n }\n }\n if (spacer !== '' && separator.length > 1) {\n res = `\\n${indentation}${res}\\n${originalIndentation}`\n }\n stack.pop()\n return `{${res}}`\n }\n case 'number':\n return isFinite(value) ? String(value) : fail ? fail(value) : 'null'\n case 'boolean':\n return value === true ? 'true' : 'false'\n case 'undefined':\n return undefined\n case 'bigint':\n if (bigint) {\n return String(value)\n }\n // fallthrough\n default:\n return fail ? fail(value) : undefined\n }\n }\n\n function stringifyIndent (key, value, stack, spacer, indentation) {\n switch (typeof value) {\n case 'string':\n return strEscape(value)\n case 'object': {\n if (value === null) {\n return 'null'\n }\n if (typeof value.toJSON === 'function') {\n value = value.toJSON(key)\n // Prevent calling `toJSON` again.\n if (typeof value !== 'object') {\n return stringifyIndent(key, value, stack, spacer, indentation)\n }\n if (value === null) {\n return 'null'\n }\n }\n if (stack.indexOf(value) !== -1) {\n return circularValue\n }\n const originalIndentation = indentation\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Array]\"'\n }\n stack.push(value)\n indentation += spacer\n let res = `\\n${indentation}`\n const join = `,\\n${indentation}`\n const maximumValuesToStringify = Math.min(value.length, maximumBreadth)\n let i = 0\n for (; i < maximumValuesToStringify - 1; i++) {\n const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n res += join\n }\n const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation)\n res += tmp !== undefined ? tmp : 'null'\n if (value.length - 1 > maximumBreadth) {\n const removedKeys = value.length - maximumBreadth - 1\n res += `${join}\"... ${getItemCount(removedKeys)} not stringified\"`\n }\n res += `\\n${originalIndentation}`\n stack.pop()\n return `[${res}]`\n }\n\n let keys = Object.keys(value)\n const keyLength = keys.length\n if (keyLength === 0) {\n return '{}'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Object]\"'\n }\n indentation += spacer\n const join = `,\\n${indentation}`\n let res = ''\n let separator = ''\n let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)\n if (isTypedArrayWithEntries(value)) {\n res += stringifyTypedArray(value, join, maximumBreadth)\n keys = keys.slice(value.length)\n maximumPropertiesToStringify -= value.length\n separator = join\n }\n if (deterministic) {\n keys = sort(keys, comparator)\n }\n stack.push(value)\n for (let i = 0; i < maximumPropertiesToStringify; i++) {\n const key = keys[i]\n const tmp = stringifyIndent(key, value[key], stack, spacer, indentation)\n if (tmp !== undefined) {\n res += `${separator}${strEscape(key)}: ${tmp}`\n separator = join\n }\n }\n if (keyLength > maximumBreadth) {\n const removedKeys = keyLength - maximumBreadth\n res += `${separator}\"...\": \"${getItemCount(removedKeys)} not stringified\"`\n separator = join\n }\n if (separator !== '') {\n res = `\\n${indentation}${res}\\n${originalIndentation}`\n }\n stack.pop()\n return `{${res}}`\n }\n case 'number':\n return isFinite(value) ? String(value) : fail ? fail(value) : 'null'\n case 'boolean':\n return value === true ? 'true' : 'false'\n case 'undefined':\n return undefined\n case 'bigint':\n if (bigint) {\n return String(value)\n }\n // fallthrough\n default:\n return fail ? fail(value) : undefined\n }\n }\n\n function stringifySimple (key, value, stack) {\n switch (typeof value) {\n case 'string':\n return strEscape(value)\n case 'object': {\n if (value === null) {\n return 'null'\n }\n if (typeof value.toJSON === 'function') {\n value = value.toJSON(key)\n // Prevent calling `toJSON` again\n if (typeof value !== 'object') {\n return stringifySimple(key, value, stack)\n }\n if (value === null) {\n return 'null'\n }\n }\n if (stack.indexOf(value) !== -1) {\n return circularValue\n }\n\n let res = ''\n\n const hasLength = value.length !== undefined\n if (hasLength && Array.isArray(value)) {\n if (value.length === 0) {\n return '[]'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Array]\"'\n }\n stack.push(value)\n const maximumValuesToStringify = Math.min(value.length, maximumBreadth)\n let i = 0\n for (; i < maximumValuesToStringify - 1; i++) {\n const tmp = stringifySimple(String(i), value[i], stack)\n res += tmp !== undefined ? tmp : 'null'\n res += ','\n }\n const tmp = stringifySimple(String(i), value[i], stack)\n res += tmp !== undefined ? tmp : 'null'\n if (value.length - 1 > maximumBreadth) {\n const removedKeys = value.length - maximumBreadth - 1\n res += `,\"... ${getItemCount(removedKeys)} not stringified\"`\n }\n stack.pop()\n return `[${res}]`\n }\n\n let keys = Object.keys(value)\n const keyLength = keys.length\n if (keyLength === 0) {\n return '{}'\n }\n if (maximumDepth < stack.length + 1) {\n return '\"[Object]\"'\n }\n let separator = ''\n let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth)\n if (hasLength && isTypedArrayWithEntries(value)) {\n res += stringifyTypedArray(value, ',', maximumBreadth)\n keys = keys.slice(value.length)\n maximumPropertiesToStringify -= value.length\n separator = ','\n }\n if (deterministic) {\n keys = sort(keys, comparator)\n }\n stack.push(value)\n for (let i = 0; i < maximumPropertiesToStringify; i++) {\n const key = keys[i]\n const tmp = stringifySimple(key, value[key], stack)\n if (tmp !== undefined) {\n res += `${separator}${strEscape(key)}:${tmp}`\n separator = ','\n }\n }\n if (keyLength > maximumBreadth) {\n const removedKeys = keyLength - maximumBreadth\n res += `${separator}\"...\":\"${getItemCount(removedKeys)} not stringified\"`\n }\n stack.pop()\n return `{${res}}`\n }\n case 'number':\n return isFinite(value) ? String(value) : fail ? fail(value) : 'null'\n case 'boolean':\n return value === true ? 'true' : 'false'\n case 'undefined':\n return undefined\n case 'bigint':\n if (bigint) {\n return String(value)\n }\n // fallthrough\n default:\n return fail ? fail(value) : undefined\n }\n }\n\n function stringify (value, replacer, space) {\n if (arguments.length > 1) {\n let spacer = ''\n if (typeof space === 'number') {\n spacer = ' '.repeat(Math.min(space, 10))\n } else if (typeof space === 'string') {\n spacer = space.slice(0, 10)\n }\n if (replacer != null) {\n if (typeof replacer === 'function') {\n return stringifyFnReplacer('', { '': value }, [], replacer, spacer, '')\n }\n if (Array.isArray(replacer)) {\n return stringifyArrayReplacer('', value, [], getUniqueReplacerSet(replacer), spacer, '')\n }\n }\n if (spacer.length !== 0) {\n return stringifyIndent('', value, [], spacer, '')\n }\n }\n return stringifySimple('', value, [])\n }\n\n return stringify\n}\n","import path from \"node:path\";\nimport { requireConnection } from \"../env.ts\";\nimport { BackendClient as _BackendClient } from \"./client/backend-client.ts\";\nimport { remoteCacheEnabledFromEnv } from \"./client/env.ts\";\nimport type { AnyNodeDefinition } from \"./engine/define.ts\";\nimport { ValidationError as _ValidationError } from \"./engine/errors.ts\";\nimport { Engine as _Engine } from \"./engine/executor.ts\";\nimport { NodeRegistry } from \"./engine/registry.ts\";\nimport { validateCanvasDeep as _validateCanvasDeep } from \"./engine/validator.ts\";\nimport { ingestNode } from \"./nodes/ingest.ts\";\nimport { audioTimelineNode } from \"./nodes/local/audioTimeline.ts\";\nimport { avMuxNode } from \"./nodes/local/avMux.ts\";\nimport { collectNode } from \"./nodes/local/collect.ts\";\nimport { ffmpegNode } from \"./nodes/local/ffmpeg.ts\";\nimport { fontSpecimenNode } from \"./nodes/local/fontSpecimen.ts\";\nimport { hyperframeRenderNode } from \"./nodes/local/hyperframe.ts\";\nimport { hyperframeSnapshotNode } from \"./nodes/local/hyperframe-snapshot.ts\";\nimport { imagemagickNode } from \"./nodes/local/imagemagick.ts\";\nimport { staticNativeNode } from \"./nodes/local/staticNative.ts\";\nimport { staticExtendMergeNode, staticPlaceNode } from \"./nodes/local/staticPlace.ts\";\nimport { staticPrefilterNode } from \"./nodes/local/staticPrefilter.ts\";\nimport { staticSafeFitNode } from \"./nodes/local/staticSafeFit.ts\";\nimport { textNode } from \"./nodes/local/text.ts\";\nimport { videoConcatNode } from \"./nodes/local/videoConcat.ts\";\nimport { audioVoiceConvertNode } from \"./nodes/remote/audioVoiceConvert.ts\";\nimport { dialogueNode } from \"./nodes/remote/dialogue.ts\";\nimport { imageGenerateNode } from \"./nodes/remote/image.ts\";\nimport { imageAspectAdaptNode } from \"./nodes/remote/imageAspectAdapt.ts\";\nimport { imageBackgroundRemoveNode } from \"./nodes/remote/imageBackgroundRemove.ts\";\nimport { imageDescribeNode } from \"./nodes/remote/imageDescribe.ts\";\nimport { imageLayerizeNode } from \"./nodes/remote/imageLayerize.ts\";\nimport { imageReferenceSheetNode } from \"./nodes/remote/imageReferenceSheet.ts\";\nimport { imageSearchNode } from \"./nodes/remote/imageSearch.ts\";\nimport { imageSelectNode } from \"./nodes/remote/imageSelect.ts\";\nimport { musicNode } from \"./nodes/remote/music.ts\";\nimport { soundEffectNode } from \"./nodes/remote/soundEffect.ts\";\nimport { textGenerateNode } from \"./nodes/remote/textGenerate.ts\";\nimport { ttsNode } from \"./nodes/remote/tts.ts\";\nimport { videoGenerateNode } from \"./nodes/remote/video.ts\";\nimport { videoBackgroundRemoveNode } from \"./nodes/remote/videoBackgroundRemove.ts\";\nimport { videoDeconstructNode } from \"./nodes/remote/videoDeconstruct.ts\";\nimport { videoLipsyncNode } from \"./nodes/remote/videoLipsync.ts\";\nimport { videoTranscribeNode } from \"./nodes/remote/videoTranscribe.ts\";\nimport { voiceSelectNode } from \"./nodes/remote/voiceSelect.ts\";\nimport { generateCatalog as _generateCatalog } from \"./schema/catalog.ts\";\nimport { LocalAssetStore as _LocalAssetStore } from \"./storage/asset-store.ts\";\nimport { LocalCacheStore as _LocalCacheStore } from \"./storage/cache-store.ts\";\nimport { LayeredCacheStore, RemoteCacheStore } from \"./storage/remote-cache-store.ts\";\n\nexport const BackendClient = _BackendClient;\nexport const Engine = _Engine;\nexport const LocalAssetStore = _LocalAssetStore;\nexport const LocalCacheStore = _LocalCacheStore;\nexport const validateCanvasDeep = _validateCanvasDeep;\nexport const generateCatalog = _generateCatalog;\nexport const ValidationError = _ValidationError;\n\nconst LOCAL_NODES: readonly AnyNodeDefinition[] = [\n textNode,\n ingestNode,\n hyperframeRenderNode,\n hyperframeSnapshotNode,\n ffmpegNode,\n imagemagickNode,\n videoTranscribeNode,\n fontSpecimenNode,\n audioTimelineNode,\n videoConcatNode,\n avMuxNode,\n collectNode,\n staticPrefilterNode,\n staticNativeNode,\n staticSafeFitNode,\n staticPlaceNode,\n staticExtendMergeNode,\n];\n\nconst REMOTE_NODES: readonly AnyNodeDefinition[] = [\n textGenerateNode,\n imageGenerateNode,\n imageAspectAdaptNode,\n imageDescribeNode,\n imageReferenceSheetNode,\n imageSearchNode,\n imageSelectNode,\n videoGenerateNode,\n ttsNode,\n audioVoiceConvertNode,\n musicNode,\n dialogueNode,\n soundEffectNode,\n videoLipsyncNode,\n videoBackgroundRemoveNode,\n videoDeconstructNode,\n voiceSelectNode,\n imageBackgroundRemoveNode,\n imageLayerizeNode,\n];\n\n/** Convenience: registry pre-loaded with every node ships with the engine. */\nexport function defaultRegistry(): NodeRegistry {\n const r = new NodeRegistry();\n for (const n of LOCAL_NODES) r.register(n);\n for (const n of REMOTE_NODES) r.register(n);\n return r;\n}\n\n/** Build a ready-to-run engine using env vars + the project cwd. */\nexport function createEngineFromEnv(\n opts: {\n cwd?: string;\n cacheDir?: string;\n outputsDir?: string;\n log?: (line: string) => void;\n /**\n * Layer the company-scoped remote cache over the local one (and stamp\n * durable asset URLs onto fresh outputs) so a fresh sandbox re-runs an\n * already-computed canvas at zero credits. Defaults to on; disable with\n * `remoteCache: false` or env BAKER_CANVAS_REMOTE_CACHE=off.\n */\n remoteCache?: boolean;\n } = {},\n): _Engine {\n const cwd = opts.cwd ?? process.cwd();\n const cacheDir = opts.cacheDir ?? path.join(cwd, \"canvas\", \".cache\");\n const outputsDir = opts.outputsDir ?? path.join(cwd, \"canvas\");\n const connection = requireConnection();\n const client = new _BackendClient({ baseUrl: connection.BAKER_API_URL, apiKey: connection.BAKER_API_KEY });\n const assets = new _LocalAssetStore(path.join(cacheDir, \"assets\"));\n const localCache = new _LocalCacheStore(path.join(cacheDir, \"index\"));\n const remoteCacheEnabled = opts.remoteCache ?? remoteCacheEnabledFromEnv();\n const cache = remoteCacheEnabled\n ? new LayeredCacheStore({\n local: localCache,\n remote: new RemoteCacheStore(client, opts.log),\n assets,\n log: opts.log,\n })\n : localCache;\n return new _Engine({\n registry: defaultRegistry(),\n client,\n assets,\n cache,\n outputsDir,\n log: opts.log,\n persistAssets: remoteCacheEnabled,\n });\n}\n","import { debugLogHttp, readBodyForLog } from \"../../debugLog.ts\";\n\n// A deterministic provider content-policy block (fal/Seedance rejecting a photoreal\n// face). Backends signal it with `code: \"content_policy_blocked\"`; we key off the code,\n// not the HTTP status, because fal's proxy chain surfaces the same block as a clean 422\n// OR a masked 502 — treating it as a 5xx would retry a call that can never succeed.\nexport const CONTENT_POLICY_CODE = \"content_policy_blocked\";\n\n// A duplicate of a request the backend is still running (the same idempotency\n// key, claimed by the first attempt). Retrying is exactly right: the backend\n// replays the first attempt's stored response and never dispatches twice.\nconst REQUEST_IN_PROGRESS_CODE = \"request_in_progress\";\n\ntype HttpError =\n | { kind: \"unauthorized\"; status: number; message: string }\n | { kind: \"validation\"; status: number; message: string; code?: string; details?: unknown }\n | { kind: \"provider\"; status: number; provider?: string; code: string; message: string; retryable: boolean }\n | { kind: \"content_policy\"; status: number; provider?: string; message: string }\n | { kind: \"timeout\"; provider?: string; message: string }\n | { kind: \"server\"; status: number; message: string }\n | { kind: \"network\"; cause: unknown };\n\nexport class BackendHttpError extends Error {\n detail: HttpError;\n constructor(detail: HttpError) {\n super(describe(detail));\n this.name = \"BackendHttpError\";\n this.detail = detail;\n }\n}\n\nfunction describe(d: HttpError): string {\n switch (d.kind) {\n case \"unauthorized\":\n return `unauthorized: ${d.message}`;\n case \"validation\":\n return `invalid request: ${d.message}`;\n case \"provider\":\n return `provider error${d.provider ? ` (${d.provider})` : \"\"}: ${d.message}`;\n case \"content_policy\":\n return `content policy blocked${d.provider ? ` (${d.provider})` : \"\"}: ${d.message}`;\n case \"timeout\":\n return `provider timeout${d.provider ? ` (${d.provider})` : \"\"}: ${d.message}`;\n case \"server\":\n return `server error ${d.status}: ${d.message}`;\n case \"network\":\n return d.cause instanceof Error ? `network: ${d.cause.message}` : \"network error\";\n default: {\n const _ex: never = d;\n return String(_ex);\n }\n }\n}\n\ntype HttpClientOpts = {\n baseUrl: string;\n apiKey: string;\n timeoutMs?: number;\n maxRetries?: number;\n fetchFn?: typeof fetch;\n sleepFn?: (ms: number) => Promise<void>;\n};\n\nexport class HttpClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly fetchFn: typeof fetch;\n private readonly sleepFn: (ms: number) => Promise<void>;\n\n constructor(opts: HttpClientOpts) {\n this.baseUrl = opts.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = opts.apiKey;\n this.timeoutMs = opts.timeoutMs ?? 10 * 60 * 1000;\n this.maxRetries = opts.maxRetries ?? 3;\n this.fetchFn = opts.fetchFn ?? fetch;\n this.sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));\n }\n\n async postJson<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n return await this.requestJson<T>(\"POST\", path, body, signal);\n }\n\n async putJson<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {\n return await this.requestJson<T>(\"PUT\", path, body, signal);\n }\n\n async getJson<T>(path: string, signal?: AbortSignal): Promise<T> {\n return await this.requestJson<T>(\"GET\", path, undefined, signal);\n }\n\n private async requestJson<T>(\n method: \"GET\" | \"POST\" | \"PUT\",\n path: string,\n body: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const url = `${this.baseUrl}${path.startsWith(\"/\") ? path : `/${path}`}`;\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n const outcome = await this.attempt<T>(method, url, body, attempt, signal);\n if (outcome.kind === \"value\") return outcome.value;\n if (outcome.kind === \"retry\") {\n await this.sleepFn(backoffMs(attempt));\n continue;\n }\n throw outcome.error;\n }\n throw new BackendHttpError({ kind: \"server\", status: 0, message: \"retries exhausted\" });\n }\n\n private async attempt<T>(\n method: \"GET\" | \"POST\" | \"PUT\",\n url: string,\n body: unknown,\n attempt: number,\n signal: AbortSignal | undefined,\n ): Promise<{ kind: \"value\"; value: T } | { kind: \"retry\" } | { kind: \"throw\"; error: BackendHttpError }> {\n const controller = new AbortController();\n const onAbort = () => controller.abort(signal?.reason);\n if (signal) {\n if (signal.aborted) controller.abort(signal.reason);\n else signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n const timer = setTimeout(() => controller.abort(new Error(\"timeout\")), this.timeoutMs);\n const startedAt = Date.now();\n try {\n const res = await this.fetchFn(url, {\n method,\n headers:\n method === \"GET\"\n ? { Authorization: `Bearer ${this.apiKey}` }\n : { \"Content-Type\": \"application/json\", Authorization: `Bearer ${this.apiKey}` },\n body: method === \"GET\" ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n debugLogHttp({\n source: \"canvas\",\n method,\n url,\n requestBody: method === \"GET\" ? undefined : body,\n status: res.status,\n ok: res.ok,\n responseBody: await readBodyForLog(res),\n durationMs: Date.now() - startedAt,\n });\n if (res.ok) return { kind: \"value\", value: (await res.json()) as T };\n const err = await parseErrorBody(res);\n if (attempt < this.maxRetries && shouldRetry(err)) return { kind: \"retry\" };\n return { kind: \"throw\", error: new BackendHttpError(err) };\n } catch (e) {\n debugLogHttp({\n source: \"canvas\",\n method,\n url,\n requestBody: method === \"GET\" ? undefined : body,\n durationMs: Date.now() - startedAt,\n error: e,\n });\n return this.handleAttemptException(e, attempt, signal, method);\n } finally {\n clearTimeout(timer);\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n private handleAttemptException(\n e: unknown,\n attempt: number,\n signal: AbortSignal | undefined,\n method: \"GET\" | \"POST\" | \"PUT\",\n ): { kind: \"retry\" } | { kind: \"throw\"; error: BackendHttpError } {\n if (e instanceof BackendHttpError) return { kind: \"throw\", error: e };\n const aborted = (e as { name?: string }).name === \"AbortError\";\n if (aborted && signal?.aborted) {\n return { kind: \"throw\", error: new BackendHttpError({ kind: \"network\", cause: signal.reason ?? e }) };\n }\n // Internal timeout: retrying a POST re-dispatches a non-idempotent provider\n // call (the backend only replays *completed* responses, and a timeout means\n // nothing was stored), so only GETs auto-retry — a POST fails terminally\n // rather than risk a double charge.\n if (aborted) {\n if (method === \"GET\" && attempt < this.maxRetries) return { kind: \"retry\" };\n return { kind: \"throw\", error: new BackendHttpError({ kind: \"timeout\", message: \"request timed out\" }) };\n }\n if (attempt < this.maxRetries) return { kind: \"retry\" };\n return { kind: \"throw\", error: new BackendHttpError({ kind: \"network\", cause: e }) };\n }\n}\n\nexport type ErrorBody = { code?: string; message?: string; provider?: string; retryable?: boolean; details?: unknown };\n\nasync function parseErrorBody(res: Response): Promise<HttpError> {\n let body: { error?: ErrorBody } = {};\n try {\n body = (await res.json()) as typeof body;\n } catch {\n // ignore parse errors\n }\n const errObj = body.error ?? {};\n // No `{ error: … }` envelope means this never reached the node dispatcher —\n // every failure it handles carries a code, a provider and real text. A\n // bodyless 5xx is the edge: it dropped a held-open request, or the backend\n // restarted mid-flight. Say that, instead of `HTTP 502` with a provider\n // attached that never touched the request.\n const bodyless = body.error === undefined && res.status >= 500;\n const message = bodyless\n ? `HTTP ${res.status} with no response body — the request was dropped before the server answered`\n : (errObj.message ?? `HTTP ${res.status}`);\n // 502 is the ONLY status whose classification is wrong for a bodyless body:\n // it would read as a retryable `provider` error blaming nobody. Everything\n // else already classifies correctly and must keep doing so — notably 504,\n // which an edge gateway timeout also returns bodyless (an HTML page) and\n // which must stay a terminal `timeout`: retrying that POST re-dispatches a\n // billed provider call for every node still on the synchronous exec path.\n if (bodyless && res.status === 502) {\n return { kind: \"server\", status: res.status, message };\n }\n return classifyBackendError(res.status, errObj, message);\n}\n\n/**\n * A 4xx that names a provider is that provider's verdict on the request, not a\n * malformed call. An unreadable page comes back this way so its reason and its\n * `retryable: false` survive the edge, which strips a 502's body; read as a\n * plain validation error it reached the agent as `invalid_params` and sent it\n * to rewrite params that were never wrong. Null when the body names nobody.\n */\nfunction providerVerdict(status: number, errObj: ErrorBody, message: string): HttpError | null {\n if (errObj.provider === undefined && errObj.code !== \"provider_error\") return null;\n return {\n kind: \"provider\",\n status,\n provider: errObj.provider,\n code: errObj.code ?? \"provider_error\",\n message,\n // A 4xx is terminal unless the backend says otherwise — the opposite\n // default from the 502 branch, where the status itself means \"try again\".\n retryable: errObj.retryable ?? false,\n };\n}\n\n/**\n * THE classifier for a backend failure, whichever way it arrived: an HTTP error\n * response on the synchronous exec path, or the stored error of an async job\n * (`backend-client.ts` feeds a job's `{ code, message, status, retryable }`\n * through here too). One function, so the same failure maps to the same kind on\n * both paths. It used to read as `validation` synchronously and as a provider\n * content-policy error from a job.\n */\nexport function classifyBackendError(status: number, errObj: ErrorBody, message: string): HttpError {\n return (\n codeVerdict(status, errObj, message) ??\n clientVerdict(status, errObj, message) ??\n serverVerdict(status, errObj, message) ?? {\n // Remaining 4xx (404, 405, 409, …): the request itself is wrong, so a retry\n // can't help — and re-dispatching a POST risks a double provider charge.\n kind: \"validation\",\n status,\n message,\n code: errObj.code,\n details: errObj.details,\n }\n );\n}\n\n/** Codes that decide the kind whatever status they arrive on. */\nfunction codeVerdict(status: number, errObj: ErrorBody, message: string): HttpError | null {\n // A content-policy block is non-retryable regardless of the HTTP status it\n // arrives on (fal masks the same 422 as a 502 in its proxy chain).\n if (errObj.code === CONTENT_POLICY_CODE) {\n return { kind: \"content_policy\", status, provider: errObj.provider, message };\n }\n if (errObj.code === REQUEST_IN_PROGRESS_CODE) return { kind: \"server\", status, message };\n return null;\n}\n\n/** Auth failures and refused requests. */\nfunction clientVerdict(status: number, errObj: ErrorBody, message: string): HttpError | null {\n if (status === 401 || status === 403) return { kind: \"unauthorized\", status, message };\n if (status !== 400 && status !== 422) return null;\n return (\n providerVerdict(status, errObj, message) ?? {\n kind: \"validation\",\n status,\n message,\n code: errObj.code,\n details: errObj.details,\n }\n );\n}\n\n/** 5xx and 429: transient unless the body says retrying cannot help. */\nfunction serverVerdict(status: number, errObj: ErrorBody, message: string): HttpError | null {\n // A 5xx whose body says retrying cannot help (a node not implemented, a job\n // that crashed or was canceled) is terminal: the status alone would retry it.\n if (status >= 500 && errObj.retryable === false) {\n return {\n kind: \"provider\",\n status,\n provider: errObj.provider,\n code: errObj.code ?? \"server_error\",\n message,\n retryable: false,\n };\n }\n if (status === 504 || (status === 502 && errObj.code === \"provider_timeout\")) {\n return { kind: \"timeout\", provider: errObj.provider, message };\n }\n if (status === 502) {\n return {\n kind: \"provider\",\n status,\n provider: errObj.provider,\n code: errObj.code ?? \"provider_error\",\n message,\n retryable: errObj.retryable ?? true,\n };\n }\n if (status >= 500 || status === 429) return { kind: \"server\", status, message };\n return null;\n}\n\nfunction backoffMs(attempt: number): number {\n return 1000 * 2 ** attempt;\n}\n\nfunction shouldRetry(err: HttpError): boolean {\n if (err.kind === \"server\") return true;\n if (err.kind === \"provider\" && err.retryable) return true;\n return false;\n}\n","import { BackendHttpError, classifyBackendError, HttpClient } from \"./http.ts\";\n\nexport type RawAssetOutput = {\n kind: \"image\" | \"video\" | \"audio\" | \"json\" | \"text\" | \"font\";\n url: string;\n sha256: string;\n mime: string;\n metadata?: Record<string, unknown>;\n};\n\ntype ExecRequest = {\n nodeType: string;\n nodeVersion: string;\n params: unknown;\n inputs: Record<string, unknown>;\n /** Run-scoped dedupe key: the backend replays the stored response for a\n * repeated key instead of re-dispatching to the provider (and re-charging). */\n idempotency_key?: string;\n /** Run + node identity, so a completed async job can be mapped back to its\n * frozen run's node by the backend stale-run reconciliation sweep. */\n canvas_run_id?: string;\n node_id?: string;\n};\n\ntype ExecResponse = {\n outputs: Record<string, RawAssetOutput | RawAssetOutput[] | unknown>;\n credits_charged?: number;\n duration_ms?: number;\n};\n\n/** A long-running node accepted as a background job (HTTP 202). */\ntype AsyncJobAccepted = { job_id: string; status: \"running\" };\n\n/** Poll response for `GET /api/canvas/jobs/{id}`. */\ntype JobStatus =\n | { status: \"running\" }\n | { status: \"completed\"; result: ExecResponse }\n | { status: \"failed\"; error: JobError };\n\nfunction isAsyncJob(res: ExecResponse | AsyncJobAccepted): res is AsyncJobAccepted {\n return typeof (res as AsyncJobAccepted).job_id === \"string\";\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\ntype JobError = { code?: string; message?: string; provider?: string; retryable?: boolean; status?: number };\n\n// A failed job goes through the SAME classifier as an HTTP error from the sync\n// path, using the status and body the backend stored, so one failure maps to one\n// kind whichever path the node took. A job failure is terminal unless the\n// backend said otherwise: the poll already waited for it, and replaying the\n// node is the engine's decision, made on `retryable`.\nfunction failedJobError(error: JobError): BackendHttpError {\n const message = error.message ?? \"the node failed on the server without saying why\";\n return new BackendHttpError(\n classifyBackendError(error.status ?? 502, { ...error, retryable: error.retryable ?? false }, message),\n );\n}\n\n/**\n * Adaptive poll cadence: hit the status row fast for the first ~15 attempts so a\n * quick node (a cache hit, a short generation) surfaces in ~1s instead of ~3s,\n * then ease to 3s so a multi-minute video job is not hammered. Attempt-indexed\n * and pure so the schedule is unit-testable.\n */\nexport function pollInterval(attempt: number): number {\n return attempt < 15 ? 1_000 : 3_000;\n}\n/** Outer safety bound before giving up polling an async job. */\nconst JOB_POLL_MAX_MS = 20 * 60 * 1000;\n\n/** A resumable run as reported by `GET /api/canvas/runs/active`. */\nexport type ActiveRunInfo = {\n runId: string;\n status: \"running\" | \"completed\" | \"failed\" | \"awaiting_approval\";\n interrupted?: boolean;\n /** The backend's verdict: an unfinished run another sandbox may take over. */\n adoptable?: boolean;\n updatedAt?: number;\n canvasSha?: string;\n canvasSnapshotUrl?: string;\n};\n\n/** The newest snapshot-bearing run for a creative — `GET /api/canvas/runs/latest`. */\nexport type SnapshotRunInfo = {\n runId: string;\n status: \"running\" | \"completed\" | \"failed\" | \"awaiting_approval\";\n interrupted?: boolean;\n canvasSha?: string;\n canvasSnapshotUrl: string;\n};\n\ntype ArtifactResponse = {\n artifact: {\n kind: string;\n name: string;\n version: string;\n description?: string;\n tags?: string[];\n slots: Record<string, RawAssetOutput | RawAssetOutput[]>;\n };\n manifest_sha: string;\n};\n\nexport class BackendClient {\n private readonly http: HttpClient;\n\n constructor(opts: {\n baseUrl: string;\n apiKey: string;\n fetchFn?: typeof fetch;\n timeoutMs?: number;\n maxRetries?: number;\n }) {\n this.http = new HttpClient(opts);\n }\n\n async exec(req: ExecRequest, signal?: AbortSignal): Promise<ExecResponse> {\n const res = await this.http.postJson<ExecResponse | AsyncJobAccepted>(\"/api/canvas/nodes/exec\", req, signal);\n // Long-running nodes (video_deconstruct) return a job id instead of running\n // synchronously, so no HTTP connection is held for their multi-minute\n // duration. Poll the cheap status endpoint until the job is terminal; each\n // poll is a fast request, so a stalled provider can never drop the work.\n if (isAsyncJob(res)) {\n return await this.pollJob(res.job_id, signal);\n }\n return res;\n }\n\n private async pollJob(jobId: string, signal?: AbortSignal): Promise<ExecResponse> {\n const deadline = Date.now() + JOB_POLL_MAX_MS;\n const path = `/api/canvas/jobs/${encodeURIComponent(jobId)}`;\n for (let attempt = 0; ; attempt++) {\n if (signal?.aborted) {\n throw new BackendHttpError({ kind: \"network\", cause: signal.reason ?? new Error(\"aborted\") });\n }\n const job = await this.http.getJson<JobStatus>(path, signal);\n if (job.status === \"completed\") return job.result;\n if (job.status === \"failed\") throw failedJobError(job.error);\n if (Date.now() > deadline) {\n throw new BackendHttpError({ kind: \"timeout\", message: `job ${jobId} did not finish in time` });\n }\n await sleep(pollInterval(attempt));\n }\n }\n\n presignAssetUpload(\n sha256: string,\n mime: string,\n signal?: AbortSignal,\n // GC-registry attribution: \"source\" marks bytes wired into COMMITTED repo\n // files (never sweepable), \"blueprint\" a portable run snapshot; omitted →\n // ordinary node output.\n purpose?: \"output\" | \"source\" | \"blueprint\",\n // Ask for an `If-None-Match: *` write, so an object already stored at this\n // content address is never overwritten. The header is SIGNED into the URL, so\n // only a caller that sends it exactly may ask; the backend echoes\n // `conditional: true` when it signed the URL that way (an older backend does\n // not, and then the header must not be sent).\n conditional?: boolean,\n ): Promise<{ putUrl: string; publicUrl: string; conditional?: boolean }> {\n return this.http.postJson<{ putUrl: string; publicUrl: string; conditional?: boolean }>(\n \"/api/canvas/assets/presign\",\n { sha256, mime, purpose, ...(conditional ? { conditional: true } : {}) },\n signal,\n );\n }\n\n /** Remote cache lookup. A miss (404) — or an old backend without the route — returns null. */\n async getCacheEntry<T>(cacheKey: string, signal?: AbortSignal): Promise<T | null> {\n try {\n const res = await this.http.getJson<{ entry: T }>(`/api/canvas/cache/${encodeURIComponent(cacheKey)}`, signal);\n return res.entry;\n } catch (e) {\n if (e instanceof BackendHttpError && \"status\" in e.detail && e.detail.status === 404) return null;\n throw e;\n }\n }\n\n async putCacheEntry(entry: { cacheKey: string }, signal?: AbortSignal): Promise<void> {\n await this.http.putJson<{ success: boolean }>(\n `/api/canvas/cache/${encodeURIComponent(entry.cacheKey)}`,\n entry,\n signal,\n );\n }\n\n /** Durable run-history record — POST /api/canvas/runs (idempotent server-side on runId). */\n async recordRun(payload: unknown, signal?: AbortSignal): Promise<void> {\n await this.http.postJson<{ success: boolean }>(\"/api/canvas/runs\", payload, signal);\n }\n\n /**\n * Resumable-run lookup — GET /api/canvas/runs/active. Returns the newest\n * still-running/interrupted run for the creative (pinned to the exact canvas\n * sha), so a fresh sandbox can adopt its run id and re-attach billed\n * in-flight jobs. Null on no active run — or an older backend without the\n * route (both 404).\n */\n async getActiveRun(creativeSlug: string, canvasSha?: string, signal?: AbortSignal): Promise<ActiveRunInfo | null> {\n const params = new URLSearchParams({ slug: creativeSlug });\n if (canvasSha) params.set(\"sha\", canvasSha);\n try {\n const res = await this.http.getJson<{ run: ActiveRunInfo }>(`/api/canvas/runs/active?${params}`, signal);\n return res.run;\n } catch (e) {\n if (e instanceof BackendHttpError && \"status\" in e.detail && e.detail.status === 404) return null;\n throw e;\n }\n }\n\n /**\n * Portable-rerun lookup — GET /api/canvas/runs/latest. The newest run for\n * the creative that recorded a canvas snapshot manifest; null when none\n * exists (or the backend predates the route — both 404).\n */\n async getLatestSnapshotRun(creativeSlug: string, signal?: AbortSignal): Promise<SnapshotRunInfo | null> {\n const params = new URLSearchParams({ slug: creativeSlug });\n try {\n const res = await this.http.getJson<{ run: SnapshotRunInfo }>(`/api/canvas/runs/latest?${params}`, signal);\n return res.run;\n } catch (e) {\n if (e instanceof BackendHttpError && \"status\" in e.detail && e.detail.status === 404) return null;\n throw e;\n }\n }\n\n /**\n * Chat-scoped blueprint sync — POST /api/creatives/definition. Lets the\n * dashboard draw a scaffolded creative's workflow graph BEFORE the first run.\n * Additive on the backend (never archives siblings, never sets definitionPath).\n */\n async syncCreativeDefinition(payload: unknown, signal?: AbortSignal): Promise<void> {\n await this.http.postJson<{ success: boolean }>(\"/api/creatives/definition\", payload, signal);\n }\n\n getArtifact(kind: string, name: string, version?: string, signal?: AbortSignal): Promise<ArtifactResponse> {\n const path = version\n ? `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}/${encodeURIComponent(version)}`\n : `/api/canvas/artifacts/${encodeURIComponent(kind)}/${encodeURIComponent(name)}`;\n return this.http.getJson<ArtifactResponse>(path, signal);\n }\n}\n","import { API_KEY_FORMAT_MESSAGE, isBakerApiKey } from \"../../apiKeyFormat.ts\";\n\ntype BackendCredentials = {\n url: string;\n apiKey: string;\n};\n\nfunction credentialsFromEnv(env: NodeJS.ProcessEnv = process.env): BackendCredentials | null {\n const url = env.BAKER_API_URL;\n const apiKey = env.BAKER_API_KEY;\n if (!url || !apiKey) return null;\n return { url, apiKey };\n}\n\nexport function requireCredentialsFromEnv(env: NodeJS.ProcessEnv = process.env): BackendCredentials {\n const c = credentialsFromEnv(env);\n if (!c) {\n throw new Error(\"missing BAKER_API_URL and/or BAKER_API_KEY environment variables\");\n }\n if (!isBakerApiKey(c.apiKey)) {\n throw new Error(`BAKER_API_KEY invalid: ${API_KEY_FORMAT_MESSAGE}`);\n }\n return c;\n}\n\n/** Remote canvas cache + durable asset persistence — on unless explicitly opted out. */\nexport function remoteCacheEnabledFromEnv(env: NodeJS.ProcessEnv = process.env): boolean {\n return env.BAKER_CANVAS_REMOTE_CACHE !== \"off\";\n}\n","export type ValidationIssue = {\n path: string;\n code: string;\n message: string;\n // \"error\" (default) blocks validate/run; \"warning\" is advisory — surfaced but\n // never fatal, so a still-runnable canvas can flag a likely-wrong-but-legal\n // configuration (a missing element reference, a scene span past a model's cap).\n severity?: \"error\" | \"warning\";\n received?: unknown;\n expected?: string;\n did_you_mean?: string[];\n node_id?: string;\n node_type?: string;\n};\n\n/** An issue is blocking unless it is explicitly a warning. */\nexport function isBlocking(issue: ValidationIssue): boolean {\n return issue.severity !== \"warning\";\n}\n\nclass CanvasError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CanvasError\";\n }\n}\n\nexport class ValidationError extends CanvasError {\n issues: ValidationIssue[];\n constructor(issues: ValidationIssue[]) {\n const head = issues[0];\n const summary = head ? `${head.code}: ${head.message}` : \"canvas validation failed\";\n super(`${issues.length} validation issue(s): ${summary}`);\n this.name = \"ValidationError\";\n this.issues = issues;\n }\n}\n\ntype NodeExecutionErrorCause =\n | { kind: \"local\"; cause: unknown }\n // `retryable` is the backend's authoritative classification: `providerErr`\n // pins every provider failure to HTTP 502 and carries real retryability in a\n // separate body field, so `status` alone can't be trusted for it.\n | { kind: \"remote\"; status: number; code: string; message: string; provider?: string; retryable?: boolean }\n | { kind: \"timeout\"; provider?: string }\n | { kind: \"network\"; cause: unknown }\n // A deterministic provider content-policy block (e.g. fal/Seedance rejecting a\n // photoreal face as a real-person likeness). NEVER retryable — it fails identically\n // on every attempt — so it surfaces as its own actionable kind, not a generic error.\n | { kind: \"content_policy\"; provider?: string; message: string };\n\nexport class NodeExecutionError extends CanvasError {\n nodeId: string;\n nodeType: string;\n cause: NodeExecutionErrorCause;\n constructor(nodeId: string, nodeType: string, cause: NodeExecutionErrorCause) {\n super(`node ${nodeId} (${nodeType}) failed: ${describeCause(cause)}`);\n this.name = \"NodeExecutionError\";\n this.nodeId = nodeId;\n this.nodeType = nodeType;\n this.cause = cause;\n }\n}\n\n/**\n * The run was deliberately aborted rather than failing on its own.\n * - \"signal\" (SIGINT/SIGTERM): terminal for this process but RESUMABLE — the\n * caller must preserve the in-flight marker so the next run re-attaches\n * in-flight billed jobs instead of re-billing them.\n */\nexport type RunAbortReason = \"signal\" | \"checkpoint\";\n\nexport class RunAbortedError extends CanvasError {\n reason: RunAbortReason;\n constructor(reason: RunAbortReason, message: string) {\n super(message);\n this.name = \"RunAbortedError\";\n this.reason = reason;\n }\n}\n\nexport type LayerFailure = { nodeId: string; reason: unknown };\n\n/** Two or more nodes of the same topological layer failed; every failure is kept. */\nexport class LayerExecutionError extends CanvasError {\n failures: LayerFailure[];\n constructor(failures: LayerFailure[]) {\n const ids = failures.map((f) => f.nodeId).join(\", \");\n super(`${failures.length} nodes failed in one layer: ${ids}`);\n this.name = \"LayerExecutionError\";\n this.failures = failures;\n }\n}\n\nexport function describeFailureReason(reason: unknown): string {\n return reason instanceof Error ? reason.message : String(reason);\n}\n\n/** A 400/422 naming no provider: the backend refused the request before any model saw it. */\nfunction isInputRejection(reason: unknown): boolean {\n if (!(reason instanceof NodeExecutionError) || reason.cause.kind !== \"remote\") return false;\n const { status, provider } = reason.cause;\n return (status === 400 || status === 422) && provider === undefined;\n}\n\n/**\n * The run envelope's error code. `input_invalid` when every failed node was refused\n * for its own input (a prompt over the model's cap) — it fails identically on every\n * run until the input changes; `runtime` for everything else.\n */\nexport function runFailureCode(e: unknown): \"input_invalid\" | \"runtime\" {\n const reasons = e instanceof LayerExecutionError ? e.failures.map((f) => f.reason) : [e];\n return reasons.length > 0 && reasons.every(isInputRejection) ? \"input_invalid\" : \"runtime\";\n}\n\n/**\n * Structured per-node failure detail persisted onto the run record so the\n * dashboard can show *why* a node died — the real upstream status/provider —\n * instead of only a red node and the layer's summary banner. Mirrors the\n * `NodeExecutionErrorCause` variants into a compact, display-ready shape.\n */\nexport type NodeFailureDetail = {\n message: string;\n /** Engine/provider error code (`server_error`, `unauthorized`, `content_policy`, `timeout`, …). */\n code?: string;\n provider?: string;\n retryable?: boolean;\n};\n\nexport function failureDetail(reason: unknown): NodeFailureDetail {\n if (reason instanceof NodeExecutionError) {\n const c = reason.cause;\n switch (c.kind) {\n case \"remote\":\n // `c.message` carries the true upstream text (e.g. `elevenlabs 502: …`).\n // Prefer the backend's authoritative `retryable` flag; only fall back to\n // status (5xx/429 transient) when it's absent, since `providerErr` masks\n // every provider failure as HTTP 502 and a bare status would then read\n // as \"retryable\" even for a non-retryable 4xx (e.g. an ElevenLabs 401).\n return {\n message: c.message,\n code: c.code,\n provider: c.provider,\n retryable: c.retryable ?? (c.status >= 500 || c.status === 429),\n };\n case \"timeout\":\n return {\n message: `timed out${c.provider ? ` (${c.provider})` : \"\"}`,\n code: \"timeout\",\n provider: c.provider,\n retryable: true,\n };\n case \"content_policy\":\n return { message: c.message, code: \"content_policy\", provider: c.provider, retryable: false };\n case \"network\":\n return {\n message: c.cause instanceof Error ? c.cause.message : \"network error\",\n code: \"network\",\n retryable: true,\n };\n case \"local\":\n return { message: c.cause instanceof Error ? c.cause.message : String(c.cause), code: \"local\" };\n default: {\n const _exhaustive: never = c;\n return { message: String(_exhaustive) };\n }\n }\n }\n return { message: describeFailureReason(reason) };\n}\n\n/**\n * What to do next, which is the only thing the status could never say.\n *\n * `providerErr` pins 502 on EVERY provider failure, so the number diagnoses\n * nothing — and an agent read it as \"the service is down\", offered to wait two\n * minutes and stopped, on a run whose provider was up the whole time.\n */\nfunction retryVerdict(retryable: boolean | undefined): string {\n if (retryable === true) return \"transient — retry the run\";\n if (retryable === false) return \"not retryable — retrying will not help, fix the input\";\n return \"retryability unknown\";\n}\n\nfunction describeCause(c: NodeExecutionErrorCause): string {\n switch (c.kind) {\n case \"local\":\n return c.cause instanceof Error ? c.cause.message : String(c.cause);\n case \"remote\":\n return `[${c.code}] ${c.message}${c.provider ? ` (${c.provider})` : \"\"} — ${retryVerdict(c.retryable)}`;\n case \"timeout\":\n return `timeout${c.provider ? ` (${c.provider})` : \"\"}`;\n case \"network\":\n return c.cause instanceof Error ? `network: ${c.cause.message}` : \"network error\";\n case \"content_policy\":\n return `CONTENT_POLICY_BLOCKED${c.provider ? ` (${c.provider})` : \"\"}: ${c.message}`;\n default: {\n const _exhaustive: never = c;\n return String(_exhaustive);\n }\n }\n}\n","/**\n * Where an ad runs → the sizes it must be delivered in, and what each placement's interface covers.\n *\n * One registry for every platform Baker publishes to, so the sizes a static ad is made in come\n * from its destination (the ad set's placements, or the platforms named) rather than a preset\n * remembered by hand, and every delivered size is checked against the interface of the\n * placement it actually runs in. Each placement cites the platform's own spec; the date the\n * numbers were last checked is recorded in `docs/marketing-intelligence.md` (Ad sizes).\n *\n * Fractions are of the frame: `safe` is the margin a design keeps at each edge, `cover` the part\n * an interface really draws over (a word there is not seen), with `rail` the column of buttons\n * down the lower right of a full-screen vertical feed. Meta's Reels and Stories numbers are\n * calibrated on hand-labelled audits (`platform-safe-area.ts` in the CLI records how); the\n * others are the platforms' published safe-zone guides, not yet calibrated.\n *\n * Pure data and pure functions.\n */\n\nexport const AD_PLATFORMS = [\n \"meta\",\n \"google_display\",\n \"google_pmax\",\n \"google_demandgen\",\n \"google_search\",\n \"youtube\",\n \"linkedin\",\n \"tiktok\",\n \"x\",\n \"reddit\",\n \"pinterest\",\n \"snapchat\",\n \"chatgpt\",\n] as const;\nexport type AdPlatform = (typeof AD_PLATFORMS)[number];\n\nexport type EdgeShares = { top: number; bottom: number; left: number; right: number };\n/** A column of buttons down the right side: `width` wide, from `from` of the height down. */\nexport type ButtonRail = { width: number; from: number };\n/** The part of the frame an interface draws over. */\nexport type InterfaceCover = { top: number; bottom: number; rail?: ButtonRail };\n\nexport type PlacementSize = { width: number; height: number; required: boolean };\n\nexport type AdPlacement = {\n id: string;\n platform: AdPlatform;\n name: string;\n sizes: readonly PlacementSize[];\n /** The margin a design keeps at each edge. */\n safe: EdgeShares;\n /** What the interface covers; absent when nothing is drawn over the picture. */\n cover?: InterfaceCover;\n /** The margin kept around `cover.rail`: an element there is close (a note), not covered. */\n railMargin?: ButtonRail;\n /** A rule the platform enforces on the picture, said where it applies. */\n rule?: string;\n source: string;\n};\n\nconst FEED_MARGIN: EdgeShares = { top: 0.04, bottom: 0.04, left: 0.04, right: 0.04 };\nconst size = (width: number, height: number, required = true): PlacementSize => ({ width, height, required });\n\nexport const AD_PLACEMENTS: readonly AdPlacement[] = [\n {\n id: \"meta_feed\",\n platform: \"meta\",\n name: \"Facebook and Instagram feeds\",\n sizes: [size(1080, 1350), size(1080, 1080, false)],\n safe: FEED_MARGIN,\n source: \"https://www.facebook.com/business/ads-guide/update/image/instagram-feed\",\n },\n {\n id: \"meta_stories\",\n platform: \"meta\",\n name: \"Facebook and Instagram Stories\",\n sizes: [size(1080, 1920)],\n safe: { top: 0.16, bottom: 0.22, left: 0.04, right: 0.04 },\n cover: { top: 0.09, bottom: 0.15 },\n source: \"https://www.facebook.com/business/ads-guide/update/image/instagram-story\",\n },\n {\n id: \"meta_reels\",\n platform: \"meta\",\n name: \"Facebook and Instagram Reels\",\n sizes: [size(1080, 1920)],\n safe: { top: 0.16, bottom: 0.37, left: 0.04, right: 0.04 },\n cover: { top: 0.09, bottom: 0.32, rail: { width: 0.08, from: 0.6 } },\n railMargin: { width: 0.1, from: 0.55 },\n source: \"https://www.facebook.com/business/ads-guide/update/image/instagram-reels\",\n },\n {\n id: \"meta_right_column\",\n platform: \"meta\",\n name: \"Facebook right column, Marketplace and search results\",\n sizes: [size(1080, 1080), size(1200, 628, false)],\n safe: FEED_MARGIN,\n source: \"https://www.facebook.com/business/ads-guide/update/image/facebook-right-column\",\n },\n {\n id: \"google_display_responsive\",\n platform: \"google_display\",\n name: \"Responsive display ads (Google Display Network)\",\n sizes: [size(1200, 628), size(1200, 1200), size(900, 1600, false)],\n safe: FEED_MARGIN,\n rule: \"Text may cover at most 20% of the image; Google crops up to 5% from each side.\",\n source: \"https://support.google.com/google-ads/answer/9823397\",\n },\n {\n id: \"google_display_banners\",\n platform: \"google_display\",\n name: \"Uploaded display banners\",\n sizes: [\n size(300, 250),\n size(728, 90),\n size(320, 50),\n size(336, 280, false),\n size(300, 600, false),\n size(160, 600, false),\n size(320, 100, false),\n size(970, 250, false),\n ],\n safe: FEED_MARGIN,\n rule: \"Each banner is exactly its pixel size, 150 KB at most.\",\n source: \"https://support.google.com/google-ads/answer/7031480\",\n },\n {\n id: \"google_pmax\",\n platform: \"google_pmax\",\n name: \"Performance Max asset group\",\n sizes: [size(1200, 628), size(1200, 1200), size(960, 1200, false)],\n safe: FEED_MARGIN,\n rule: \"Google crops up to 5% from each side; keep text and logos off the edges.\",\n source: \"https://support.google.com/google-ads/answer/13676244\",\n },\n {\n id: \"google_demandgen\",\n platform: \"google_demandgen\",\n name: \"Demand Gen (YouTube, Discover, Gmail)\",\n sizes: [size(1200, 628), size(1200, 1200), size(960, 1200, false)],\n safe: FEED_MARGIN,\n source: \"https://support.google.com/google-ads/answer/13547298\",\n },\n {\n id: \"google_search_images\",\n platform: \"google_search\",\n name: \"Image assets on Search ads\",\n sizes: [size(1200, 1200), size(1200, 628, false)],\n safe: FEED_MARGIN,\n rule: \"No overlaid text, logos or buttons on the image.\",\n source: \"https://support.google.com/google-ads/answer/12118270\",\n },\n {\n id: \"youtube_feed\",\n platform: \"youtube\",\n name: \"YouTube home and watch-next feeds\",\n sizes: [size(1200, 628), size(1200, 1200, false)],\n safe: FEED_MARGIN,\n source: \"https://support.google.com/google-ads/answer/13547298\",\n },\n {\n id: \"youtube_shorts\",\n platform: \"youtube\",\n name: \"YouTube Shorts\",\n sizes: [size(1080, 1920)],\n safe: { top: 0.12, bottom: 0.35, left: 0.05, right: 0.05 },\n cover: { top: 0.08, bottom: 0.3, rail: { width: 0.14, from: 0.45 } },\n railMargin: { width: 0.16, from: 0.4 },\n source: \"https://support.google.com/youtube/answer/10059070\",\n },\n {\n id: \"linkedin_feed\",\n platform: \"linkedin\",\n name: \"LinkedIn feed (single image)\",\n sizes: [size(1200, 1200), size(1200, 627, false), size(720, 900, false)],\n safe: FEED_MARGIN,\n source: \"https://www.linkedin.com/help/lms/answer/a426534\",\n },\n {\n id: \"tiktok_feed\",\n platform: \"tiktok\",\n name: \"TikTok For You feed\",\n sizes: [size(1080, 1920)],\n safe: { top: 0.1, bottom: 0.3, left: 0.06, right: 0.06 },\n cover: { top: 0.07, bottom: 0.25, rail: { width: 0.13, from: 0.45 } },\n railMargin: { width: 0.15, from: 0.4 },\n source: \"https://ads.tiktok.com/help/article/tiktok-ads-safe-zone\",\n },\n {\n id: \"x_timeline\",\n platform: \"x\",\n name: \"X timeline (image and website card)\",\n sizes: [size(1200, 628), size(1200, 1200, false)],\n safe: FEED_MARGIN,\n source: \"https://business.x.com/en/help/campaign-setup/creative-ad-specifications\",\n },\n {\n id: \"reddit_feed\",\n platform: \"reddit\",\n name: \"Reddit feed and conversation pages\",\n sizes: [size(1080, 1080), size(1080, 1350, false), size(1200, 628, false)],\n safe: FEED_MARGIN,\n source: \"https://business.reddithelp.com/s/article/image-ad-specs\",\n },\n {\n id: \"pinterest_feed\",\n platform: \"pinterest\",\n name: \"Pinterest home feed and search (standard pin)\",\n sizes: [size(1000, 1500), size(1000, 1000, false)],\n safe: FEED_MARGIN,\n source: \"https://help.pinterest.com/en/business/article/pinterest-product-specs\",\n },\n {\n id: \"snapchat_snap_ads\",\n platform: \"snapchat\",\n name: \"Snapchat Snap Ads\",\n sizes: [size(1080, 1920)],\n safe: { top: 0.11, bottom: 0.21, left: 0.04, right: 0.04 },\n cover: { top: 0.08, bottom: 0.18 },\n source: \"https://businesshelp.snapchat.com/s/article/single-image-video-specs\",\n },\n {\n id: \"chatgpt_card\",\n platform: \"chatgpt\",\n name: \"ChatGPT sponsored card\",\n sizes: [size(1200, 1200)],\n safe: FEED_MARGIN,\n rule: \"The card shows a square of the image; keep everything that matters inside it.\",\n source: \"https://help.openai.com/en/articles/chatgpt-ads\",\n },\n];\n\nconst RATIOS: ReadonlyArray<[string, number]> = [\n [\"1:1\", 1],\n [\"4:5\", 4 / 5],\n [\"9:16\", 9 / 16],\n [\"16:9\", 16 / 9],\n [\"1.91:1\", 1.91],\n [\"2:3\", 2 / 3],\n [\"3:4\", 3 / 4],\n [\"4:3\", 4 / 3],\n];\n\n/** A size's name: the usual ratio it is (within 1%), else its exact pixels (`728x90`). */\nexport function ratioLabel(width: number, height: number): string {\n const r = width / height;\n const known = RATIOS.find(([, value]) => Math.abs(Math.log(r / value)) < 0.01);\n return known ? known[0] : `${width}x${height}`;\n}\n\nexport type DeliveredFormat = {\n /** A ratio (`4:5`) the static pipeline makes, or exact pixels (`728x90`) for a fixed banner. */\n format: string;\n width: number;\n height: number;\n /** The placements that run this size. */\n placements: string[];\n};\n\nexport type DeliverySet = {\n /** The required ratios, in the order first met: made from the one chosen picture. */\n formats: DeliveredFormat[];\n /** Fixed-pixel banners the destination requires, made apart (a picture re-laid out cannot reach 728x90). */\n banners: DeliveredFormat[];\n /** Names that are no platform here. */\n unknown: string[];\n};\n\n/** Every placement of these platforms, or the placements named themselves. */\nexport function placementsFor(names: readonly string[]): { placements: AdPlacement[]; unknown: string[] } {\n const placements: AdPlacement[] = [];\n const unknown: string[] = [];\n for (const raw of names) {\n const name = raw.trim().toLowerCase();\n if (!name) continue;\n const found = AD_PLACEMENTS.filter((p) => p.platform === name || p.id === name);\n if (found.length === 0) unknown.push(raw.trim());\n for (const p of found) if (!placements.includes(p)) placements.push(p);\n }\n return { placements, unknown };\n}\n\n/** Adds one placement's size to the list it belongs in, merged with the same format already there. */\nfunction addSize(into: DeliveredFormat[], format: string, size: PlacementSize, placement: string): void {\n const existing = into.find((f) => f.format === format);\n if (!existing) {\n into.push({ format, width: size.width, height: size.height, placements: [placement] });\n return;\n }\n if (!existing.placements.includes(placement)) existing.placements.push(placement);\n if (size.width > existing.width) Object.assign(existing, { width: size.width, height: size.height });\n}\n\n/** The required sizes of every placement the destination runs in, unioned by format. */\nexport function deliverySet(names: readonly string[]): DeliverySet {\n const { placements, unknown } = placementsFor(names);\n const formats: DeliveredFormat[] = [];\n const banners: DeliveredFormat[] = [];\n for (const placement of placements) {\n for (const s of placement.sizes.filter((x) => x.required)) {\n const format = ratioLabel(s.width, s.height);\n addSize(format.includes(\"x\") ? banners : formats, format, s, placement.id);\n }\n }\n return { formats, banners, unknown };\n}\n\n/**\n * What the interfaces of these vertical placements cover together: the larger share at each\n * edge, the wider and longer button rail. Null when none of them draws over the picture.\n */\nexport function verticalCover(\n placementIds: readonly string[],\n): (InterfaceCover & { safe: EdgeShares; railMargin?: ButtonRail }) | null {\n const covering = AD_PLACEMENTS.filter((p) => placementIds.includes(p.id) && p.cover);\n if (covering.length === 0) return null;\n const max = (pick: (p: AdPlacement) => number | undefined) => Math.max(...covering.map((p) => pick(p) ?? 0));\n const rails = covering.filter((p) => p.cover?.rail);\n const margins = covering.filter((p) => p.railMargin);\n return {\n top: max((p) => p.cover?.top),\n bottom: max((p) => p.cover?.bottom),\n ...(rails.length > 0\n ? {\n rail: {\n width: max((p) => p.cover?.rail?.width),\n from: Math.min(...rails.map((p) => p.cover?.rail?.from ?? 1)),\n },\n }\n : {}),\n ...(margins.length > 0\n ? {\n railMargin: {\n width: max((p) => p.railMargin?.width),\n from: Math.min(...margins.map((p) => p.railMargin?.from ?? 1)),\n },\n }\n : {}),\n safe: {\n top: max((p) => p.safe.top),\n bottom: max((p) => p.safe.bottom),\n left: max((p) => p.safe.left),\n right: max((p) => p.safe.right),\n },\n };\n}\n","/**\n * Ad-platform → aspect-ratio presets for the `image_aspect_adapt` node.\n *\n * A creative is authored at ONE base ratio; fanning it out to every placement\n * a platform expects is mechanical, but the ratio set is easy to misremember.\n * These presets name the platform once and expand to the canonical set, so the\n * agent (and the node) never hand-list ratios from memory.\n *\n * Only **on-grid** ratios (a subset of the node's `ASPECT_ADAPT_FORMATS`) are\n * emitted — the node renders those directly, and formats that already match the\n * source ratio pass through free. Off-grid targets a platform also accepts\n * (e.g. Google's 1.91:1 landscape) are recorded in `offGrid` for a follow-up\n * `baker images crop` pass; they are NOT rendered by the adapt node.\n *\n * Shared by the CLI node (cost estimate) and the backend handler (expansion),\n * so both price and run a `platform` request identically.\n */\n\nexport const AD_FORMAT_PLATFORMS = [\n \"meta_feed\",\n \"meta_feed_stories\",\n \"meta_all\",\n \"linkedin\",\n \"google_search\",\n \"google_demandgen\",\n \"tiktok\",\n \"youtube\",\n] as const;\n\nexport type AdFormatPlatform = (typeof AD_FORMAT_PLATFORMS)[number];\n\nexport type AdPlatformPreset = {\n /** On-grid target ratios the adapt node renders (source-matching ones pass through free). */\n readonly formats: readonly string[];\n /** Ratios the platform also accepts that the node can't render directly — crop from the nearest rendered format. */\n readonly offGrid?: readonly string[];\n /** One-line, agent-facing description of the set. */\n readonly note: string;\n};\n\nexport const AD_PLATFORM_FORMAT_PRESETS: Record<AdFormatPlatform, AdPlatformPreset> = {\n meta_feed: { formats: [\"4:5\"], note: \"Meta/Instagram feed static.\" },\n meta_feed_stories: { formats: [\"4:5\", \"9:16\"], note: \"Meta feed + Stories/Reels.\" },\n meta_all: { formats: [\"4:5\", \"9:16\", \"1:1\"], note: \"Meta feed + Stories/Reels + square.\" },\n linkedin: { formats: [\"1:1\"], note: \"LinkedIn single-image feed (square).\" },\n google_search: {\n // Search image assets: 1:1 is the required one, 1.91:1 the optional landscape.\n // 16:9 is rendered only as the crop source for that landscape — Search never serves 16:9 itself.\n formats: [\"1:1\", \"16:9\"],\n offGrid: [\"1.91:1\"],\n note: \"Google Search image assets (the image extension on a text ad); 1:1 required, 1.91:1 landscape — crop that from the rendered 16:9. Stage 3+ per campaign.\",\n },\n google_demandgen: {\n // Renders 16:9 too so the off-grid 1.91:1 landscape has a native source to crop from.\n formats: [\"1:1\", \"4:5\", \"16:9\"],\n offGrid: [\"1.91:1\"],\n note: \"Google Demand Gen; also wants 1.91:1 landscape — crop that from the rendered 16:9.\",\n },\n tiktok: { formats: [\"9:16\"], note: \"TikTok full-screen vertical.\" },\n youtube: { formats: [\"16:9\"], note: \"YouTube in-stream / companion display.\" },\n};\n\n/** The canonical on-grid ratio set the node should render for a platform. */\nexport function platformFormats(platform: AdFormatPlatform): readonly string[] {\n return AD_PLATFORM_FORMAT_PRESETS[platform].formats;\n}\n\n/**\n * The target ratios an `image_aspect_adapt` node renders: a `platform` preset\n * expanded to its canonical set, or the explicit `formats` list. The CLI (cost\n * estimate) and the backend (dispatch) both resolve through this one function\n * so they price and run a `platform` request identically.\n */\nexport function resolveAdaptFormats(params: {\n formats?: readonly string[];\n platform?: AdFormatPlatform;\n}): readonly string[] {\n if (params.platform) return platformFormats(params.platform);\n return params.formats ?? [];\n}\n","import cjsModule from '../index.js'\n\nexport const configure = cjsModule.configure\n\nexport const stringify = cjsModule\nexport default cjsModule\n","import stableStringify from \"safe-stable-stringify\";\n\n/**\n * Deterministic JSON encoding for cache keys.\n *\n * Rules (locked — drift breaks all caches):\n * 1. Object keys sorted alphabetically (codepoint order, locale-independent).\n * 2. No whitespace.\n * 3. `undefined` values and entries are dropped (not serialized as null).\n * 4. Strings are NFC-normalized before encoding.\n * 5. Numbers use the shortest representation (V8 default; we don't post-process).\n * 6. `NaN` and `±Infinity` throw — explicit reject, never silently encode.\n * 7. Arrays preserve order.\n */\nexport function canonicalize(value: unknown): string {\n const normalized = normalizeForCanonical(value);\n const out = stableStringify(normalized);\n if (out === undefined) {\n throw new Error(\"canonicalize: value serialized to undefined (top-level undefined?)\");\n }\n return out;\n}\n\nfunction normalizeForCanonical(value: unknown): unknown {\n if (value === null) return null;\n if (typeof value === \"string\") return value.normalize(\"NFC\");\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw new Error(`canonicalize: non-finite number ${value}`);\n }\n return value;\n }\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"bigint\") {\n throw new Error(\"canonicalize: bigint not supported\");\n }\n if (Array.isArray(value)) {\n return value.map(normalizeForCanonical);\n }\n if (typeof value === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n if (v === undefined) continue;\n out[k.normalize(\"NFC\")] = normalizeForCanonical(v);\n }\n return out;\n }\n // undefined, function, symbol — drop\n return undefined;\n}\n","/**\n * Take the cast's NAME out of the prompts, leaving the person in.\n *\n * Gemini refuses \"real people's names or likenesses\" — one filter covering both — and an\n * agent writes prompts the way a person would: \"Carmen on camera, relaxed and\n * reassuring\". Observed on a real ad: five clips rendered, the sixth came back\n * CONTENT_POLICY_BLOCKED, and the only thing separating them was luck, because every one\n * of them named her.\n *\n * The name buys the model nothing. The face arrives as a reference image and the identity\n * sheet carries the description; the name is decoration that happens to trip a filter. So\n * it is replaced rather than removed — a brief that reads \"on camera, relaxed\" with no\n * subject at all is a worse prompt than one that says who is in shot.\n *\n * Whole words only, and case-insensitively: \"Carmen\" goes, \"carmengarcia\" stays.\n */\nexport function withoutCastName(text: string, names: readonly string[]): string {\n let out = text;\n for (const name of names) {\n const token = name.trim();\n if (token.length < 3) continue;\n const pattern = new RegExp(`(?<![\\\\p{L}\\\\p{N}])${escapeForPattern(token)}(?![\\\\p{L}\\\\p{N}])`, \"giu\");\n out = out.replace(pattern, \"the person in the reference\");\n }\n return out;\n}\n\nfunction escapeForPattern(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","/**\n * Whether a clip will be small enough for us to ingest.\n *\n * A finished clip is fetched and held in memory to be hashed and uploaded, inside a\n * Convex action capped at 512 MB. Ask for one big enough and the action dies with\n * \"ran out of memory\" — after the provider has rendered and charged for it, and the\n * user is told \"this one didn't come back, run it again\", which retries into the same\n * wall. Observed on the first 30s/1080p clip anyone rendered, which is a combination\n * that only became possible when Wan 3.0 was added.\n *\n * The rate is measured across three real clips at 720p, and it moves a lot with the\n * content: 8s/9.5 MB (1.19 MB/s), 15s/19.0 MB (1.27), 15s/31.9 MB (2.13). The worst of\n * them is the one used, because underestimating is what produces the crash this exists\n * to prevent — a busy 30s shot is not an unusual ad, it is the normal one.\n */\n\n/** MB per second at 720p — the heaviest of three measured clips, not the average. */\nconst MB_PER_SECOND_720P = 2.13;\n\n/** Pixel count relative to 720p, which sets how the rate scales. */\nconst RESOLUTION_WEIGHT: Readonly<Record<string, number>> = {\n \"360p\": 0.25,\n \"480p\": 0.44,\n \"720p\": 1,\n \"1080p\": 2.25,\n \"4k\": 9,\n \"4K\": 9,\n};\n\n/**\n * What one action can hold.\n *\n * Convex caps an action at 512 MB and the file is live more than once — the fetched body\n * and the upload body at least — so the ceiling on the FILE is well under half of that.\n * 120 MB refuses the 30s/1080p clip that actually crashed (≈144 MB at the measured worst\n * rate) while leaving 30s at 720p and 15s at 1080p alone, which is where real ads sit.\n *\n * This is a stopgap with a number in it. The real fix is ingesting without holding the\n * file at all, and then this constant goes away.\n */\nexport const MAX_CLIP_INGEST_MB = 120;\n\n/** Roughly how many MB a clip of this shape will weigh. */\nexport function estimateClipMb(durationSeconds: number, resolution: string | undefined): number {\n const weight = RESOLUTION_WEIGHT[resolution ?? \"720p\"] ?? 1;\n return durationSeconds * MB_PER_SECOND_720P * weight;\n}\n\n/** The refusal, or null when the clip is small enough to ingest. */\nexport function clipTooHeavyToIngest(\n durationSeconds: number | undefined,\n resolution: string | undefined,\n): string | null {\n if (!durationSeconds) return null;\n const mb = estimateClipMb(durationSeconds, resolution);\n if (mb <= MAX_CLIP_INGEST_MB) return null;\n const fits = Math.floor(MAX_CLIP_INGEST_MB / (estimateClipMb(1, resolution) || 1));\n return `A ${durationSeconds}s clip at ${resolution ?? \"720p\"} comes to roughly ${Math.round(mb)} MB, more than the ${MAX_CLIP_INGEST_MB} MB we can take in — it would render, bill, and then fail on the way into the library. Shorten it to about ${fits}s at this resolution, or drop the resolution and keep the length.`;\n}\n","/**\n * What a generated frame is not allowed to invent.\n *\n * Every clause here was bought with a render. They lived in the ad scaffolder, which is\n * where they were learned — and that is exactly why a storyboard generated through Studio\n * came back with an electricity bill printed backwards and a customer resting her elbows on\n * the solar panel she was supposed to be praising. The rules existed; the route around them\n * did not know about them. So they live here, in the contract both routes already import,\n * rather than in whichever one happened to hit the defect first.\n *\n * Pure string composition: no model, no request, nothing to mock.\n */\nexport function frameRealismDirection(\n opts: { currency?: string; role?: string; readableText?: \"allowed\" | \"forbidden\" } = {},\n): string {\n return (\n \" Everything obeys real-world physics: paper, card and screens are OPAQUE with nothing showing through from behind,\" +\n \" every object is at believable real-world scale next to the people handling it, and every object has its\" +\n \" real-world form and construction — a phone has ONE screen and it is on the front.\" +\n // The worst of the three failures behind this, because the viewer reads the number: a\n // model invents one whenever a screen or a document is in shot, and it contradicts the\n // script it sits under. Frames simply do not get legible text; the captions carry the\n // numbers.\n //\n // Default ON, and asked for explicitly rather than inferred: an ad frame with no\n // person in it still sits under a script for an invented number to argue with. Only a\n // plain Studio picture opts out — it has no script, and it may BE the poster somebody\n // asked for, which the model picker sells GPT Image 2.5 on being able to render.\n (opts.readableText === \"allowed\"\n ? \"\"\n : \" NO readable text or numbers anywhere in frame — phone screens, documents and signage stay illegible or out of\" +\n \" focus, because any figure the model invents will contradict the script.\") +\n // Measured: one finished ad reported malformed hands or limbs in 8 of 12 sampled frames;\n // another, a woman with an extra right arm and an unattached hand gripping a panel.\n // Fiddly work at close range is where a video model breaks, every time.\n \" Hands are kept simple: no close-up of fingers manipulating small parts, no hand gripping the edge of an object,\" +\n \" and each person has exactly TWO arms and TWO legs, all attached and all visible or all out of frame.\" +\n \" Anyone working does so the way the trade actually does it: nobody stands or kneels on the equipment being\" +\n \" installed, nothing is fitted overhanging an edge or floating unsupported, and every part rests on the\" +\n \" structure that would really carry it.\" +\n // The newest clause, and the one no route had. A testimonial came back with the customer\n // repairing the panel herself: nothing said she was not the installer, so the model cast\n // her as one. Who someone IS in the picture has to be stated, or it is guessed.\n (opts.role\n ? ` The person on camera is ${opts.role} — they are shown as that and never doing somebody else's job.`\n : \"\") +\n (opts.currency ? ` If a currency is unavoidably visible it is ${opts.currency}.` : \"\")\n );\n}\n","/**\n * The one conversion between provider dollars and Baker credits.\n *\n * Lives in its own module, imported by both `videoCost.ts` and `nodeCost.ts`,\n * so neither has to import the other for it (a cycle there leaves the constant\n * uninitialised for whichever module loads second).\n */\n\n/** Credits per USD of provider cost. The backend ledger reads this same constant. */\nexport const CREDITS_PER_USD = 100;\n\n/**\n * Whole credits for a USD amount: rounded UP, so a quote or a reported charge is\n * never under what the ledger records.\n *\n * The snap to 6 decimals first matters: `0.112 * 5 * 100` is 56.00000000000001\n * in IEEE-754, and a bare ceil turns an exact 56 into 57.\n *\n * The ledger itself stores exact fractional credits (`credits/utils.ts`), so this\n * is the rule for every WHOLE-credit figure: the engine's pre-run quote and the\n * `credits_charged` a node execution reports back.\n */\nexport function usdToCredits(usd: number): number {\n if (!(usd > 0)) return 0;\n return Math.ceil(Number((usd * CREDITS_PER_USD).toFixed(6)));\n}\n","/**\n * Model registry — single source of truth for per-model capabilities.\n *\n * Curated for the ad-creative use case. The canvas-engine runs inside the\n * controlled E2B sandbox (see `packages/e2b-template/src/template.ts`); we\n * expose a small, opinionated set of models rather than the full provider\n * catalogue. Param names here are CANONICAL — one name per concept across\n * all models. Provider-specific shapes get translated at the backend adapter\n * layer.\n *\n * Consumed by:\n * - the engine's pre-run validator (`validateModelParams`)\n * - the dashboard model picker (param controls + input slot icons)\n * - the backend handlers (defence-in-depth, when wired)\n */\n\nimport { AD_FORMAT_PLATFORMS } from \"./adPlatformFormats\";\n\nexport type ParamSchema =\n | { kind: \"string\"; enum?: readonly string[]; maxLength?: number }\n | { kind: \"number\"; enum?: readonly number[]; min?: number; max?: number; integer?: boolean }\n | { kind: \"boolean\" }\n | { kind: \"json\" };\n\nexport type InputKind = \"image\" | \"video\" | \"audio\";\n\n/**\n * A single input slot the model can consume. `mimes` is the set of MIME types\n * the provider accepts for that kind — used by Stage 3 pre-flight MIME\n * enforcement (a mime mismatch between source output and sink accepted set is\n * a `mime_not_accepted` validation error).\n */\ntype InputSpec = {\n kind: InputKind;\n mimes?: readonly string[];\n /**\n * How many assets the provider accepts in this slot. Read off the live\n * schemas, where the spread is wide and consequential: gpt-image-2 takes 16\n * references, the Gemini image models 14, and Recraft exactly ONE. A caller who\n * wires four references to Recraft is not slightly over — they get a\n * pre-dispatch 400 for every take in the batch.\n *\n * Absent = the provider advertises no cap we have confirmed. Absent means\n * UNGATED, never \"one\": guessing a ceiling refuses renders that would have\n * worked, and a refusal you invented is worse than a limit you never had.\n */\n max?: number;\n};\n\n/** Who serves a model. `google-interactions` is Google's own API (Omni), not OpenRouter. */\nexport type ModelProvider =\n | \"openrouter\"\n | \"replicate\"\n | \"google-interactions\"\n | \"elevenlabs\"\n | \"fal\"\n | \"groq\"\n | \"deepgram\";\n\n/** A provider and the id the model goes by on that provider's wire. */\nexport type ProviderRoute = { provider: ModelProvider; wireModel: string };\n\n/** A route as written in the registry: an absent `wireModel` means the registry id IS the wire id. */\ntype RouteSpec = { provider: ModelProvider; wireModel?: string };\n\n/** Published $/second for a clip, silent and with sound, with per-resolution overrides. */\nexport type VideoSecondRate = {\n silent: number;\n audio: number;\n byResolution?: Readonly<Record<string, { silent: number; audio: number }>>;\n};\n\n/**\n * Pricing that lives in a formula rather than in a number on the entry: a token\n * count derived from the frame (Seedance, Omni) or a per-unit rate on units only the\n * provider measures (ElevenLabs characters, lipsync minutes). The formula itself is\n * in `videoCost.ts` / `nodeCost.ts`; naming it here is what makes a model's price\n * findable from its entry.\n */\nexport type BillingFormula =\n | \"seedance-2.0\"\n | \"seedance-2.5\"\n | \"gemini-omni\"\n | \"elevenlabs-characters\"\n | \"elevenlabs-music-seconds\"\n | \"elevenlabs-sound-effect-seconds\"\n | \"elevenlabs-voice-convert-minutes\"\n | \"lipsync-minutes\"\n | \"video-background-remove-frames\"\n | \"image-background-remove-call\"\n | \"transcription\";\n\n/**\n * How a model is priced. `usage` is explicit, not a missing price: the provider\n * reports what the call cost afterwards (every OpenRouter text, vision and image\n * call), so there is no number to quote up front.\n */\nexport type ModelBilling =\n | { kind: \"usage\" }\n | {\n kind: \"per_second\";\n usd: VideoSecondRate;\n /** Seconds billed however short the clip is, where the provider bills a floor. */\n minBilledSeconds?: number;\n }\n | { kind: \"formula\"; formula: BillingFormula };\n\nconst OPENROUTER_ROUTE: RouteSpec = { provider: \"openrouter\" };\nconst USAGE_BILLED: ModelBilling = { kind: \"usage\" };\n\n/**\n * Some providers bill a floor regardless of how short the clip is. Measured,\n * not published: a 4s Wan 2.7 clip billed $0.50 against a $0.10/s rate, and a 3s\n * HappyHorse clip billed $0.494 against $0.0988/s, both exactly five seconds'\n * worth. Declared per model (`minBilledSeconds`), because it was only OBSERVED on\n * the two Alibaba models.\n */\nexport const MIN_BILLED_SECONDS = 5;\n\n/**\n * The fal route Seedance clips are re-rendered on when ByteDance's face filter\n * refuses a reference-guided one: Seedance 2.5 under a licence that takes faces,\n * whichever Seedance tier was asked for. See `convex/lib/falSeedance.ts`.\n */\nexport const SEEDANCE_FACE_RECOVERY_ROUTE: ProviderRoute = {\n provider: \"fal\",\n wireModel: \"bytedance/seedance-2.5/reference-to-video\",\n};\n\ntype ModelSpec = {\n /** Human label shown in the model picker. */\n label: string;\n /** The provider that serves this model and its id there. */\n route: RouteSpec;\n /** How a call is priced; see `ModelBilling`. */\n billing: ModelBilling;\n /** A second route the backend falls back to for a refusal the first route cannot serve. */\n recoveryRoute?: ProviderRoute;\n /** Input slots the canvas MUST wire up for this model to run. */\n inputs: readonly InputSpec[];\n /**\n * Input slots the model can use if provided but does not require. The UI\n * shows icons for these as drop-targets; the validator does not require\n * them.\n */\n optional_inputs?: readonly InputSpec[];\n /** Canonical param names that MUST be present. */\n required: readonly string[];\n /** Allowed params keyed by canonical name + per-param schema. */\n params: Readonly<Record<string, ParamSchema>>;\n};\n\nexport type NodeKind =\n | \"text_generate\"\n | \"image_aspect_adapt\"\n | \"image_describe\"\n | \"image_generate\"\n | \"image_reference_sheet\"\n | \"image_select\"\n | \"video_generate\"\n | \"video_deconstruct\"\n | \"video_lipsync\"\n | \"tts\"\n | \"audio_voice_convert\"\n | \"music\"\n | \"dialogue\"\n | \"sound_effect\";\n\n// ---------------------------------------------------------------------------\n// Shared enums\n// ---------------------------------------------------------------------------\n\nconst OPENROUTER_IMAGE_AR = [\"1:1\", \"2:3\", \"3:2\", \"3:4\", \"4:3\", \"4:5\", \"5:4\", \"9:16\", \"16:9\", \"21:9\"] as const;\nconst OPENROUTER_IMAGE_AR_EXTREME = [...OPENROUTER_IMAGE_AR, \"1:4\", \"4:1\", \"1:8\", \"8:1\"] as const;\n\n// gpt-image-2 accepts a NARROWER ratio set than every other image model here —\n// notably no 4:5 or 5:4, which are the Meta/Instagram feed portrait formats.\n// OpenRouter rejects an unlisted ratio with a pre-dispatch 400\n// (\"aspect_ratio: not supported. Accepted: 1:1, 3:2, 2:3, 4:3, 3:4, 16:9, 9:16,\n// 21:9, auto\"), so portrait ad formats must come from `image_aspect_adapt` on a\n// Gemini model rather than straight out of gpt-image-2.\n// Source: GET /api/v1/images/models/openai/gpt-image-2/endpoints.\nconst OPENROUTER_IMAGE_AR_GPT = [\"1:1\", \"2:3\", \"3:2\", \"3:4\", \"4:3\", \"9:16\", \"16:9\", \"21:9\"] as const;\n\n// ---------------------------------------------------------------------------\n// Hard provider ceilings.\n//\n// Every number below is quoted from the provider's own schema, not from a blog\n// post or from memory, and each one is a failure we would otherwise only find by\n// paying for it: an over-length prompt is a 4xx AFTER the batch is open and the\n// tiles are spinning, which reads to the user as \"it broke, try again\" and gets\n// retried identically. Declared here, the same request is refused for free with\n// the number to cut to.\n//\n// Sources (re-check these before editing a number):\n// OpenAI openai/openai-openapi `CreateImageRequest.prompt` — \"32000\n// characters for the GPT image models\".\n// Recraft API appendix — 10,000 characters for the V4/V4.1 family (the 1,000\n// everyone quotes is the retired V2/V3 limit).\n// Replicate GET /v1/models/bytedance/seedance-2.0 → `prompt.maxLength: 4000`.\n// Kling API reference — prompt and negative_prompt cap at 2,500 each.\n// Vertex Veo reference — `seed` is a uint32, \"accepted range 0-4294967295\".\n// ---------------------------------------------------------------------------\nconst GPT_IMAGE_PROMPT_MAX = 32_000;\nconst RECRAFT_PROMPT_MAX = 10_000;\nconst SEEDANCE_PROMPT_MAX = 4_000;\nconst KLING_PROMPT_MAX = 2_500;\n/** Vertex (Veo) and BytePlus (Seedance 2.5) both type `seed` as a uint32. */\nconst UINT32_MAX = 4_294_967_295;\nconst VEO_SEED_MAX = UINT32_MAX;\n\n// Reference-image ceilings, from `input_references` on each model's live\n// endpoint schema (GET /api/v1/images/models/{id}/endpoints) and from\n// Replicate's `reference_images` description for Seedance.\nconst GPT_IMAGE_MAX_REFERENCES = 16;\nconst GEMINI_IMAGE_MAX_REFERENCES = 14;\nconst RECRAFT_MAX_REFERENCES = 1;\nconst SEEDANCE_MAX_REFERENCES = 9;\n\nconst OPENROUTER_IMAGE_SIZE = [\"1K\", \"2K\", \"4K\"] as const;\n/**\n * `0.5K` is our CANONICAL spelling of the half-size tier; OpenRouter's wire enum\n * calls it `512`, and the backend adapter translates it in `buildImageGenBody`.\n * Sending `0.5K` straight through is a request-schema 400, so the translation is\n * not optional — but the canonical spelling stays `0.5K` so canvases already\n * authored against it keep running.\n *\n * Exported so the engine node schemas and the studio param schema build their\n * enums from this rather than hand-copying the list a fifth time.\n */\nexport const OPENROUTER_IMAGE_SIZES = [\"0.5K\", ...OPENROUTER_IMAGE_SIZE] as const;\nconst OPENROUTER_IMAGE_SIZE_EXTENDED = OPENROUTER_IMAGE_SIZES;\n\n// Nano Banana 2 Lite renders at ONE size. `GET /api/v1/images/models/\n// google/gemini-3.1-flash-lite-image/endpoints` reports `resolution: [\"1K\"]` on\n// both its Google Vertex and Google AI Studio endpoints, and asking for 2K or 4K\n// is a hard 400 (\"resolution: not supported. Accepted: 1K\") — verified live.\nconst GEMINI_LITE_IMAGE_SIZE = [\"1K\"] as const;\n\n// OpenRouter's unified image API forwards a `quality` knob. Only gpt-image actually\n// honors it: OpenRouter's per-endpoint schema lists it for gpt-image and NOT for the\n// Gemini image models, which return an identically-priced 200 whether it is sent or\n// not. So it is declared only on the models that use it — an inert knob elsewhere.\nconst OPENROUTER_IMAGE_QUALITY = [\"auto\", \"low\", \"medium\", \"high\"] as const;\n\n// gpt-image-2.5 is the same model surface as gpt-image-2 in every respect but this: the\n// quality ladder gained two rungs above `high`. Same ratios, same 16 references, same\n// per-token price. Note the price is per OUTPUT TOKEN, so the rungs are not free —\n// a `max` render bills more tokens than a `high` one at an identical rate.\n// Source: GET /api/v1/images/models/openai/gpt-image-2.5-sunburst/endpoints.\nconst OPENROUTER_IMAGE_QUALITY_25 = [\"auto\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\n// Images from ONE call: one request and one wait for N candidates of the same brief. Not\n// cheaper: measured on 2026-09-30, gpt-image-2.5 at n=3 billed exactly 3x n=1, the prompt\n// tokens tripled too. Declared only where a live call proved the model honours it: the\n// Gemini image models refuse it outright (\"n: must be exactly 1\"), and a model that\n// silently returned one image would leave a pick with nothing to choose. OpenRouter's\n// ceiling is 10. Read it through `maxImagesPerCall`.\n// Source: https://openrouter.ai/docs/guides/overview/multimodal/image-generation;\n// probes in scripts/creative-evals/.out/probes (n3-*.json).\nconst OPENROUTER_IMAGE_N: ParamSchema = { kind: \"number\", integer: true, min: 1, max: 10 };\n\n// Recraft renders a NARROWER ratio set than the rest of the image roster, and\n// notably has no 4:5 / 5:4 — the Meta and Instagram feed portrait formats. It was\n// declared here with the full OpenRouter set, so asking the vector model for the\n// single most common portrait ad shape was a pre-dispatch 400 on every take.\n// Portrait vector work has to snap to 3:4 (which it now does) or be adapted\n// afterwards through `image_aspect_adapt`.\n// Source: GET /api/v1/images/models/recraft/recraft-v4.1-pro-vector/endpoints.\nconst RECRAFT_IMAGE_AR = [\"1:1\", \"4:3\", \"3:4\", \"16:9\", \"9:16\"] as const;\n\n// Seedance renders EVERY whole second from 4 to 15 — no gaps.\n//\n// Both sources agree: Replicate's schema declares `duration` as an integer with\n// `minimum: -1, maximum: 15` (-1 meaning \"you pick\"), and OpenRouter's catalogue\n// reports `supported_durations: [4…15]`. Take the set from them and nowhere else.\n// A gate stricter than the provider costs no 400 to reveal it, so it silently\n// narrows what the product can make until someone reads the schema.\nexport const SEEDANCE_DURATIONS = [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as const;\n\n// ---------------------------------------------------------------------------\n// Seedance 2.5 — the long-form, multimodal-reference tier.\n//\n// A DIFFERENT model from 2.0 in every dimension that matters here, and reached a\n// different way: 2.0 is routed through Replicate, 2.5 through OpenRouter's video\n// gateway (provider `seed`), where it is a first-class entry. Every number below\n// is read off `GET /api/v1/videos/models → bytedance/seedance-2.5`, not from the\n// 2.0 entry beside it — they disagree on three of the four gated sets.\n//\n// The trades against 2.0, so a caller can pick on purpose:\n// - LENGTH: every whole second 4-30, against 2.0's 4-15. This is the reason to\n// reach for it — a 30s clip is a whole ad, not a beat.\n// - REFERENCES: up to 50 assets, and they are not all images — 30 images, 10\n// videos, 10 audio. Video references are what make edit/extend possible at\n// all, and no other curated model takes a non-image reference.\n// - RESOLUTION: 480p/720p ONLY. 2.0 goes to 1080p and 4K; 2.5 does not on this\n// route (the higher tiers are a separate upscale product), so a hero beat\n// that must be 1080p still belongs on 2.0 or Veo.\n// - SHAPE: no 9:21. 2.0 has it, 2.5 stops at 21:9.\n// ---------------------------------------------------------------------------\n\n/** Every whole second from 4 to 30 — `supported_durations`, no gaps. */\nexport const SEEDANCE_25_DURATIONS = [\n 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,\n] as const;\n\n/** `supported_aspect_ratios`. Narrower than 2.0's — no 9:21. */\nexport const SEEDANCE_25_ASPECT_RATIOS = [\"1:1\", \"3:4\", \"9:16\", \"4:3\", \"16:9\", \"21:9\"] as const;\n\n/**\n * `supported_resolutions`. Deliberately NOT widened to 1080p/4K: those are real\n * Seedance tiers but they are not on this route, and asking for one is a\n * provider 400 after the batch is open.\n */\nexport const SEEDANCE_25_RESOLUTIONS = [\"480p\", \"720p\"] as const;\n\n/**\n * The multimodal reference budget: 50 assets total, split by kind.\n *\n * Per-kind rather than one number, because the provider enforces them\n * separately — eleven audio clips is refused even with nothing else wired. The\n * split is BytePlus's own published Omni Reference limit and matches the model\n * description's \"up to 50 image, video, and audio reference assets\".\n */\nexport const SEEDANCE_25_MAX_IMAGE_REFERENCES = 30;\nexport const SEEDANCE_25_MAX_VIDEO_REFERENCES = 10;\nexport const SEEDANCE_25_MAX_AUDIO_REFERENCES = 10;\n\n// Kling accepts ANY whole second from 3 to 15 (OpenRouter\n// GET /api/v1/videos/models), gaps included.\nexport const KLING_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as const;\n\n// ---------------------------------------------------------------------------\n// Gemini Omni Flash — length is PROMPTED, not parameterised.\n//\n// Omni takes no `duration` field anywhere in its request (every spelling, at\n// every nesting level, is rejected with `Unknown parameter`). It does honour a\n// length asked for IN THE PROMPT: verified live, \"exactly 3 seconds\" returned\n// 3.008s and \"exactly 6 seconds\" returned 6.016s, against a 10.005s default when\n// the prompt names no length.\n//\n// So `duration` IS a canvas param for this model — the backend adapter translates\n// it into a prompt directive instead of a body field, exactly the canonical-name /\n// provider-shape split this registry is built on.\n// ---------------------------------------------------------------------------\n\n/**\n * Clip lengths Omni renders. Outside this range it does NOT fail — it silently\n * snaps (2s came back 3.008s, 15s came back 10.005s), which is worse than a\n * rejection: you are billed for the length you got, not the one you asked for.\n * Gate it here so the snap can't happen unnoticed.\n */\nexport const GEMINI_OMNI_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10] as const;\n\n/** What Omni renders when the prompt names no length — its ceiling, not its floor. */\nexport const GEMINI_OMNI_DEFAULT_SECONDS = 10;\n\n/** The only aspect ratios Omni accepts; anything else is a pre-dispatch 400. */\nexport const GEMINI_OMNI_ASPECT_RATIOS = [\"16:9\", \"9:16\"] as const;\n\n/**\n * The resolutions Omni 1.1 renders, cheapest first.\n *\n * **1.1 ONLY.** This is the capability that separates the two Omni entries, and\n * the reason they ARE two entries rather than one rolling key: the preview\n * (`google/gemini-omni-flash`) has no resolution knob at all and renders every\n * clip at 720p, so a canvas asking it for 360p is asking for something it does\n * not do. Do NOT add this enum to the legacy entry to \"keep them consistent\" —\n * the inconsistency is the fact being recorded.\n *\n * Read off the API's own enumeration, which is also where the lowercase `4k`\n * comes from: `1080p` and `4K` (the spelling Veo takes) are two different\n * strings and only one of them is accepted here.\n *\n * Cheapest first because `snapToSupported` in the studio lane falls back to the\n * FIRST entry when the current pick is unavailable — landing a model switch on\n * the dearest tier is the expensive direction of that mistake.\n *\n * 360p is the reason this list matters. It renders at exactly a third of the\n * 720p token rate, which makes it the tier for trying a brief out: five drafts\n * at 360p cost less than two takes at 720p, and the composition, pacing and\n * audio you are judging all survive the smaller frame. 1080p and 4K are\n * upscales of the same render, so they are worth paying for on the take you\n * keep, not on the ones you are choosing between.\n */\nexport const GEMINI_OMNI_RESOLUTIONS = [\"360p\", \"720p\", \"1080p\", \"4k\"] as const;\n\n/**\n * Image mimes allowed on an Omni `image` input part. Deliberately the narrow\n * intersection of Gemini's documented image inputs and what we have actually\n * rendered with (PNG, verified end-to-end). `image/gif` is excluded because it\n * is absent from Gemini's documented input set — we have NOT observed a refusal,\n * so this is a conservative gate, and conservative is the free direction: an\n * over-narrow list fails in `baker canvas validate` at no cost, whereas an\n * over-wide one fails at dispatch after the call is billed.\n */\nexport const GEMINI_OMNI_IMAGE_MIMES = [\"image/png\", \"image/jpeg\", \"image/webp\"] as const;\n\n// Grok Imagine goes down to a single second — the only model we carry that does.\n// Its listing advertises up to 15s, but REFERENCE-to-video (the only way an\n// avatar reaches it) is capped at 10 and refuses anything longer with a 400:\n// \"Duration 15s exceeds the maximum allowed for reference-to-video, which is\n// 10s.\" Measured. Capped here rather than documented, because the ceiling only\n// shows up on the route we actually use.\nexport const GROK_IMAGINE_DURATIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] as const;\n\n// Wan 2.7 starts at 2s and caps at 10, shorter than the rest.\nexport const WAN_27_DURATIONS = [2, 3, 4, 5, 6, 7, 8, 9, 10] as const;\n\nexport const HAPPYHORSE_DURATIONS = [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as const;\n\n// Wan 3.0 renders 2–30s, three times 2.7's ceiling — every whole second, no gaps.\nexport const WAN_30_DURATIONS = [\n 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,\n] as const;\n\n// Grok Imagine 1.5 advertises 1–15s. Unlike the older Grok there is no\n// reference-to-video route to cap it on, because 1.5 does not have one.\nexport const GROK_IMAGINE_15_DURATIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] as const;\n\n// Runway Gen-4.5: 2–10s.\nexport const RUNWAY_45_DURATIONS = [2, 3, 4, 5, 6, 7, 8, 9, 10] as const;\n\n// ElevenLabs `output_format` values the pipeline supports. Constrained to the\n// mp3 family: the backend handlers label every stored audio asset audio/mpeg,\n// so pcm/opus/ulaw would be saved under a wrong mime and break downstream\n// ffmpeg/composition steps. Widen only together with mime handling.\nexport const ELEVENLABS_OUTPUT_FORMATS = [\n \"mp3_22050_32\",\n \"mp3_44100_32\",\n \"mp3_44100_64\",\n \"mp3_44100_96\",\n \"mp3_44100_128\",\n \"mp3_44100_192\",\n] as const;\n\n// Hard validation bounds derived from the backend's per-node cost cap\n// (MAX_CANVAS_NODE_COST_USD = $10): anything above these always dies at\n// dispatch with `cost_limit_exceeded`, so reject it at validation instead.\n// tts/dialogue: $10 / $0.00022 per character. music: $10 / $0.022 per second.\nexport const ELEVENLABS_MAX_TEXT_CHARS = 45_454;\nexport const ELEVENLABS_MAX_MUSIC_LENGTH_MS = 454_545;\n\n/**\n * The Eleven Music ENGINE both music routes run on.\n *\n * Separate from the route names in the registry below — `elevenlabs/music-v1`\n * selects compose and `elevenlabs/video-background-music-v1` selects\n * video-to-music, and canvases already name those, so they keep their names.\n * This is the `model_id` that rides on the wire, which was implicitly `music_v1`\n * (the provider default) until it was measured.\n *\n * Measured on a real client cut before switching — the same three clips, the\n * same brief, only `model_id` different. Over the 25s carrying speech, v1's\n * level wandered **16.3 dB** while v2 held **3.9 dB**; that wander is what a bed\n * disappearing and reappearing under a voice sounds like, and it is what sent an\n * agent round the \"too quiet\" → raise it → \"demasiado alta\" loop. v2 also sits\n * 1.7 dB further out of the 200 Hz–3 kHz speech band, so it masks the voice\n * less, and ends at -32 dBFS where v1 collapses to -50 dB (that fade is what\n * made the tail read as empty). Against it: v2 plays ~5x the note attacks.\n * Judged by ear on both renders before this was changed.\n */\nexport const ELEVENLABS_MUSIC_ENGINE = \"music_v2\";\n\n// ---------------------------------------------------------------------------\n// Accepted-mime sets — keyed by provider transport, not by model.\n// ---------------------------------------------------------------------------\n\nconst OPENROUTER_IMAGE_MIMES = [\"image/png\", \"image/jpeg\", \"image/webp\", \"image/gif\"] as const;\n// Non-image reference assets, today only Seedance 2.5. Kept narrow on purpose:\n// these ride to the provider as a plain URL it has to fetch and decode itself,\n// and an exotic container is a refusal after the clip is queued. MP4/MOV and the\n// mp3 family are what the rest of this pipeline already produces (R2 renders,\n// ElevenLabs speech and music), so the set is exactly what we can actually wire.\nconst OPENROUTER_VIDEO_REFERENCE_MIMES = [\"video/mp4\", \"video/quicktime\", \"video/webm\"] as const;\n// MP3 and WAV only, and the exclusion is the load-bearing part: Seedance refuses\n// an M4A outright (\"the parameter audio format specified in the request is not\n// valid\"), verified live against a real Mux audio rendition. That matters because\n// M4A is exactly what the video library holds — every Mux `audio-only` rendition\n// is one — so the obvious source of a soundtrack in this product is the one\n// format the model will not read.\nconst OPENROUTER_AUDIO_REFERENCE_MIMES = [\"audio/mpeg\", \"audio/mp3\", \"audio/wav\"] as const;\nconst REPLICATE_IMAGE_MIMES = [\"image/png\", \"image/jpeg\", \"image/webp\"] as const;\nconst REPLICATE_VIDEO_MIMES = [\"video/mp4\", \"video/webm\", \"video/quicktime\"] as const;\n// Formats both Mux (frame extraction) and Gemini (multimodal analysis) accept.\nconst DECONSTRUCT_VIDEO_MIMES = [\"video/mp4\", \"video/webm\", \"video/quicktime\"] as const;\nconst REPLICATE_AUDIO_MIMES = [\"audio/wav\", \"audio/mpeg\", \"audio/mp3\"] as const;\n\n// ---------------------------------------------------------------------------\n// Registry\n// ---------------------------------------------------------------------------\n\n/**\n * Canonical, ordered list of `image_generate` model ids. Single source of truth\n * shared by the backend HTTP boundary (`z.enum`) and the CLI `studio generate`\n * command, so the accepted-model set can never drift between them. These MUST\n * stay in sync with the keys of `MODEL_REGISTRY.image_generate` below — the\n * `_assertImageGenerateModelsMatchRegistry` type guard enforces it at compile time.\n */\nexport const IMAGE_GENERATE_MODELS = [\n \"openai/gpt-image-2.5-sunburst\",\n \"openai/gpt-image-2\",\n \"openai/gpt-5.4-image-2\",\n \"google/gemini-3.1-flash-image\",\n \"google/gemini-3.1-flash-lite-image\",\n \"google/gemini-3-pro-image\",\n \"recraft/recraft-v4.1-pro-vector\",\n // LEGACY — the preview ids the Gemini image models shipped under until they went GA\n // on 2026-06-18. Kept so every canvas written with them still validates and re-runs;\n // never pick them for new work.\n \"google/gemini-3.1-flash-image-preview\",\n \"google/gemini-3-pro-image-preview\",\n] as const;\n\n/**\n * Default image-generation model when a caller doesn't specify one — Nano Banana\n * flash (`google/gemini-3.1-flash-image`): fast iteration, reference\n * editing, and extreme aspect ratios.\n * The `(typeof IMAGE_GENERATE_MODELS)[number]` annotation guarantees at compile\n * time that the default is one of the accepted models. Consumed by the backend\n * `/api/studio/generate` handler so the literal lives in exactly one place.\n */\nexport const DEFAULT_IMAGE_GENERATE_MODEL: (typeof IMAGE_GENERATE_MODELS)[number] = \"google/gemini-3.1-flash-image\";\n\n/**\n * The price tier of a picture from each image model. Nano Banana Pro renders about three\n * times what the other image models do per picture (measured $0.13 a 2K image), so it is\n * quoted at its own rate. Keyed by every curated id: a new model cannot ship unclassified.\n */\nconst IMAGE_PRICE_TIER: Readonly<Record<(typeof IMAGE_GENERATE_MODELS)[number], \"pro\" | \"standard\">> = {\n \"openai/gpt-image-2.5-sunburst\": \"standard\",\n \"openai/gpt-image-2\": \"standard\",\n \"openai/gpt-5.4-image-2\": \"standard\",\n \"google/gemini-3.1-flash-image\": \"standard\",\n \"google/gemini-3.1-flash-lite-image\": \"standard\",\n \"google/gemini-3-pro-image\": \"pro\",\n \"recraft/recraft-v4.1-pro-vector\": \"standard\",\n \"google/gemini-3.1-flash-image-preview\": \"standard\",\n \"google/gemini-3-pro-image-preview\": \"pro\",\n};\n\n/** Credits quoted up front per picture at each price tier (the picture is billed on usage afterwards). */\nexport const IMAGE_TIER_CREDITS = { pro: 20, standard: 5 } as const;\n\n/** The up-front quote of one picture from `model`: Pro's rate for a Pro image model, the plain rate otherwise (an unknown id too). */\nexport function imagePictureCredits(model: string): number {\n const tier = (IMAGE_PRICE_TIER as Readonly<Record<string, \"pro\" | \"standard\">>)[model] ?? \"standard\";\n return IMAGE_TIER_CREDITS[tier];\n}\n\n/**\n * Canonical, ordered list of `video_generate` model ids — a deliberately SMALL,\n * curated roster (not a Higgsfield-style catalog). Each slot earns its place as\n * \"the best model for a specific job\":\n * - `google/gemini-omni-1.1-flash` — DEFAULT. Native audio + reference images in\n * one call, 3-10s, and a real quality dial: 360p / 720p / 1080p / 4k at\n * ~$0.034 / $0.101 / $0.152 / $0.304 per second, billed for the seconds it\n * renders. Only a length outside 3-10s or a 1:1 frame still forces a beat off\n * it.\n * - `google/gemini-omni-flash` — LEGACY. The 1.1 preview: 720p only, at the same\n * per-second rate, so it is strictly 1.1 minus the quality dial. Kept so\n * stored canvases re-run verbatim; never pick it for new work.\n * - `bytedance/seedance-2.0` — identity/product workhorse (cheap, strong i2v),\n * and the only Seedance tier that reaches 1080p/4K.\n * - `bytedance/seedance-2.5` — LONG-FORM and MULTIMODAL: 4-30s in one render,\n * and the only curated model that takes video and audio references, so\n * edit/extend and audio-driven beats exist here or nowhere. Capped at 720p.\n * - `google/veo-3.1` — photoreal cine ceiling + real-face fallback (dodges the\n * ByteDance real-person filter).\n * - `google/veo-3.1-fast` — cheap Veo iteration tier.\n * - `kwaivgi/kling-v3.0-pro` — motion-transfer / dynamic multi-shot beats.\n * Kept in sync with the keys of `MODEL_REGISTRY.video_generate` by the\n * `video_generate lists exactly the keys` invariant in `registry.test.ts`.\n */\nexport const VIDEO_GENERATE_MODELS = [\n \"bytedance/seedance-2.0\",\n \"bytedance/seedance-2.5\",\n \"google/gemini-omni-1.1-flash\",\n \"google/gemini-omni-flash\",\n \"google/veo-3.1\",\n \"google/veo-3.1-fast\",\n \"google/veo-3.1-lite\",\n \"kwaivgi/kling-v3.0-pro\",\n \"x-ai/grok-imagine-video\",\n \"x-ai/grok-imagine-video-1.5\",\n \"alibaba/wan-2.7\",\n \"alibaba/wan-3.0\",\n \"runway/gen-4.5\",\n \"alibaba/happyhorse-1.1\",\n] as const;\n\n/**\n * Default video-generation model when a caller doesn't specify one — Gemini Omni\n * Flash. It wins the default slot because it is the only curated model that\n * returns picture AND synchronized audio from a single call, takes reference\n * images, and bills ~$0.101 per second at 720p (a 5s clip ~$0.51, vs ~$2.50 for\n * the same on Seedance) — or a third of that at 360p. Kept as a named constant\n * (not a hardcoded string at call sites) per the model-catalog rule; the CLI\n * scaffolder's scored router can override it per brief, and does — a photoreal\n * on-camera cast still routes to Veo, and a length outside 3-10s must leave this\n * default behind. A beat needing 1080p no longer has to: 1.1 renders it here.\n */\nexport const DEFAULT_VIDEO_GENERATE_MODEL: (typeof VIDEO_GENERATE_MODELS)[number] = \"google/gemini-omni-1.1-flash\";\n\n/**\n * Which models can do REFERENCE-to-video — images used as visual guidance\n * rather than as the exact opening frame. This is the capability an avatar\n * needs: \"here is who she is, now invent a shot of her\".\n *\n * This list is an explicit allow-list, and every entry on it was MEASURED —\n * the model cards are unreliable in both directions, so nothing is here or\n * absent on the strength of documentation alone.\n *\n * Reaching references three different ways: Seedance 2.0 through Replicate,\n * whose wrapper takes `reference_images`; Gemini Omni Flash through Google's own\n * Interactions API, which declares `reference_to_video` as a first-class task;\n * and Seedance 2.5 THROUGH OpenRouter, where `input_references` is a real,\n * honoured field and the submit is reported back as an `r2v` task. So\n * \"OpenRouter means frame-only\" is no longer true as a blanket rule — which is\n * exactly why this stays an allow-list rather than a guess about the route.\n *\n * The avatar entries were probed with a real identity sheet through OpenRouter's\n * `input_references` channel:\n * - `x-ai/grok-imagine-video` — IN. Preserved the face, honoured the requested\n * 9:16, built a new scene from the prompt. Cheapest of them. No audio.\n * - `alibaba/wan-2.7` — IN. Same identity quality and the only one with native\n * audio, so it is the talking-head option — in landscape only.\n * - `alibaba/happyhorse-1.1` — IN. Correct ratio, strong identity, no audio.\n * - `alibaba/wan-2.6`, `minimax/hailuo-2.3` — OUT. They accept the reference and\n * then animate the sheet ITSELF, labels and grid included: they read\n * `input_references` as \"the image to move\", not as identity guidance.\n *\n * Veo and Kling remain frame-only: `GET /api/v1/videos/models` reports\n * `supported_frame_images: [\"first_frame\", \"last_frame\"]` for both and advertises\n * no reference capability at all. OpenRouter's generic body still DEFINES\n * `input_references`, so sending them there is accepted and then quietly produces\n * a request with no usable image conditioning — every clip in the batch, every\n * time. Veo additionally REJECTS an identity sheet handed to it as a first frame\n * (Vertex flags a grid of face close-ups), so its avatar path is always an\n * ordinary in-scene still. Gate on this instead of discovering it one billed\n * refusal at a time.\n */\nexport const REFERENCE_TO_VIDEO_MODELS = [\n \"bytedance/seedance-2.0\",\n \"bytedance/seedance-2.5\",\n \"google/gemini-omni-1.1-flash\",\n \"google/gemini-omni-flash\",\n \"x-ai/grok-imagine-video\",\n \"alibaba/wan-2.7\",\n \"alibaba/happyhorse-1.1\",\n] as const;\n\nexport function supportsReferenceToVideo(model: string): boolean {\n return (REFERENCE_TO_VIDEO_MODELS as readonly string[]).includes(model);\n}\n\n/**\n * Reference-to-video models that accept a HUMAN likeness.\n *\n * Seedance is the exception that forces this second list: it does reference-to-\n * video, and it refuses every request whose reference contains a photoreal face.\n * Routing an avatar to it is a guaranteed, instantaneous failure.\n *\n * IMPORTANT — this list is NOT \"the models that can put an avatar in a clip\".\n * It is the models that can do it **from the identity sheet alone**. An avatar\n * also reaches video the other way: generate a still of them, then animate that\n * still, and there the face is carried by the frame so every frame-capable\n * model qualifies — Veo 3.1 included, and it is excellent at it. Gate on this\n * list only when there is no opening frame to animate.\n */\nexport const AVATAR_VIDEO_MODELS = [\"x-ai/grok-imagine-video\", \"alibaba/wan-2.7\", \"alibaba/happyhorse-1.1\"] as const;\n\nexport function supportsAvatarVideo(model: string): boolean {\n return (AVATAR_VIDEO_MODELS as readonly string[]).includes(model);\n}\n\n/**\n * Models that must never be handed an avatar, by EITHER route.\n *\n * A denylist rather than an allowlist, on purpose. The list above is only the\n * models that take an identity sheet DIRECTLY; every frame-capable model can\n * also carry an avatar the other way — generate an in-scene still of them first,\n * then animate that still, where the face is an ordinary photograph and no\n * reference channel is needed. Veo 3.1 does this beautifully (measured: 9:16,\n * native audio, identity intact). Restricting avatars to the reference-capable\n * three ruled out the best-looking route on no evidence at all.\n *\n * The two entries here are the ones that actually refused:\n * - Seedance classifies pixels, not provenance: it rejects a photoreal face as\n * a reference AND would as a frame, whoever generated it.\n * - Veo 3.1 Lite returned `completed with no output (content may have been\n * filtered)` on the very still Veo 3.1 animated happily from the same URL. A\n * model that swallows the request and bills for nothing is worse than one\n * that errors, so it stays out of avatar work until that changes.\n */\nexport const AVATAR_BLOCKED_VIDEO_MODELS = [\"bytedance/seedance\", \"google/veo-3.1-lite\"] as const;\n\nexport function canCarryAvatar(model: string): boolean {\n return !AVATAR_BLOCKED_VIDEO_MODELS.some((blocked) => model.startsWith(blocked));\n}\n\n/**\n * Default for a clip that has to carry an avatar's face. Grok Imagine measured\n * best on the three things that decide it — identity retention, honouring the\n * requested aspect ratio, and cost — so it is the default; `alibaba/wan-2.7` is\n * the swap when the clip needs native audio.\n */\nexport const DEFAULT_AVATAR_VIDEO_MODEL: (typeof AVATAR_VIDEO_MODELS)[number] = \"x-ai/grok-imagine-video\";\n\n/** Ratios an avatar clip can be rendered in directly, per model, as measured. */\nexport function avatarVideoSupportsVertical(model: string): boolean {\n return model !== \"alibaba/wan-2.7\";\n}\n\n/**\n * Gemini Omni is reached through Google's own Interactions API rather than the\n * OpenRouter video gateway (it is absent from OpenRouter's catalogue entirely).\n * The backend dispatcher branches on this, and the cost/validation layers use it\n * to apply Omni's fixed-output rules.\n */\nexport function isGeminiOmniModel(model: string): boolean {\n return model.startsWith(\"google/gemini-omni\");\n}\n\n/**\n * The catalogue id of the Omni PREVIEW, which is a different model from 1.1 in\n * the two ways that cost money: it has no `resolution` (720p only) and it bills\n * its whole 10s window whatever length it renders.\n *\n * Named rather than spelled inline because the two ids differ by four characters\n * and `startsWith(\"google/gemini-omni\")` matches both — the routing, quoting and\n * param layers all have to tell them apart, and a hand-written comparison that\n * gets it backwards fails silently in the expensive direction.\n */\nexport const GEMINI_OMNI_PREVIEW_MODEL = \"google/gemini-omni-flash\";\n\n/** True for the legacy Omni preview specifically, not for 1.1. */\nexport function isGeminiOmniPreviewModel(model: string): boolean {\n return model === GEMINI_OMNI_PREVIEW_MODEL;\n}\n\n/**\n * Seedance 2.5, which is a DIFFERENT provider route from 2.0 despite the shared\n * family name: 2.5 goes through OpenRouter's video gateway, 2.0 through Replicate.\n *\n * An exact id rather than a prefix, and it earns the extra strictness: a\n * `startsWith(\"bytedance/seedance\")` test here would silently route 2.5 into the\n * Replicate wrapper, which has no video or audio references, no 30s durations and\n * a 4,000-character prompt cap this model does not have — a request that half\n * works is far harder to spot than one that does not.\n */\nexport function isSeedance25Model(model: string): boolean {\n return model === \"bytedance/seedance-2.5\";\n}\n\n/**\n * Which models take a SECOND conditioning image as the clip's closing frame, so\n * the model interpolates real in-shot motion between two authored keyframes.\n *\n * Omni does not: its Interactions API takes ONE image, and a second is dropped\n * silently. That matters to the SCAFFOLDER more than to the render — an end\n * keyframe it will never use is a billed `image_generate` for nothing — so this\n * gates whether the closing frame is generated at all, not just whether it is\n * wired.\n *\n * An allow-list, like `REFERENCE_TO_VIDEO_MODELS`: an unknown model is assumed\n * NOT to take one, because the cost of guessing wrong that way is a slightly\n * looser clip, while guessing the other way bills an image every clip.\n */\nexport const LAST_FRAME_TO_VIDEO_MODELS = [\n \"bytedance/seedance-2.0\",\n \"bytedance/seedance-2.5\",\n \"google/veo-3.1\",\n \"google/veo-3.1-fast\",\n \"kwaivgi/kling-v3.0-pro\",\n] as const;\n\nexport function supportsLastFrame(model: string): boolean {\n return (LAST_FRAME_TO_VIDEO_MODELS as readonly string[]).includes(model);\n}\n\n/**\n * Whether a model reads a reference of a kind that is NOT an image.\n *\n * Derived from the registry's own optional input slots rather than kept as a\n * second allow-list, because — unlike image references — there is no ambiguity to\n * resolve: Veo and Kling declare an optional image slot for their FRAMES, so an\n * image slot cannot tell you whether the model does reference-to-video, but a\n * video or audio slot exists on exactly the models that read one.\n *\n * What they unlock, and why they are worth the plumbing: a VIDEO reference is how\n * edit and extend are expressed at all (there is no separate endpoint — you hand\n * the clip back in as a reference), and an AUDIO reference is what makes a beat\n * cut to a track or a voiceover instead of inventing its own timing. Only\n * Seedance 2.5 takes either today.\n */\nexport function supportsReferenceKind(model: string, kind: InputKind): boolean {\n return inputSlot(\"video_generate\", model, kind) !== undefined;\n}\n\n/**\n * The curated video models that read a reference of this kind, in roster order.\n *\n * Exists so a caller that has to CHOOSE one — the Studio, when a marketer picks a\n * clip to build on and the current model cannot — names it by capability rather\n * than by hardcoding an id at the call site, which is the rule this registry is\n * the source of truth for.\n */\nexport function videoModelsSupportingReferenceKind(kind: InputKind): string[] {\n return VIDEO_GENERATE_MODELS.filter((model) => supportsReferenceKind(model, kind));\n}\n\nexport const MODEL_REGISTRY: Readonly<Record<NodeKind, Readonly<Record<string, ModelSpec>>>> = {\n text_generate: {\n \"~google/gemini-flash-latest\": {\n label: \"Google Gemini Flash (latest)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n system: { kind: \"string\" },\n response_format: { kind: \"string\", enum: [\"text\", \"json_object\"] },\n web_search: { kind: \"boolean\" },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n \"~google/gemini-pro-latest\": {\n label: \"Google Gemini Pro (latest)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n system: { kind: \"string\" },\n response_format: { kind: \"string\", enum: [\"text\", \"json_object\"] },\n web_search: { kind: \"boolean\" },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n },\n\n image_describe: {\n // Vision extraction — the Visual Architect system prompt lives on the\n // backend, so there is no `prompt` param; `focus` appends emphasis only.\n \"~google/gemini-pro-latest\": {\n label: \"Google Gemini Pro (latest, Vision)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n focus: { kind: \"string\" },\n context: { kind: \"string\" },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n \"~google/gemini-flash-latest\": {\n label: \"Google Gemini Flash (latest, Vision)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n focus: { kind: \"string\" },\n context: { kind: \"string\" },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n },\n\n image_select: {\n // Vision judgement — the selection contract (numbered images, JSON\n // response) is baked into the backend prompt; `prompt` carries only the\n // selection criteria. `count` has an engine-side default of 1, so it is\n // not registry-required.\n \"~google/gemini-flash-latest\": {\n label: \"Google Gemini Flash (latest, Vision)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n count: { kind: \"number\", min: 1 },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n \"~google/gemini-pro-latest\": {\n label: \"Google Gemini Pro (latest, Vision)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n count: { kind: \"number\", min: 1 },\n temperature: { kind: \"number\", min: 0, max: 2 },\n max_tokens: { kind: \"number\", min: 1 },\n },\n },\n },\n\n image_generate: {\n // `seed` is omitted on every entry — OpenRouter's `image_config` schema\n // has no `seed` slot for image-gen (different from video-gen), so passing\n // it would be a silent no-op.\n // The direct image model, not the `gpt-5.4-image-2` chat model that emitted\n // images as a side effect. Notable capability gaps vs the Gemini entries:\n // no `image_size` (OpenAI picks the pixel dimensions from the ratio, and a\n // `resolution` is silently ignored) and the narrower `..._AR_GPT` ratio set.\n \"openai/gpt-image-2.5-sunburst\": {\n // Identical to the gpt-image-2 entry below except for the quality ladder and `n`\n // (several images from one call) — the ratio set, the 16-reference ceiling, the missing `image_size` and the reasons\n // `background` / `output_compression` stay undeclared all carry over unchanged.\n label: \"OpenAI GPT Image 2.5\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GPT_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\", maxLength: GPT_IMAGE_PROMPT_MAX },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_GPT },\n quality: { kind: \"string\", enum: OPENROUTER_IMAGE_QUALITY_25 },\n n: OPENROUTER_IMAGE_N,\n },\n },\n \"openai/gpt-image-2\": {\n // Its live endpoint also advertises `background` (auto|opaque) and\n // `output_compression` (0-100), which we deliberately do not declare. Not an\n // oversight: a registry param is a promise the whole system keeps — the\n // canvas node schema, the dashboard control, the CLI flag and the cost model\n // all have to carry it — and neither knob changes what the picture IS.\n // Declaring them would widen four surfaces to expose a file-size dial.\n label: \"OpenAI GPT Image 2\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GPT_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\", maxLength: GPT_IMAGE_PROMPT_MAX },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_GPT },\n quality: { kind: \"string\", enum: OPENROUTER_IMAGE_QUALITY },\n },\n },\n // LEGACY — kept so canvases authored before the gpt-image-2 switch still\n // validate and re-run unchanged (`baker canvas run --slug` restores a recorded\n // canvas verbatim, so dropping this model would break every stored creative\n // that used it). Its param surface is the ORIGINAL one on purpose: unlike\n // gpt-image-2 it still honors 4:5/5:4, and `image_size` is accepted-then-\n // ignored upstream rather than rejected. Verified live on /api/v1/images.\n // Don't reach for it in new work — the scaffolders default to gpt-image-2.\n \"openai/gpt-5.4-image-2\": {\n label: \"OpenAI GPT-5.4 Image 2 (legacy)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GPT_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\", maxLength: GPT_IMAGE_PROMPT_MAX },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n quality: { kind: \"string\", enum: OPENROUTER_IMAGE_QUALITY },\n },\n },\n // The GA Nano Banana ids (2026-06-18). Same surface as the preview entries they\n // replace — read off GET /api/v1/images/models/google/gemini-3.1-flash-image/endpoints\n // and .../gemini-3-pro-image/endpoints — so a canvas moves between them unchanged.\n \"google/gemini-3.1-flash-image\": {\n label: \"Google Gemini 3.1 Flash Image (Nano Banana 2)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GEMINI_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_EXTREME },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n \"google/gemini-3-pro-image\": {\n label: \"Google Gemini 3 Pro Image (Nano Banana Pro)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GEMINI_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n },\n },\n // LEGACY preview ids — see IMAGE_GENERATE_MODELS.\n \"google/gemini-3.1-flash-image-preview\": {\n label: \"Google Gemini 3.1 Flash Image (Preview)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GEMINI_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_EXTREME },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n \"google/gemini-3.1-flash-lite-image\": {\n // Nano Banana 2 Lite. Same Gemini image family as the flash tier — same\n // extreme aspect-ratio set, same reference editing — at HALF the output-token\n // price ($0.00003 vs $0.00006 per output image token), which makes it the\n // cheap-iteration tier for image work the way `veo-3.1-fast` is for video.\n //\n // The one thing it gives up is resolution: it renders 1K and nothing else.\n // That is a hard provider 400, not a soft clamp, so it is gated here rather\n // than discovered at dispatch. A 2K/4K hero frame belongs on the flash or pro\n // tier. Unlike gpt-image it keeps 4:5 and 5:4, so Meta/Instagram portrait\n // formats still come straight out of it.\n //\n // Reached through the normal OpenRouter image gateway (Google Vertex and\n // Google AI Studio both serve it) — no direct-provider exception needed.\n // Source: GET /api/v1/images/models/google/gemini-3.1-flash-lite-image/endpoints.\n label: \"Google Gemini 3.1 Flash Lite Image\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GEMINI_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_EXTREME },\n image_size: { kind: \"string\", enum: GEMINI_LITE_IMAGE_SIZE },\n },\n },\n \"google/gemini-3-pro-image-preview\": {\n label: \"Google Gemini 3 Pro Image (Preview)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: GEMINI_IMAGE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n // No `quality`: OpenRouter's per-endpoint schema lists it for gpt-image and\n // for NO Gemini image model, pro included. It is accepted-then-dropped, so\n // declaring it invites the scaffolder to emit a param that does nothing.\n },\n },\n \"recraft/recraft-v4.1-pro-vector\": {\n // The vector/logo tier, and the odd one out of this roster in three ways.\n // All three are read off its live endpoint schema, and all three are the\n // kind of fact that only shows up as a billed 400 if it is guessed:\n // - it renders FIVE ratios (no 4:5 / 5:4 / 2:3 / 3:2 / 21:9), so portrait\n // ad shapes snap to 3:4 rather than being sent as-is;\n // - it has NO resolution knob (`supported_parameters` lists none), so\n // `image_size` is refused here instead of accepted and ignored;\n // - it takes exactly ONE reference image, against 14-16 everywhere else.\n // Its palette levers are real but arrive as provider passthrough\n // (`controls`), which is why they are `json` rather than typed enums; the\n // backend adapter builds that block in `buildImageGenBody`.\n label: \"Recraft v4.1 Pro Vector\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: RECRAFT_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\", maxLength: RECRAFT_PROMPT_MAX },\n aspect_ratio: { kind: \"string\", enum: RECRAFT_IMAGE_AR },\n strength: { kind: \"number\", min: 0, max: 1 },\n rgb_colors: { kind: \"json\" },\n background_rgb_color: { kind: \"json\" },\n },\n },\n },\n\n image_reference_sheet: {\n // Multi-view subject reference sheets (turnaround grids). The sheet prompt\n // is baked into the backend handler — params here are semantic levers\n // only. Both models consume MULTIPLE reference images via the `references`\n // input slot (required: a sheet without source images is just image_generate).\n \"google/gemini-3-pro-image\": {\n label: \"Google Gemini 3 Pro Image (Nano Banana Pro)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"subject_description\", \"subject_type\"],\n params: {\n subject_description: { kind: \"string\" },\n subject_type: { kind: \"string\", enum: [\"character\", \"person\", \"product\", \"location\"] },\n views: { kind: \"json\" },\n style: { kind: \"string\" },\n prompt_override: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n },\n },\n \"google/gemini-3.1-flash-image\": {\n label: \"Google Gemini 3.1 Flash Image (Nano Banana 2)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"subject_description\", \"subject_type\"],\n params: {\n subject_description: { kind: \"string\" },\n subject_type: { kind: \"string\", enum: [\"character\", \"person\", \"product\", \"location\"] },\n views: { kind: \"json\" },\n style: { kind: \"string\" },\n prompt_override: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_EXTREME },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n // LEGACY preview ids, kept so existing canvases re-run.\n \"google/gemini-3-pro-image-preview\": {\n label: \"Google Gemini 3 Pro Image (Nano Banana Pro)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"subject_description\", \"subject_type\"],\n params: {\n subject_description: { kind: \"string\" },\n subject_type: { kind: \"string\", enum: [\"character\", \"person\", \"product\", \"location\"] },\n views: { kind: \"json\" },\n style: { kind: \"string\" },\n prompt_override: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n },\n },\n \"google/gemini-3.1-flash-image-preview\": {\n label: \"Google Gemini 3.1 Flash Image (Preview)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"subject_description\", \"subject_type\"],\n params: {\n subject_description: { kind: \"string\" },\n subject_type: { kind: \"string\", enum: [\"character\", \"person\", \"product\", \"location\"] },\n views: { kind: \"json\" },\n style: { kind: \"string\" },\n prompt_override: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: OPENROUTER_IMAGE_AR_EXTREME },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n },\n\n image_aspect_adapt: {\n // Multi-format adaptation of one creative. The recomposition prompt is\n // baked into the backend handler. The target ratios come from EITHER an\n // explicit `formats` list OR a `platform` preset that expands to the\n // canonical set; exactly one of the two is required — that \"one of\" gate,\n // and per-element ratio validation, live in the node's Zod schema (the\n // registry can't express \"one of\" or gate array elements), so `formats`\n // is not listed as registry-required. The source creative is a required\n // input slot. `copy` (the exact lines the ad carries) and `safe_areas` (per\n // format, the fractions each edge's interface covers) are set by the static\n // scaffold so the reframe keeps every word, once, where the platform shows it.\n \"google/gemini-3-pro-image\": {\n label: \"Google Gemini 3 Pro Image (Nano Banana Pro)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n formats: { kind: \"json\" },\n platform: { kind: \"string\", enum: AD_FORMAT_PLATFORMS },\n guidance: { kind: \"string\" },\n copy: { kind: \"json\" },\n safe_areas: { kind: \"json\" },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n },\n },\n \"google/gemini-3.1-flash-image\": {\n label: \"Google Gemini 3.1 Flash Image (Nano Banana 2)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n formats: { kind: \"json\" },\n platform: { kind: \"string\", enum: AD_FORMAT_PLATFORMS },\n guidance: { kind: \"string\" },\n copy: { kind: \"json\" },\n safe_areas: { kind: \"json\" },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n // LEGACY preview ids, kept so existing canvases re-run.\n \"google/gemini-3-pro-image-preview\": {\n label: \"Google Gemini 3 Pro Image (Nano Banana Pro)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n formats: { kind: \"json\" },\n platform: { kind: \"string\", enum: AD_FORMAT_PLATFORMS },\n guidance: { kind: \"string\" },\n copy: { kind: \"json\" },\n safe_areas: { kind: \"json\" },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE },\n },\n },\n \"google/gemini-3.1-flash-image-preview\": {\n label: \"Google Gemini 3.1 Flash Image (Preview)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [],\n params: {\n formats: { kind: \"json\" },\n platform: { kind: \"string\", enum: AD_FORMAT_PLATFORMS },\n guidance: { kind: \"string\" },\n copy: { kind: \"json\" },\n safe_areas: { kind: \"json\" },\n image_size: { kind: \"string\", enum: OPENROUTER_IMAGE_SIZE_EXTENDED },\n },\n },\n },\n\n video_generate: {\n \"bytedance/seedance-2.0\": {\n // Routed via Replicate's official `bytedance/seedance-2.0` model. NOTE:\n // ByteDance's upstream likeness filter blocks photoreal human faces on ANY\n // reseller, and it does NOT care whether the person is real: it classifies\n // pixels, so an AI-generated face that looks photographic is refused\n // exactly like a photograph (E005 / content_policy_blocked). Confirmed in\n // production against faces we generated ourselves. The only escape is Veo\n // — NOT a synthetic presenter, which an earlier version of this note\n // wrongly recommended and which sends you round the same loop.\n label: \"ByteDance Seedance 2.0\",\n route: { provider: \"replicate\" },\n billing: { kind: \"formula\", formula: \"seedance-2.0\" },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: REPLICATE_IMAGE_MIMES, max: SEEDANCE_MAX_REFERENCES }],\n required: [\"prompt\"],\n params: {\n // Replicate's Seedance wrapper hard-caps the prompt at 4000 chars; gate\n // it here so an over-length prompt fails validate (free) not the billed call.\n prompt: { kind: \"string\", maxLength: SEEDANCE_PROMPT_MAX },\n aspect_ratio: {\n kind: \"string\",\n enum: [\"1:1\", \"3:4\", \"9:16\", \"4:3\", \"16:9\", \"21:9\", \"9:21\"],\n },\n resolution: { kind: \"string\", enum: [\"480p\", \"720p\", \"1080p\", \"4k\"] },\n duration: { kind: \"number\", enum: SEEDANCE_DURATIONS },\n // Replicate types this `integer`, and a fractional seed is a schema\n // rejection rather than a rounded one. No range is advertised, so none is\n // invented — see `InputSpec.max` on why absent must stay ungated.\n seed: { kind: \"number\", integer: true },\n generate_audio: { kind: \"boolean\" },\n },\n },\n \"bytedance/seedance-2.5\": {\n // Seedance 2.5 — the LONG-FORM, MULTIMODAL tier, and the only curated video\n // model reached through OpenRouter that is not frame-only.\n //\n // Routed through OpenRouter's video gateway (provider `seed`), NOT through\n // Replicate like 2.0 — the gateway carries the multimodal reference surface\n // 2.5 exists for, and Replicate's 2.0 wrapper only has `reference_images`.\n // The backend branches on the exact 2.0 id for the Replicate path, so this\n // model must never be folded into a `startsWith(\"bytedance/seedance\")`\n // routing test: that is one character away from sending 2.5 to a wrapper\n // that cannot express half of what it is being asked to do.\n //\n // Same upstream likeness filter as 2.0 — ByteDance classifies PIXELS, so an\n // AI-generated face that reads photographic is refused exactly like a\n // photograph (content_policy_blocked). A reference-guided clip refused that\n // way is re-rendered on fal, which serves the same model under a licence that\n // takes faces; a frame-started one is reported, never moved to another model.\n //\n // No published prompt cap on this route, so none is invented: OpenRouter's\n // request schema declares no `maxLength` and the model is sold on long,\n // multi-shot briefs. A ceiling we made up would refuse the exact prompts\n // this tier exists to render.\n label: \"ByteDance Seedance 2.5\",\n route: OPENROUTER_ROUTE,\n billing: { kind: \"formula\", formula: \"seedance-2.5\" },\n recoveryRoute: SEEDANCE_FACE_RECOVERY_ROUTE,\n inputs: [],\n optional_inputs: [\n { kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES, max: SEEDANCE_25_MAX_IMAGE_REFERENCES },\n { kind: \"video\", mimes: OPENROUTER_VIDEO_REFERENCE_MIMES, max: SEEDANCE_25_MAX_VIDEO_REFERENCES },\n { kind: \"audio\", mimes: OPENROUTER_AUDIO_REFERENCE_MIMES, max: SEEDANCE_25_MAX_AUDIO_REFERENCES },\n ],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: SEEDANCE_25_ASPECT_RATIOS },\n resolution: { kind: \"string\", enum: SEEDANCE_25_RESOLUTIONS },\n duration: { kind: \"number\", enum: SEEDANCE_25_DURATIONS },\n seed: { kind: \"number\", integer: true, min: 0, max: UINT32_MAX },\n generate_audio: { kind: \"boolean\" },\n // A visible AI watermark on an ad creative is a defect, and this is the\n // only curated model that lets us turn one off: `watermark` is named in\n // the model's own `allowed_passthrough_parameters`, so it rides as a\n // top-level body field exactly like Kling's `cfg_scale` does.\n watermark: { kind: \"boolean\" },\n },\n },\n \"google/gemini-omni-1.1-flash\": {\n // Google Gemini Omni 1.1 Flash — the DEFAULT video model. Natively\n // multimodal: it reasons over text + images and renders video WITH\n // synchronized audio in one call, and it is the only curated model that\n // takes reference images through its own first-party API (task\n // `reference_to_video`).\n //\n // It is a SEPARATE entry from `google/gemini-omni-flash` (the preview),\n // not a rolling upgrade of it, because the two do not render the same\n // things: 1.1 takes a `resolution` and the preview does not, and 1.1 bills\n // the seconds it renders where the preview bills its whole 10s window. One\n // shared key would have to promise the union of those, which means a canvas\n // could ask the preview for 360p and be quoted a third of what it charges.\n //\n // NOT on OpenRouter. `GET /api/v1/videos/models` lists 17 video models and\n // no Gemini Omni at either version, so this is the sanctioned direct-provider\n // exception (see `convex/lib/geminiOmni.ts`): Google's Interactions API,\n // `POST /v1beta/interactions`. Its own `generateContent` endpoint refuses it\n // (\"This model only supports Interactions API\"), so the Veo `predictLongRunning`\n // shape does not apply either.\n //\n // The param set below is deliberately short and was read off the live API,\n // not off the docs — the knobs absent here are rejected with a pre-dispatch\n // 400 (`Unknown parameter '<name>'`): no `seed`, no `negative_prompt`, no\n // `generate_audio`, no `person_generation` / `enhance_prompt` /\n // `conditioning_scale`. Audio is always on and cannot be turned off.\n // Widening this list does not widen the model; it just moves a free\n // validation error into a billed one.\n //\n // `duration` is the odd one out: Omni has no duration FIELD at any nesting\n // level, but it honours a length named in the prompt (3-10s), so the adapter\n // turns this param into a prompt directive. It is a real, billable knob.\n //\n // NO VIDEO INPUT SLOT, and that is a gate on our project rather than a gap\n // in the model or a mistake in how we ask. 1.1 advertises video references,\n // `edit` and `extend`, and every request carrying a video comes back 400\n // `content_blocked`.\n //\n // The controlled version, run through the shape Google's own docs use\n // (`{\"type\":\"document\",\"uri\":<Files API uri>}`, no generation_config),\n // changing only the uploaded file: an IMAGE returns 200 and renders a clip;\n // a VIDEO returns `content_blocked`. Same route, same structure. So the\n // request shape is right and video input specifically is refused. Also\n // measured across inline base64, all three tasks, video AND text output,\n // synthetic clips, Omni's own output, real camera footage, and both our\n // keys. Full probe log: `.context/omni-1.1-measurements.md`.\n //\n // Adding `{ kind: \"video\", mimes: … }` below is the ONE line that turns the\n // whole feature on — the \"Build on\" control, the reference picker taking an\n // MP4, the drop target and the source-clip cost premium are all driven off\n // this slot. Add it the day the 400 stops, not before: until then it offers\n // a control whose every use is refused.\n label: \"Google Gemini Omni 1.1 Flash\",\n route: { provider: \"google-interactions\", wireModel: \"gemini-omni-1.1-flash\" },\n billing: { kind: \"formula\", formula: \"gemini-omni\" },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: GEMINI_OMNI_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: GEMINI_OMNI_ASPECT_RATIOS },\n resolution: { kind: \"string\", enum: GEMINI_OMNI_RESOLUTIONS },\n duration: { kind: \"number\", enum: GEMINI_OMNI_DURATIONS },\n },\n },\n \"google/gemini-omni-flash\": {\n // LEGACY — the Omni PREVIEW (`gemini-omni-flash-preview`), superseded by\n // `google/gemini-omni-1.1-flash` above. Kept so canvases authored before 1.1\n // still validate and re-run unchanged (`baker canvas run --slug` restores a\n // recorded canvas verbatim, so dropping this model would break every stored\n // creative that used it). Don't reach for it in new work.\n //\n // Its param surface is the ORIGINAL one on purpose, and the omission that\n // matters is `resolution`. This model renders 720p / 24fps and nothing else.\n //\n // It does NOT reject the field, which is the trap and the reason this is\n // enforced here rather than left to the provider: asked for `360p` it\n // returns HTTP 200, a 1280x720 clip, and a bill of 17,376 video tokens —\n // the 720p rate, three times the 5,793 that tier costs on 1.1. Accepted,\n // ignored, and charged for. Copying 1.1's enum across to make the two\n // entries match would hand a canvas a quality dial that does nothing and a\n // quote a third of the real charge.\n label: \"Google Gemini Omni Flash (legacy preview)\",\n route: { provider: \"google-interactions\", wireModel: \"gemini-omni-flash-preview\" },\n billing: { kind: \"formula\", formula: \"gemini-omni\" },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: GEMINI_OMNI_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: GEMINI_OMNI_ASPECT_RATIOS },\n duration: { kind: \"number\", enum: GEMINI_OMNI_DURATIONS },\n },\n },\n \"google/veo-3.1\": {\n // Photoreal CINE CEILING + the real-face fallback (Veo generates adult\n // humans from a keyframe, dodging ByteDance's real-person filter). Same\n // OpenRouter google-vertex routing as the fast tier; the quality dial is\n // `resolution: 1080p` + `generate_audio` + a real `negative_prompt`, not a\n // separate provider knob. Reach for this for hero beats and any clip that\n // must carry a real human likeness.\n label: \"Google Veo 3.1\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // Veo prices 4K far above the base tier, and 4K is a selectable resolution,\n // so folding it into the base rate under-quotes the dearest clip we can make.\n // A QUOTE only: the charge is the usage OpenRouter reports for the call.\n usd: { silent: 0.2, audio: 0.4, byResolution: { \"4K\": { silent: 0.4, audio: 0.6 } } },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n negative_prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\"] },\n resolution: { kind: \"string\", enum: [\"720p\", \"1080p\", \"4K\"] },\n duration: { kind: \"number\", enum: [4, 6, 8] },\n // Vertex declares `seed` a uint32: \"accepted range 0-4294967295\".\n seed: { kind: \"number\", integer: true, min: 0, max: VEO_SEED_MAX },\n generate_audio: { kind: \"boolean\" },\n person_generation: { kind: \"string\", enum: [\"allow_all\", \"allow_adult\"] },\n enhance_prompt: { kind: \"boolean\" },\n conditioning_scale: { kind: \"number\" },\n },\n },\n \"kwaivgi/kling-v3.0-pro\": {\n // Motion-transfer / dynamic multi-shot beats. Reachable through the default\n // OpenRouter gateway (generic video body — no google-vertex block), so it\n // needs no direct-provider exception. `cfg_scale` trades prompt adherence\n // vs motion freedom; higher = closer to prompt.\n //\n // Params below are the ones OpenRouter actually advertises for this model\n // (GET /api/v1/videos/models). Three were wrong before and each was a free\n // 400 or a silently unsupported knob: it renders 720p ONLY (1080p was\n // offered and rejected), it takes ANY duration from 3-15s (we allowed just\n // 5 and 10), and it does not support `seed` at all.\n label: \"Kling 3.0\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // One flat rate across every resolution and mode.\n usd: { silent: 0.112, audio: 0.168 },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n // Kling caps the prompt shorter than Seedance; gate it here so an\n // over-length prompt fails validate (free) not the billed call. Its\n // negative prompt is a SEPARATE 2,500 budget, not a shared one.\n prompt: { kind: \"string\", maxLength: KLING_PROMPT_MAX },\n negative_prompt: { kind: \"string\", maxLength: KLING_PROMPT_MAX },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\", \"1:1\"] },\n resolution: { kind: \"string\", enum: [\"720p\"] },\n duration: { kind: \"number\", enum: KLING_DURATIONS },\n generate_audio: { kind: \"boolean\" },\n cfg_scale: { kind: \"number\", min: 0, max: 1 },\n },\n },\n \"x-ai/grok-imagine-video\": {\n // The avatar workhorse. Measured best of the reference-capable models on\n // identity retention AND aspect-ratio obedience, at the lowest rate of any\n // video model we carry ($0.05/s at 480p). No audio at all — `generate_audio`\n // is absent from its OpenRouter listing, so a talking head goes to Wan 2.7.\n // NOTE: `x-ai/grok-imagine-video-1.5` is NOT a drop-in upgrade — it is\n // described as plain image-to-video and drops the reference conditioning\n // this entry exists for.\n label: \"Grok Imagine Video\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n usd: {\n silent: 0.07,\n audio: 0.07,\n byResolution: { \"480p\": { silent: 0.05, audio: 0.05 }, \"720p\": { silent: 0.07, audio: 0.07 } },\n },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"] },\n resolution: { kind: \"string\", enum: [\"480p\", \"720p\"] },\n duration: { kind: \"number\", enum: GROK_IMAGINE_DURATIONS },\n },\n },\n \"alibaba/wan-2.7\": {\n // Reference-to-video AND native audio — the only model we carry with both,\n // which makes it the landscape talking-head option.\n //\n // LANDSCAPE ONLY, measured. It advertises 9:16, 1:1, 4:3 and 3:4, and\n // delivers 16:9 regardless: asked for vertical via `aspect_ratio: \"9:16\"`\n // it returned 1280x720, and asked again via an explicit\n // `size: \"720x1280\"` from its own `supported_sizes` it returned 1280x720\n // again. The enum below is therefore what it DOES, not what it claims, so\n // nobody pays for a vertical clip that comes back landscape. For a vertical\n // avatar clip use `x-ai/grok-imagine-video` (silent) or animate a generated\n // still with `google/veo-3.1-lite` (native audio, honours 9:16).\n label: \"Alibaba Wan 2.7\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // Flat $0.10/s across resolutions and modes, audio included.\n usd: { silent: 0.1, audio: 0.1 },\n minBilledSeconds: MIN_BILLED_SECONDS,\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n negative_prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\"] },\n resolution: { kind: \"string\", enum: [\"720p\", \"1080p\"] },\n duration: { kind: \"number\", enum: WAN_27_DURATIONS },\n seed: { kind: \"number\" },\n generate_audio: { kind: \"boolean\" },\n },\n },\n \"x-ai/grok-imagine-video-1.5\": {\n // NOT a replacement for the entry above, and deliberately absent from\n // `AVATAR_VIDEO_MODELS` and `REFERENCE_TO_VIDEO_MODELS`: OpenRouter describes the\n // older Grok as \"text-, image-, and reference-conditioned\" and 1.5 as taking \"an\n // optional starting image\". Reference conditioning is the whole reason Grok carries\n // avatars — it is what holds a face from the identity sheet — so swapping them\n // would quietly cost every avatar clip its likeness.\n //\n // What it adds over the older one: 1080p, and 15s rather than 10.\n label: \"Grok Imagine Video 1.5\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // `cents_per_video_output_second_*`: 8c, 14c and 25c. No audio, so both\n // columns are the same rate rather than one being wrong.\n usd: {\n silent: 0.14,\n audio: 0.14,\n byResolution: {\n \"480p\": { silent: 0.08, audio: 0.08 },\n \"720p\": { silent: 0.14, audio: 0.14 },\n \"1080p\": { silent: 0.25, audio: 0.25 },\n },\n },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"] },\n resolution: { kind: \"string\", enum: [\"480p\", \"720p\", \"1080p\"] },\n duration: { kind: \"number\", enum: GROK_IMAGINE_15_DURATIONS },\n },\n },\n \"alibaba/wan-3.0\": {\n // 2.7's successor on every axis that matters: 2–30s against 2–10, 1080p and 480p\n // as well as 720p, and it keeps the native audio that made 2.7 the landscape\n // talking-head option.\n //\n // The ratios are what it ADVERTISES. 2.7 advertised five and delivered 16:9 for\n // all of them — measured, twice, which is why its enum here is a single value. That\n // was a different model and this one is not gated on its predecessor's lie, but\n // the first vertical clip anyone renders here is worth looking at before a campaign\n // is built on it.\n //\n // No `last_frame`: 2.7 takes one and 3.0 does not, so a canvas that ends a shot on\n // a chosen frame stays on 2.7.\n label: \"Alibaba Wan 3.0\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // Priced per resolution where 2.7 was flat, and the rate still includes\n // audio: there is no separate audio SKU. `duration_seconds_480p|720p|1080p`.\n usd: {\n silent: 0.1,\n audio: 0.1,\n byResolution: {\n \"480p\": { silent: 0.05, audio: 0.05 },\n \"720p\": { silent: 0.1, audio: 0.1 },\n \"1080p\": { silent: 0.2, audio: 0.2 },\n },\n },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:4\"] },\n resolution: { kind: \"string\", enum: [\"480p\", \"720p\", \"1080p\"] },\n duration: { kind: \"number\", enum: WAN_30_DURATIONS },\n seed: { kind: \"number\" },\n generate_audio: { kind: \"boolean\" },\n },\n },\n \"runway/gen-4.5\": {\n // One resolution, two shapes, and silent — `generate_audio: false` in its listing,\n // so a talking head belongs on Wan or Omni. First frame only, no reference.\n label: \"Runway Gen-4.5\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n // One flat rate, one resolution: `cents_per_second_output: 12`. Silent.\n usd: { silent: 0.12, audio: 0.12 },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\"] },\n resolution: { kind: \"string\", enum: [\"720p\"] },\n duration: { kind: \"number\", enum: RUNWAY_45_DURATIONS },\n seed: { kind: \"number\" },\n },\n },\n \"alibaba/happyhorse-1.1\": {\n // Third reference-capable option: strong identity, correct ratios, no audio.\n // First-frame only (no `last_frame`), so it cannot close a beat on a\n // specified end pose the way Wan and Veo can.\n label: \"Alibaba HappyHorse 1.1\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n usd: { silent: 0.1278, audio: 0.1278, byResolution: { \"720p\": { silent: 0.0988, audio: 0.0988 } } },\n minBilledSeconds: MIN_BILLED_SECONDS,\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\", \"1:1\", \"4:3\", \"3:4\", \"21:9\", \"9:21\"] },\n resolution: { kind: \"string\", enum: [\"720p\", \"1080p\"] },\n duration: { kind: \"number\", enum: HAPPYHORSE_DURATIONS },\n seed: { kind: \"number\" },\n },\n },\n \"google/veo-3.1-lite\": {\n // The cheapest way to animate a still that carries a real face: same Veo\n // family and the same escape from ByteDance's filter, at $0.05/s with audio\n // at 720p against Veo 3.1's $0.40. That 8x is the difference between an\n // avatar clip you iterate on and one you think twice about.\n label: \"Google Veo 3.1 Lite\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n usd: { silent: 0.05, audio: 0.08, byResolution: { \"720p\": { silent: 0.03, audio: 0.05 } } },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n negative_prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\"] },\n resolution: { kind: \"string\", enum: [\"720p\", \"1080p\"] },\n duration: { kind: \"number\", enum: [4, 6, 8] },\n seed: { kind: \"number\" },\n generate_audio: { kind: \"boolean\" },\n person_generation: { kind: \"string\", enum: [\"allow_all\", \"allow_adult\"] },\n enhance_prompt: { kind: \"boolean\" },\n conditioning_scale: { kind: \"number\" },\n },\n },\n \"google/veo-3.1-fast\": {\n // Cheap test/iteration model. Forwarded by the backend via\n // `provider.options.google-vertex.parameters` (camelCased on the wire).\n label: \"Google Veo 3.1 Fast\",\n route: OPENROUTER_ROUTE,\n billing: {\n kind: \"per_second\",\n usd: {\n silent: 0.1,\n audio: 0.12,\n byResolution: { \"720p\": { silent: 0.08, audio: 0.1 }, \"4K\": { silent: 0.25, audio: 0.3 } },\n },\n },\n inputs: [],\n optional_inputs: [{ kind: \"image\", mimes: OPENROUTER_IMAGE_MIMES }],\n required: [\"prompt\"],\n params: {\n prompt: { kind: \"string\" },\n negative_prompt: { kind: \"string\" },\n aspect_ratio: { kind: \"string\", enum: [\"16:9\", \"9:16\"] },\n resolution: { kind: \"string\", enum: [\"720p\", \"1080p\", \"4K\"] },\n duration: { kind: \"number\", enum: [4, 6, 8] },\n seed: { kind: \"number\", integer: true, min: 0, max: VEO_SEED_MAX },\n generate_audio: { kind: \"boolean\" },\n // Image-to-video and EU/UK/CH/MENA regions cap this at `allow_adult`;\n // `allow_all` is text-to-video only. Allow both so an image-conditioned\n // Veo clip (the real-face fallback) validates.\n person_generation: { kind: \"string\", enum: [\"allow_all\", \"allow_adult\"] },\n enhance_prompt: { kind: \"boolean\" },\n conditioning_scale: { kind: \"number\" },\n },\n },\n },\n\n video_deconstruct: {\n // Replication-grade video extraction — the forensic system prompts live on\n // the backend; `focus` appends emphasis only. Frames ride on a throwaway\n // Mux asset; analysis runs on Gemini with the video as a file part.\n \"~google/gemini-flash-latest\": {\n label: \"Google Gemini Flash (latest, Video)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"video\", mimes: DECONSTRUCT_VIDEO_MIMES }],\n required: [],\n params: {\n mode: { kind: \"string\", enum: [\"full\", \"index\"] },\n language: { kind: \"string\" },\n max_scenes: { kind: \"number\", min: 1, max: 60 },\n focus: { kind: \"string\" },\n start_s: { kind: \"number\", min: 0 },\n end_s: { kind: \"number\", min: 0 },\n max_clip_s: { kind: \"number\", min: 1, max: 60 },\n transcriber: { kind: \"string\", enum: [\"groq\", \"deepgram\"] },\n // Real visual shot-cut timestamps (seconds) from local PySceneDetect;\n // scene boundaries snap onto them so frames never straddle a hard cut.\n // Shape (number[]) is enforced by the node + backend Zod schemas.\n shot_cuts: { kind: \"json\" },\n },\n },\n \"~google/gemini-pro-latest\": {\n label: \"Google Gemini Pro (latest, Video)\",\n route: OPENROUTER_ROUTE,\n billing: USAGE_BILLED,\n inputs: [{ kind: \"video\", mimes: DECONSTRUCT_VIDEO_MIMES }],\n required: [],\n params: {\n mode: { kind: \"string\", enum: [\"full\", \"index\"] },\n language: { kind: \"string\" },\n max_scenes: { kind: \"number\", min: 1, max: 60 },\n focus: { kind: \"string\" },\n start_s: { kind: \"number\", min: 0 },\n end_s: { kind: \"number\", min: 0 },\n max_clip_s: { kind: \"number\", min: 1, max: 60 },\n transcriber: { kind: \"string\", enum: [\"groq\", \"deepgram\"] },\n // See gemini-flash-latest above — local PySceneDetect shot-cut timestamps.\n shot_cuts: { kind: \"json\" },\n },\n },\n },\n\n video_lipsync: {\n // Formerly `fal/veed-lipsync`, which named a provider and a model it had stopped\n // running on; that id stays accepted through `MODEL_ALIASES`.\n \"replicate/sync-lipsync-2\": {\n label: \"Sync Labs Lipsync 2 (Replicate)\",\n route: { provider: \"replicate\", wireModel: \"sync/lipsync-2\" },\n billing: { kind: \"formula\", formula: \"lipsync-minutes\" },\n inputs: [\n { kind: \"video\", mimes: REPLICATE_VIDEO_MIMES },\n { kind: \"audio\", mimes: REPLICATE_AUDIO_MIMES },\n ],\n required: [],\n params: {},\n },\n },\n\n tts: {\n \"elevenlabs/eleven_v3\": {\n // eleven_v3 does NOT yet support context stitching\n // (previous_text/next_text/*_request_ids). ElevenLabs returns a clean\n // `unsupported_model` 400 if you pass them.\n label: \"ElevenLabs Eleven v3\",\n route: { provider: \"elevenlabs\", wireModel: \"eleven_v3\" },\n billing: { kind: \"formula\", formula: \"elevenlabs-characters\" },\n inputs: [],\n required: [\"text\", \"voice\"],\n params: {\n text: { kind: \"string\", maxLength: ELEVENLABS_MAX_TEXT_CHARS },\n voice: { kind: \"string\" },\n language_code: { kind: \"string\" },\n stability: { kind: \"number\", min: 0, max: 1 },\n similarity_boost: { kind: \"number\", min: 0, max: 1 },\n voice_settings: { kind: \"json\" },\n seed: { kind: \"number\", min: 0, max: 4_294_967_295 },\n pronunciation_dictionary_locators: { kind: \"json\" },\n apply_text_normalization: { kind: \"string\", enum: [\"auto\", \"on\", \"off\"] },\n apply_language_text_normalization: { kind: \"boolean\" },\n with_timestamps: { kind: \"boolean\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n },\n },\n },\n\n audio_voice_convert: {\n \"elevenlabs/eleven_multilingual_sts_v2\": {\n // Speech-to-speech / Voice Changer: re-voice an existing audio clip in the\n // TARGET voice, preserving timing/prosody. Used to normalize a talking-head\n // clip's native (generator-chosen) voice into ONE consistent brand voice.\n label: \"ElevenLabs Voice Changer (multilingual STS v2)\",\n route: { provider: \"elevenlabs\", wireModel: \"eleven_multilingual_sts_v2\" },\n billing: { kind: \"formula\", formula: \"elevenlabs-voice-convert-minutes\" },\n inputs: [{ kind: \"audio\", mimes: REPLICATE_AUDIO_MIMES }],\n required: [\"voice\"],\n params: {\n voice: { kind: \"string\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n remove_background_noise: { kind: \"boolean\" },\n },\n },\n },\n\n music: {\n \"elevenlabs/music-v1\": {\n label: \"ElevenLabs Music v1 (Compose)\",\n route: { provider: \"elevenlabs\", wireModel: ELEVENLABS_MUSIC_ENGINE },\n billing: { kind: \"formula\", formula: \"elevenlabs-music-seconds\" },\n inputs: [],\n required: [],\n params: {\n prompt: { kind: \"string\" },\n composition_plan: { kind: \"json\" },\n music_length_ms: { kind: \"number\", min: 3000, max: ELEVENLABS_MAX_MUSIC_LENGTH_MS },\n seed: { kind: \"number\" },\n force_instrumental: { kind: \"boolean\" },\n respect_sections_durations: { kind: \"boolean\" },\n with_timestamps: { kind: \"boolean\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n },\n },\n \"elevenlabs/video-background-music-v1\": {\n label: \"ElevenLabs Video Background Music v1\",\n route: { provider: \"elevenlabs\", wireModel: ELEVENLABS_MUSIC_ENGINE },\n billing: { kind: \"formula\", formula: \"elevenlabs-music-seconds\" },\n inputs: [{ kind: \"video\", mimes: REPLICATE_VIDEO_MIMES }],\n required: [],\n params: {\n description: { kind: \"string\" },\n tags: { kind: \"json\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n },\n },\n },\n\n dialogue: {\n \"elevenlabs/eleven_v3\": {\n label: \"ElevenLabs Eleven v3 (Dialogue)\",\n route: { provider: \"elevenlabs\", wireModel: \"eleven_v3\" },\n billing: { kind: \"formula\", formula: \"elevenlabs-characters\" },\n inputs: [],\n required: [\"inputs\"],\n params: {\n inputs: { kind: \"json\" },\n language_code: { kind: \"string\" },\n settings: { kind: \"json\" },\n seed: { kind: \"number\", min: 0, max: 4_294_967_295 },\n apply_text_normalization: { kind: \"string\", enum: [\"auto\", \"on\", \"off\"] },\n with_timestamps: { kind: \"boolean\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n },\n },\n },\n\n sound_effect: {\n \"elevenlabs/eleven_text_to_sound_v2\": {\n label: \"ElevenLabs Text-to-Sound v2\",\n route: { provider: \"elevenlabs\", wireModel: \"eleven_text_to_sound_v2\" },\n billing: { kind: \"formula\", formula: \"elevenlabs-sound-effect-seconds\" },\n inputs: [],\n required: [\"text\"],\n params: {\n text: { kind: \"string\" },\n duration_seconds: { kind: \"number\", min: 0.5, max: 30 },\n prompt_influence: { kind: \"number\", min: 0, max: 1 },\n loop: { kind: \"boolean\" },\n output_format: { kind: \"string\", enum: ELEVENLABS_OUTPUT_FORMATS },\n },\n },\n },\n};\n\n// ---------------------------------------------------------------------------\n// Legacy ids\n// ---------------------------------------------------------------------------\n\n/**\n * Ids that stored canvases still carry, mapped to the registered id they now mean.\n *\n * Every entry here is an id that named the wrong thing: all three were priced and\n * labelled on fal.ai and have run on Replicate models since, so the id told a reader\n * a provider and a model the call never reached. They are aliases rather than\n * deletions because `baker canvas run --slug` restores a recorded canvas verbatim, and a\n * canvas is never edited behind its author's back. `canonicalModelId` resolves them\n * at the two doors a node comes in through (the CLI node schema and the backend\n * handler); the canvas file and its cache key keep the id they were written with.\n */\nexport const MODEL_ALIASES: Readonly<Record<string, string>> = {\n \"fal/veed-lipsync\": \"replicate/sync-lipsync-2\",\n \"fal/veed-video-background-removal\": \"replicate/birefnet-video\",\n \"fal/birefnet-v2\": \"replicate/birefnet\",\n};\n\n/** The registered id a model id means: itself, or what its legacy alias now points at. */\nexport function canonicalModelId(model: string): string {\n // Own keys only, so a prototype name (\"toString\") is never read as an alias. Not\n // `Object.hasOwn`: the backend typechecks this package under `lib: ES2021`.\n return Object.keys(MODEL_ALIASES).includes(model) ? (MODEL_ALIASES[model] as string) : model;\n}\n\n/** A legacy id together with the id it stands for, for building node param enums. */\nexport function modelIdsAccepted(canonical: string): [string, ...string[]] {\n return [canonical, ...Object.keys(MODEL_ALIASES).filter((legacy) => MODEL_ALIASES[legacy] === canonical)];\n}\n\n// ---------------------------------------------------------------------------\n// Service models: nodes with a provider model but no per-model param gate\n// ---------------------------------------------------------------------------\n\ntype ServiceModelSpec = { label: string; route: ProviderRoute; billing: ModelBilling };\n\n/**\n * Models behind nodes the registry does not validate param by param: each node runs\n * exactly one model, so there is no choice to gate, but there is still a provider,\n * a wire id and a price, and those belong here with every other model's.\n */\nexport const SERVICE_MODELS = {\n video_background_remove: {\n \"replicate/birefnet-video\": {\n label: \"BiRefNet Video (Replicate)\",\n route: { provider: \"replicate\", wireModel: \"sprited/birefnet-video\" },\n billing: { kind: \"formula\", formula: \"video-background-remove-frames\" },\n },\n },\n image_background_remove: {\n \"replicate/birefnet\": {\n label: \"BiRefNet (Replicate)\",\n route: { provider: \"replicate\", wireModel: \"men1scus/birefnet\" },\n billing: { kind: \"formula\", formula: \"image-background-remove-call\" },\n },\n },\n // Keyed by the `transcriber` param value, which is how a node picks between them.\n video_transcribe: {\n groq: {\n label: \"Groq Whisper Large v3 Turbo\",\n route: { provider: \"groq\", wireModel: \"whisper-large-v3-turbo\" },\n billing: { kind: \"formula\", formula: \"transcription\" },\n },\n deepgram: {\n label: \"Deepgram Nova-3\",\n route: { provider: \"deepgram\", wireModel: \"nova-3\" },\n billing: { kind: \"formula\", formula: \"transcription\" },\n },\n },\n} as const satisfies Record<string, Record<string, ServiceModelSpec>>;\n\n/** The one model a service node runs, looked up through `canonicalModelId`; throws for an id nothing registers. */\nexport function serviceRoute<N extends keyof typeof SERVICE_MODELS>(node: N, model: string): ProviderRoute {\n const models: Readonly<Record<string, ServiceModelSpec>> = SERVICE_MODELS[node];\n const spec = models[canonicalModelId(model)];\n if (!spec) throw new Error(`unknown model \"${model}\" for ${node}`);\n return spec.route;\n}\n\n/** The Whisper / Nova route a transcriber choice runs on. */\nexport function transcriberRoute(transcriber: \"groq\" | \"deepgram\"): ProviderRoute {\n return SERVICE_MODELS.video_transcribe[transcriber].route;\n}\n\n// ---------------------------------------------------------------------------\n// Provider routes\n// ---------------------------------------------------------------------------\n\n/** The route a registered model runs on, or undefined when nothing registers it. Legacy ids resolve. */\nexport function routeOf(kind: NodeKind, model: string): ProviderRoute | undefined {\n const canonical = canonicalModelId(model);\n const spec = MODEL_REGISTRY[kind]?.[canonical];\n if (!spec) return undefined;\n return { provider: spec.route.provider, wireModel: spec.route.wireModel ?? canonical };\n}\n\n/**\n * The route a registered model runs on. Throws for an unknown id rather than\n * guessing one: a fallback here is how a stored canvas silently re-renders on a\n * model it was never rendered on.\n */\nexport function providerRoute(kind: NodeKind, model: string): ProviderRoute {\n const route = routeOf(kind, model);\n if (!route) throw new Error(`unknown model \"${model}\" for node \"${kind}\": it has no provider route`);\n return route;\n}\n\n/** The published per-second rate a video model is quoted at, or undefined when it is priced another way. */\nexport function perSecondBilling(model: string): { usd: VideoSecondRate; minBilledSeconds?: number } | undefined {\n const billing = MODEL_REGISTRY.video_generate[model]?.billing;\n return billing?.kind === \"per_second\" ? billing : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Roles: which model does which job, named once\n// ---------------------------------------------------------------------------\n\n/**\n * Every job a scaffolder or backend path hands to a fixed model, named here so the\n * id lives in one place. Each is typed to the node kind it runs as, and a test checks\n * the id is registered for that kind: a reference sheet asked of an image model\n * that is not a sheet model is an `unknown_model` node, found only when it runs.\n *\n * These are per-surface choices, not one default: `DEFAULT_IMAGE_GENERATE_MODEL` is\n * Gemini Flash for cheap, high-volume image work, while an ad's frames are pinned to\n * GPT Image 2.5 because each is the opening still a whole clip is built from.\n */\nexport const MODEL_ROLES = {\n /** Ad frames and static-ad plates: the most faithful text and identity reproduction. */\n adImage: { kind: \"image_generate\", model: \"openai/gpt-image-2.5-sunburst\" },\n /**\n * The multi-view reference sheet a recurring element is fused on. Gemini 3 Pro makes\n * the cleanest, best-labelled turnaround, worth it for the ONE reference every frame\n * of an ad grounds on.\n */\n referenceSheet: { kind: \"image_reference_sheet\", model: \"google/gemini-3-pro-image\" },\n /** Re-framing a static ad whose copy is baked into the pixels. */\n aspectAdapt: { kind: \"image_aspect_adapt\", model: \"google/gemini-3-pro-image\" },\n /**\n * The judge comparing candidate plates. Flash, not Pro: Pro measured 5 credits for\n * one pick, a tenth of the render it judges.\n */\n plateJudge: { kind: \"image_select\", model: \"~google/gemini-flash-latest\" },\n /** The static-ad blueprint extraction: the densest read, so Pro. */\n adDescribe: { kind: \"image_describe\", model: \"~google/gemini-pro-latest\" },\n /** The element-selection and layout passes over a blueprint. */\n blueprintAnalysis: { kind: \"text_generate\", model: \"~google/gemini-flash-latest\" },\n /**\n * The viewer an ad reaches, played before a static ad renders (`baker canvas viewer-check`):\n * says from the copy and the scene what is offered, to whom, and what to do next. Text\n * only and asked once per ad and viewer, so Flash; the calibration set (E8) decides.\n */\n viewerCheck: { kind: \"text_generate\", model: \"~google/gemini-flash-latest\" },\n /**\n * Picks the customer truths out of reviews, testimonials and threads (`baker research\n * customers`): returns exact spans the code then checks against the source. Text only and\n * one call per batch, so Flash.\n */\n customerInsights: { kind: \"text_generate\", model: \"~google/gemini-flash-latest\" },\n /**\n * The same viewer shown the FINISHED static ad at phone size for about three seconds, with no\n * brand and no brief: what is sold, whether it is for them, what to do next, what stopped them\n * (`glance-check.ts`). One small picture per run, so Flash.\n */\n glanceCheck: { kind: \"image_describe\", model: \"~google/gemini-flash-latest\" },\n /**\n * Reads every line of text and the logo of a finished static ad WITH its box, for the\n * copy and safe-area checks. Bench E10 (2026-09-30): word F1 0.994, box IoU 0.937, 4.3 s,\n * about $0.005 an image on `google/gemini-3.8-flash`, which this alias resolves to;\n * tesseract and PaddleOCR missed the 0.95 F1 gate.\n */\n textBoxReader: { kind: \"image_describe\", model: \"~google/gemini-flash-latest\" },\n /**\n * Scene-by-scene video extraction. Flash: ~2-3x faster per call than Pro, which keeps\n * each per-scene step inside the action time budget.\n */\n videoDeconstruct: { kind: \"video_deconstruct\", model: \"~google/gemini-flash-latest\" },\n /** The backend `image_search` agent loop that picks search tools and queries. */\n imageSearch: { kind: \"text_generate\", model: \"~google/gemini-flash-latest\" },\n voiceover: { kind: \"tts\", model: \"elevenlabs/eleven_v3\" },\n soundEffect: { kind: \"sound_effect\", model: \"elevenlabs/eleven_text_to_sound_v2\" },\n musicBed: { kind: \"music\", model: \"elevenlabs/music-v1\" },\n /** Re-voices a talking clip's native audio into one consistent brand voice. */\n voiceConvert: { kind: \"audio_voice_convert\", model: \"elevenlabs/eleven_multilingual_sts_v2\" },\n /** The scored video router's candidates, one per job it can route to. */\n videoRouterWorkhorse: { kind: \"video_generate\", model: \"bytedance/seedance-2.0\" },\n videoRouterRealFace: { kind: \"video_generate\", model: \"google/veo-3.1\" },\n videoRouterRealFaceEconomy: { kind: \"video_generate\", model: \"google/veo-3.1-fast\" },\n videoRouterMotion: { kind: \"video_generate\", model: \"kwaivgi/kling-v3.0-pro\" },\n imageDefault: { kind: \"image_generate\", model: DEFAULT_IMAGE_GENERATE_MODEL },\n videoDefault: { kind: \"video_generate\", model: DEFAULT_VIDEO_GENERATE_MODEL },\n avatarVideo: { kind: \"video_generate\", model: DEFAULT_AVATAR_VIDEO_MODEL },\n} as const satisfies Record<string, { kind: NodeKind; model: string }>;\n\nexport type ModelRole = keyof typeof MODEL_ROLES;\n\nexport const AD_IMAGE_MODEL = MODEL_ROLES.adImage.model;\nexport const REFERENCE_SHEET_MODEL = MODEL_ROLES.referenceSheet.model;\nexport const ASPECT_ADAPT_MODEL = MODEL_ROLES.aspectAdapt.model;\nexport const PLATE_JUDGE_MODEL = MODEL_ROLES.plateJudge.model;\nexport const AD_DESCRIBE_MODEL = MODEL_ROLES.adDescribe.model;\nexport const BLUEPRINT_ANALYSIS_MODEL = MODEL_ROLES.blueprintAnalysis.model;\nexport const VIEWER_CHECK_MODEL = MODEL_ROLES.viewerCheck.model;\nexport const CUSTOMER_INSIGHTS_MODEL = MODEL_ROLES.customerInsights.model;\nexport const GLANCE_CHECK_MODEL = MODEL_ROLES.glanceCheck.model;\nexport const TEXT_BOX_READER_MODEL = MODEL_ROLES.textBoxReader.model;\nexport const VIDEO_DECONSTRUCT_MODEL = MODEL_ROLES.videoDeconstruct.model;\nexport const IMAGE_SEARCH_MODEL = MODEL_ROLES.imageSearch.model;\nexport const VOICEOVER_MODEL = MODEL_ROLES.voiceover.model;\nexport const SOUND_EFFECT_MODEL = MODEL_ROLES.soundEffect.model;\nexport const MUSIC_BED_MODEL = MODEL_ROLES.musicBed.model;\nexport const VOICE_CONVERT_MODEL = MODEL_ROLES.voiceConvert.model;\n\n/**\n * Whether `model` is registered for `kind` (legacy ids included). The question a\n * caller asks before trusting a model id it did not pick itself: a flag, a stored\n * rebuild context.\n */\nexport function isRegisteredModel(kind: NodeKind, model: string): boolean {\n return MODEL_REGISTRY[kind]?.[canonicalModelId(model)] !== undefined;\n}\n\n/**\n * Every ratio at least one curated image model renders, in first-seen order. What a\n * requested ad format can be when the plate is rendered for it; the chosen model then\n * snaps to its nearest (`nearestSupportedAspectRatio`).\n */\nexport function imageGenerateAspectRatios(): string[] {\n const seen = new Set<string>();\n for (const model of IMAGE_GENERATE_MODELS) {\n for (const ratio of supportedAspectRatios(\"image_generate\", model) ?? []) seen.add(ratio);\n }\n return [...seen];\n}\n\n// ---------------------------------------------------------------------------\n// Capability lookups\n// ---------------------------------------------------------------------------\n//\n// Scaffolders build node params for whatever model the caller picked, and the\n// curated models do NOT agree on what they accept: gpt-image-2 has no\n// `image_size` and no 4:5/5:4, Gemini flash goes down to 0.5K and out to 8:1.\n// Reading the registry keeps that knowledge in one place instead of a per-model\n// `if` at every site that emits an `image_generate` node.\n\n/** Whether a model accepts a canonical param at all — false for unknown models. */\n/**\n * Models whose output ALWAYS carries an audio track, with no knob to turn it off.\n *\n * Needed because \"no `generate_audio` param\" means two opposite things in this\n * registry: Omni rejects the param because audio is always on and cannot be\n * disabled, while `alibaba/wan-2.6` rejects it because it has no audio at all.\n * A caller reading only `generateAudio` cannot tell them apart — and would read\n * the DEFAULT video model, which always speaks, as silent.\n */\nconst ALWAYS_AUDIO_VIDEO_MODELS = [\"google/gemini-omni-1.1-flash\", \"google/gemini-omni-flash\"] as const;\n\n/**\n * Whether a rendered clip has a voice on it — the question anything doing\n * post-processing on the audio has to answer before it spends money.\n *\n * `generateAudio` is what the batch RECORDED, which is `undefined` whenever the\n * model exposes no such param; that is why the model has to be consulted too.\n */\nexport function clipCarriesAudio(model: string, generateAudio: boolean | undefined): boolean {\n if ((ALWAYS_AUDIO_VIDEO_MODELS as readonly string[]).includes(model)) return true;\n if (!supportsParam(\"video_generate\", model, \"generate_audio\")) return false;\n return generateAudio === true;\n}\n\nexport function supportsParam(kind: NodeKind, model: string, param: string): boolean {\n return MODEL_REGISTRY[kind]?.[model]?.params[param] !== undefined;\n}\n\n/**\n * How long a prompt this model reads, or undefined when it advertises no cap.\n *\n * Undefined is a real answer and must not be treated as zero or as a default —\n * the Gemini image models publish no character limit, and inventing one would\n * refuse briefs that render fine today.\n */\n/**\n * The clip length the Studio asks for when the caller pins none.\n *\n * Distinct from `DEFAULT_VIDEO_DURATION_S` in `videoCost`, which is what the\n * PRICE falls back to for a clip that never declared a length. This one is a\n * product choice about what to render, and the two only look alike today.\n */\nexport const DEFAULT_CLIP_DURATION_S = 5;\n\n/**\n * What a clip gets when nobody pins a length, given whether anyone speaks in it.\n *\n * Five seconds is enough to show something and nowhere near enough to SAY something.\n * A line written for an ad is a full sentence, and delivered inside five seconds it comes\n * out rushed — the words land on top of each other, the mouth runs ahead of the meaning,\n * and the take is unusable however good the picture is. Eight is the floor where a\n * sentence can be spoken at the pace a person would actually use.\n *\n * Silent clips keep the five: b-roll, a product turning, a logo landing. They are not\n * saying anything, so the extra seconds buy nothing and cost real money — a clip is\n * billed by the second.\n */\nexport function clipSecondsFor({ speaks, spokenWords }: { speaks: boolean; spokenWords?: number }): number {\n if (!speaks) return DEFAULT_CLIP_DURATION_S;\n // Sized to the script, because the model does not run over when the line is too long —\n // it speeds up, and the take comes back with syllables swallowed and endings clipped.\n // Measured on a real one: 40 words asked for in 8s is 5 words a second against a\n // natural 2.5, and it read as the avatar mumbling.\n const needed = Math.ceil((spokenWords ?? 0) / SPOKEN_WORDS_PER_SECOND);\n return Math.max(SPOKEN_CLIP_DURATION_S, needed);\n}\n\n/** Unhurried ad delivery, ~150 wpm. The natural rate, not the fastest intelligible one. */\nexport const SPOKEN_WORDS_PER_SECOND = 2.5;\n\n/**\n * A quote that opens or closes a spoken line, rather than an apostrophe inside a word.\n *\n * Position decides, not the character. A bare `'` used to count as a delimiter wherever it\n * appeared, so two ordinary possessives paired up and the DIRECTION between them was read\n * as speech: \"Marta's kitchen \\u2026 about the family's energy bill\" scored 14 words of dialogue\n * in a brief where nobody had spoken yet, which lengthened a clip billed by the second and\n * told the agent a silent b-roll brief had a line in it.\n *\n * So a delimiter is flanked-checked \\u2014 a quote opening or closing a line never has a letter\n * on its inner side, and the apostrophe in `Marta's` always does. That check has to be made\n * on BOTH sides of the capture, and missing the second half is its own bug: barring the\n * apostrophe from the line's CONTENT as well left an English line unmatchable, because the\n * first contraction ended the run before it reached eight characters. `\"We'll cut your bill\n * in half\"` scored zero \\u2014 read as nobody speaking, which drops the clip to the bare floor\n * and silences the two hints that exist to catch exactly that.\n *\n * Hence the alternation: any non-quote character, or an apostrophe with a letter on both\n * sides. The branches are disjoint, so there is nothing for the engine to backtrack over.\n */\nconst SPOKEN_LINE =\n /(?<![\\p{L}\\p{N}])['\"\\u201c\\u201d\\u2018\\u2019]((?:[^'\"\\u201c\\u201d\\u2018\\u2019]|(?<=\\p{L})['\\u2019](?=\\p{L})){8,})['\"\\u201c\\u201d\\u2018\\u2019](?![\\p{L}\\p{N}])/gu;\n\n/**\n * The words a motion brief asks to be SAID, which is the quoted part of it.\n *\n * The direction around the line (\"she walks in and says\") is performed, not spoken, and\n * counting it would shorten every line for no reason.\n */\nexport function spokenWordsIn(prompt: string | undefined): number {\n if (!prompt) return 0;\n const quoted = [...prompt.matchAll(SPOKEN_LINE)].map((m) => m[1] ?? \"\");\n const text = quoted.join(\" \").trim();\n return text ? text.split(/\\s+/).filter(Boolean).length : 0;\n}\n\n/** The floor for a clip with a line in it. See `clipSecondsFor`. */\nexport const SPOKEN_CLIP_DURATION_S = 8;\n\n/**\n * The clip length a request settles on when the caller pins none: the nearest\n * length the model renders to `preferred`, ties going to the shorter.\n *\n * Exported because two callers have to agree on it — the request that sends the\n * duration and the estimate that quotes its price. If the estimate leaves the\n * duration unset while the request resolves and sends one, the two quote\n * different clips, and the caller is told a number the charge never matches.\n */\nexport function nearestClipDuration(model: string, preferred: number): number {\n const schema = MODEL_REGISTRY.video_generate[model]?.params.duration;\n const allowed = schema?.kind === \"number\" ? schema.enum : undefined;\n if (!allowed?.length || allowed.includes(preferred)) return preferred;\n return [...allowed].sort((a, b) => Math.abs(a - preferred) - Math.abs(b - preferred) || a - b)[0] as number;\n}\n\n/**\n * Models that render no sound at all — `generate_audio: false` or absent on their\n * OpenRouter listing.\n *\n * Needed because \"no audio toggle\" means two opposite things. Omni has none because it\n * ALWAYS renders synchronized sound; Runway has none because it never renders any. The\n * Studio hid the same control for both, so a marketer saw nothing either way and found\n * out which kind they had picked by watching a silent clip they had already paid for.\n */\nconst SILENT_VIDEO_MODELS: ReadonlySet<string> = new Set([\n \"runway/gen-4.5\",\n \"x-ai/grok-imagine-video\",\n \"x-ai/grok-imagine-video-1.5\",\n \"alibaba/happyhorse-1.1\",\n]);\n\n/** What a model does about sound: the caller's choice, always on, or never. */\nexport type ClipAudioMode = \"toggle\" | \"always\" | \"never\";\n\n/** Whether this model renders sound, and whether the caller gets a say. */\nexport function clipAudioMode(model: string): ClipAudioMode {\n if (supportsParam(\"video_generate\", model, \"generate_audio\")) return \"toggle\";\n return SILENT_VIDEO_MODELS.has(model) ? \"never\" : \"always\";\n}\n\n/** Every clip length a model renders, ascending, or undefined when it declares no set. */\nexport function clipDurationsFor(model: string): number[] | undefined {\n const schema = MODEL_REGISTRY.video_generate[model]?.params.duration;\n const allowed = schema?.kind === \"number\" ? schema.enum : undefined;\n return allowed?.length ? [...allowed].sort((a, b) => a - b) : undefined;\n}\n\n/** The longest clip a model will render, or undefined when it declares no set. */\nexport function maxClipSeconds(model: string): number | undefined {\n const allowed = clipDurationsFor(model);\n return allowed ? (allowed[allowed.length - 1] as number) : undefined;\n}\n\n/** One clip's worth of script on a given model. */\nexport interface ScriptSplit {\n /** How many clips the line has to be broken into. */\n clips: number;\n /** Roughly how many words each should carry. */\n wordsPerClip: number;\n /** The longest single clip this model renders. */\n maxSeconds: number;\n}\n\n/**\n * How to break a script that outruns the model, or null when it fits in one clip.\n *\n * Gemini Omni stops at 10 seconds — about 25 words — and a real testimonial is longer\n * than that. Handed the whole thing it does not refuse and it does not truncate: it\n * rushes, which is the failure this exists to prevent. So a script that does not fit is\n * a SEQUENCE, and the number of pieces is arithmetic rather than judgement.\n */\nexport function splitScriptFor(words: number, model: string): ScriptSplit | null {\n const maxSeconds = maxClipSeconds(model);\n if (!maxSeconds) return null;\n const perClip = Math.floor(maxSeconds * SPOKEN_WORDS_PER_SECOND);\n if (words <= perClip) return null;\n const clips = Math.ceil(words / perClip);\n return { clips, wordsPerClip: Math.ceil(words / clips), maxSeconds };\n}\n\nexport function promptMaxLength(kind: NodeKind, model: string): number | undefined {\n const schema = MODEL_REGISTRY[kind]?.[model]?.params.prompt;\n return schema?.kind === \"string\" ? schema.maxLength : undefined;\n}\n\n/**\n * The declared input slot of one kind, or undefined when the model has none.\n *\n * Reads the OPTIONAL slots first because that is where every generate model\n * declares references; the required slots cover the nodes (sheet, adapt) whose\n * source asset is mandatory.\n *\n * ABSENCE is the load-bearing answer here, and it is not the same as a slot with\n * no cap: `undefined` means the model does not read that kind at all, whereas a\n * slot whose `max` is undefined means it reads them with no ceiling we have\n * confirmed. Callers that need \"can it?\" must ask this, not `maxInputSlot`.\n */\nfunction inputSlot(kind: NodeKind, model: string, inputKind: InputKind): InputSpec | undefined {\n const spec = MODEL_REGISTRY[kind]?.[model];\n return (\n spec?.optional_inputs?.find((input) => input.kind === inputKind) ??\n spec?.inputs.find((input) => input.kind === inputKind)\n );\n}\n\n/** How many assets of one kind the model's slot takes; undefined = no cap advertised. */\nexport function maxInputSlot(kind: NodeKind, model: string, inputKind: InputKind): number | undefined {\n return inputSlot(kind, model, inputKind)?.max;\n}\n\n/**\n * How many images this model takes in its reference slot, or undefined when no\n * cap is advertised.\n */\nexport function maxInputReferences(kind: NodeKind, model: string): number | undefined {\n return maxInputSlot(kind, model, \"image\");\n}\n\n/**\n * Every quality step any image model in this registry declares, worst to best.\n *\n * Exported because the CLI's `image_generate` node schema needs a zod enum and had been\n * carrying its own copy — one that stopped at `high` and was never widened when the 2.5\n * ladder gained `xhigh` and `max`. That second copy did not merely drift, it BLOCKED the\n * ad path outright: `scaffold-ad` pins gpt-image-2.5, `bestSupportedQuality` correctly\n * returned `xhigh` for it, and every ad scaffolded then failed its own validation before\n * a single node ran. Which model may use which step is still decided per model, by\n * `validateModelParams` against this registry — this list only says what the word can be.\n */\nexport const IMAGE_QUALITY_STEPS = [\"auto\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\n/**\n * How many images one call to this model returns at most: its `n` ceiling, or 1 for a\n * model that declares no `n`. One is a real answer, never \"unknown\": such a model is\n * sent no `n` and renders one image per call, so N variants from it are N calls.\n */\nexport function maxImagesPerCall(kind: NodeKind, model: string): number {\n const schema = MODEL_REGISTRY[kind]?.[model]?.params.n;\n return schema?.kind === \"number\" && schema.max !== undefined ? schema.max : 1;\n}\n\n/** The quality steps a model accepts, or undefined when it has no `quality` enum. */\nexport function supportedQualities(kind: NodeKind, model: string): readonly string[] | undefined {\n const schema = MODEL_REGISTRY[kind]?.[model]?.params.quality;\n return schema?.kind === \"string\" ? schema.enum : undefined;\n}\n\n/**\n * The quality step to send a model when you want `want`.\n *\n * Undefined when the model has no `quality` knob — Gemini's endpoints do not list one,\n * and the registry deliberately does not declare it, so emitting it fails validation\n * before the call. Otherwise the highest step at or below `want`: a scaffolder writes one\n * canvas for whichever model is pinned, and the older gpt-image entries stop at `high`,\n * where an undeclared value is `unknown_param` rather than a softer picture.\n */\nexport function bestSupportedQuality(kind: NodeKind, model: string, want: string): string | undefined {\n const supported = supportedQualities(kind, model);\n if (!supported) return undefined;\n if (supported.includes(want)) return want;\n // Ordered worst to best in the registry, so the last one at or below `want` wins.\n const ladder = supported.filter((step) => step !== \"auto\");\n const ceiling = ladder.indexOf(want);\n const reachable = ceiling === -1 ? ladder : ladder.slice(0, ceiling);\n return reachable[reachable.length - 1];\n}\n\n/** The ratios a model accepts, or undefined when it has no `aspect_ratio` enum. */\nexport function supportedAspectRatios(kind: NodeKind, model: string): readonly string[] | undefined {\n const schema = MODEL_REGISTRY[kind]?.[model]?.params.aspect_ratio;\n return schema?.kind === \"string\" ? schema.enum : undefined;\n}\n\nfunction ratioValue(ratio: string): number | null {\n const [w, h] = ratio.split(\":\").map(Number);\n if (!w || !h || !Number.isFinite(w) || !Number.isFinite(h)) return null;\n return w / h;\n}\n\n/**\n * Snap a target ratio onto what `model` actually accepts, picking the closest\n * supported shape when the exact one is missing (4:5 → 3:4 on gpt-image-2).\n * Returns the target untouched when the model has no ratio enum or already\n * supports it, so callers can apply this unconditionally.\n */\nexport function nearestSupportedAspectRatio(kind: NodeKind, model: string, target: string): string {\n const supported = supportedAspectRatios(kind, model);\n if (!supported || supported.includes(target)) return target;\n const wanted = ratioValue(target);\n if (wanted === null) return target;\n let best = target;\n let bestDistance = Number.POSITIVE_INFINITY;\n for (const candidate of supported) {\n const value = ratioValue(candidate);\n if (value === null) continue;\n const distance = Math.abs(Math.log(value / wanted));\n if (distance < bestDistance) {\n bestDistance = distance;\n best = candidate;\n }\n }\n return best;\n}\n\n/** Size tiers a model accepts; undefined = the model has no `image_size` param. */\nfunction supportedImageSizes(kind: NodeKind, model: string): readonly string[] | undefined {\n const schema = MODEL_REGISTRY[kind]?.[model]?.params.image_size;\n return schema?.kind === \"string\" ? schema.enum : undefined;\n}\n\nconst IMAGE_SIZE_ORDER = [\"0.5K\", \"1K\", \"2K\", \"4K\"];\n\n/**\n * Clamp a size tier into what the model actually renders — the\n * `nearestSupportedAspectRatio` treatment for size: snap rather than reject, so a\n * caller who asks a 1K-only model (Nano Banana 2 Lite) for the scaffold's default\n * 2K keyframe gets its best available render instead of a hard validation failure.\n *\n * It prefers to snap DOWN (the largest tier at or below the target), because\n * rendering bigger than asked for costs more than the caller agreed to. When the\n * model's FLOOR is above the target it returns that floor — the smallest thing it\n * can render — since there is no cheaper option to offer. No curated model has a\n * floor above `0.5K` today, so that branch is currently unreachable.\n */\nexport function nearestSupportedImageSize(kind: NodeKind, model: string, target: string): string {\n const supported = supportedImageSizes(kind, model);\n if (!supported || supported.includes(target)) return target;\n const wanted = IMAGE_SIZE_ORDER.indexOf(target);\n if (wanted < 0) return target;\n const ranked = supported\n .filter((s) => IMAGE_SIZE_ORDER.includes(s))\n .sort((a, b) => IMAGE_SIZE_ORDER.indexOf(a) - IMAGE_SIZE_ORDER.indexOf(b));\n if (ranked.length === 0) return target;\n const belowOrEqual = ranked.filter((s) => IMAGE_SIZE_ORDER.indexOf(s) <= wanted);\n return belowOrEqual.length > 0 ? (belowOrEqual[belowOrEqual.length - 1] as string) : (ranked[0] as string);\n}\n\n// NOTE: `IMAGE_GENERATE_MODELS` (above) must list exactly the keys of\n// `MODEL_REGISTRY.image_generate`. A compile-time check isn't possible —\n// MODEL_REGISTRY's `Record<string, ModelSpec>` annotation widens its keys to\n// `string` — so the invariant is enforced by `registry.test.ts` instead, which\n// keeps this module a pure, side-effect-free data export.\n","/**\n * Per-model video cost, shared by everything that needs to say what a clip will\n * cost BEFORE it runs: the engine's pre-run estimate, the backend's per-request\n * cost cap, and the studio's prompt bar.\n *\n * It lives in the contract, next to the model registry, because a price is\n * per-model data exactly like a duration enum — and because the estimate and the\n * charge drifting apart is the failure this module exists to prevent. The engine\n * previously quoted a FLAT 50 credits for every clip regardless of duration,\n * resolution, or model, which under-quoted a 1080p 8s clip by roughly 10×.\n */\n\nimport { CREDITS_PER_USD, usdToCredits } from \"./credits\";\nimport { isGeminiOmniModel, isGeminiOmniPreviewModel, isSeedance25Model, perSecondBilling } from \"./registry\";\n\nexport { MIN_BILLED_SECONDS } from \"./registry\";\n\nexport { CREDITS_PER_USD };\n\n/**\n * The opening still an avatar clip renders before it animates anything.\n *\n * On the framed route a clip is TWO billed calls, not one: a gpt-image-2 frame\n * ($0.03 per image, OpenRouter's published `image_output` rate) and then the\n * clip itself. Quoting only the clip understates every avatar take, and a quote\n * shown before spending must never be under the charge.\n */\nexport const OPENING_FRAME_CREDITS = usdToCredits(0.03);\n\n/**\n * Seedance $/second by resolution. 4k bills at the 1080p rate as a conservative\n * floor until a real 4k rate exists.\n */\nexport const SEEDANCE_USD_PER_SECOND: Readonly<Record<string, number>> = {\n \"480p\": 0.18,\n \"720p\": 0.5,\n \"1080p\": 0.62,\n \"4k\": 0.62,\n};\n\n/** What the provider assumes when a clip does not pin a resolution. */\nexport const DEFAULT_VIDEO_RESOLUTION = \"720p\";\n\n/** What a clip costs when the model does not declare a duration. */\nexport const DEFAULT_VIDEO_DURATION_S = 5;\n\nexport function isSeedanceModel(model: string): boolean {\n return model.startsWith(\"bytedance/seedance\");\n}\n\n// ---------------------------------------------------------------------------\n// Seedance 2.5 — billed in VIDEO TOKENS, and the token count is a pure function\n// of the frame rather than a per-second sticker price.\n//\n// OpenRouter publishes `pricing_skus.video_tokens: 0.0000107` and no rate per\n// second. The conversion is exact and was checked against the advertised floor\n// on BOTH Seedance tiers before being written down:\n//\n// tokens = (width × height / 1024) × 24fps × seconds\n//\n// 2.5 cheapest frame 854×480 → 400.3125 × 24 × 0.0000107 = $0.102800/s,\n// against the model page's \"starting at $0.1028 per second\".\n// 2.0 cheapest frame 480×854 → 400.3125 × 24 × 0.0000007 = $0.067252/s,\n// against its page's \"from $0.06726/second\". Two independent hits, so the\n// formula is the provider's, not a coincidence of one rounding.\n//\n// Rates below are the DEAREST frame in each tier (21:9 is wider than 16:9 at the\n// same nominal resolution — 992×432 and 1112×834 are the real maxima), rounded\n// up. Quoting the 16:9 frame instead would under-quote a 21:9 clip, and this\n// module's whole rule is that the estimate must never land under the charge.\n//\n// Two things NOT to re-derive later: audio is free on this tier\n// (`video_tokens_without_audio` is the same 0.0000107, unlike every other model\n// here where sound is a higher rate), and a clip given a VIDEO reference bills at\n// 0.0000064 — cheaper, so ignoring it keeps the estimate conservative.\n// ---------------------------------------------------------------------------\n\n/** OpenRouter `pricing_skus.video_tokens` for `bytedance/seedance-2.5`. */\nexport const SEEDANCE_25_USD_PER_VIDEO_TOKEN = 0.0000107;\n\nexport const SEEDANCE_25_USD_PER_SECOND: Readonly<Record<string, number>> = {\n \"480p\": 0.1075,\n \"720p\": 0.2326,\n};\n\n/** The tier this model renders when a clip pins no resolution — its dearest, so an unpinned clip is never under-quoted. */\nconst SEEDANCE_25_FALLBACK_USD_PER_SECOND = 0.2326;\n\nexport function seedance25UsdPerSecond(resolution: string | undefined): number {\n return SEEDANCE_25_USD_PER_SECOND[resolution ?? DEFAULT_VIDEO_RESOLUTION] ?? SEEDANCE_25_FALLBACK_USD_PER_SECOND;\n}\n\n/** Rate for an unrecognised resolution — the 720p middle tier, never zero. */\nconst FALLBACK_USD_PER_SECOND = 0.5;\n\n/**\n * Seconds a clip is billed for: its length, or the floor its registry entry\n * declares (`minBilledSeconds`), whichever is longer. See `MIN_BILLED_SECONDS`.\n */\nfunction billedSeconds(model: string, duration: number): number {\n const floor = perSecondBilling(model)?.minBilledSeconds;\n return floor === undefined ? duration : Math.max(duration, floor);\n}\n\nexport function seedanceUsdPerSecond(resolution: string | undefined): number {\n return SEEDANCE_USD_PER_SECOND[resolution ?? DEFAULT_VIDEO_RESOLUTION] ?? FALLBACK_USD_PER_SECOND;\n}\n\n// ---------------------------------------------------------------------------\n// Gemini Omni Flash — billed in TOKENS, which come out to a clean per-second rate.\n//\n// Omni is the one model here priced by token rather than by second, but the two\n// reconcile exactly, and on 1.1 they reconcile PER RESOLUTION. Measured on the\n// live API, video output tokens are dead linear in both length and tier: 720p\n// returned 17,376 tokens for 3.008s and 34,752 for 6.016s, and holding the length\n// at 3s the four tiers returned 5,793 / 17,376 / 26,064 / 52,128 — exactly a\n// third, one, one and a half, and three times the 720p rate.\n//\n// The PREVIEW model (`google/gemini-omni-flash`) shares the per-second rate and\n// differs on the TIER, which is the one thing it has to be priced separately for.\n// It accepts a `resolution` and then IGNORES it: asked for 360p it returned a\n// 1280x720 clip and billed 17,376 tokens, the 720p rate, not the 5,793 that tier\n// costs. So it is quoted at 720p whatever was asked, because that is what it\n// renders — passing the requested tier through would quote a 360p request at a\n// third of its charge, which is the failure this module exists to prevent.\n//\n// Historical note, because the previous version of this file encoded it: the\n// preview USED to bill its whole ten-second window whatever length it returned\n// (a 3s and a 4s render both billing ~$1.034). Re-measured, that is gone — 3s\n// now bills 17,376 tokens and 6s 34,752, the same per-second line as 1.1. The\n// window quote came off with it; a flat ~$1.09 for a 3s clip would now be a 3.4x\n// OVER-quote, and being wrong in the safe direction is still being wrong.\n//\n// The one trap both share is the DEFAULT. Omni renders 10s when the prompt names\n// no length — its ceiling, not the module's usual 5s assumption — so an\n// unspecified clip must be quoted at 10s or it is under-quoted by half.\n// ---------------------------------------------------------------------------\n\n/** Google's published rate for Gemini Omni video OUTPUT tokens. */\nexport const GEMINI_OMNI_USD_PER_1M_VIDEO_TOKENS = 17.5;\n/** Video output tokens per second of 720p Omni footage (published, and measured). */\nexport const GEMINI_OMNI_VIDEO_TOKENS_PER_SECOND = 5_792;\n\n/**\n * What each resolution tier costs relative to 720p, measured rather than assumed.\n *\n * Keyed by the exact strings `GEMINI_OMNI_RESOLUTIONS` offers — including the\n * lowercase `4k`, which is the spelling Omni's API accepts and NOT the `4K` Veo\n * takes. A tier missing from this table falls back to 720p below, which is also\n * what the adapter renders when it sends no resolution at all.\n */\nexport const GEMINI_OMNI_RESOLUTION_MULTIPLIER: Readonly<Record<string, number>> = {\n \"360p\": 1 / 3,\n \"720p\": 1,\n \"1080p\": 1.5,\n \"4k\": 3,\n};\n\n/** What Omni renders when a request names no resolution. */\nexport const GEMINI_OMNI_DEFAULT_RESOLUTION = \"720p\";\n\n/**\n * Output tokens each Omni render spends on THINKING, billed at the video rate\n * alongside the picture.\n *\n * A flat allowance rather than a percentage, because that is what it measures as:\n * five renders spanning 3-6 seconds and all four tiers spent 300, 386, 461, 490\n * and 552 thought tokens — no correlation with either axis. So it is 0.6% of a 4K\n * clip and 8% of a 3s 360p draft, and a percentage margin would mis-size it at\n * both ends. 600 sits above every measurement.\n *\n * Without it the cheapest tier is the one that gets under-quoted, which is the\n * one rule this module has: a 3s 360p clip returned 5,793 video tokens and billed\n * $0.1088 against the $0.1014 its video alone predicts.\n */\nexport const GEMINI_OMNI_OVERHEAD_TOKENS = 600;\n\n/**\n * The length an un-timed Omni clip comes back at, on both models — its own\n * ceiling, not the module's usual 5s assumption. So it is the length to quote\n * when `duration` is unset.\n */\nexport const GEMINI_OMNI_WINDOW_SECONDS = 10;\n\n/**\n * USD per second of 720p Omni footage (~$0.101); multiply by the tier above for\n * the rest. Input tokens ($1.50/1M) are left out: a prompt plus a reference image\n * measured ~1.1k tokens — under a fifth of a cent, noise against the video\n * output, and far too small to push the estimate under the charge.\n */\nexport const GEMINI_OMNI_USD_PER_SECOND =\n (GEMINI_OMNI_VIDEO_TOKENS_PER_SECOND * GEMINI_OMNI_USD_PER_1M_VIDEO_TOKENS) / 1_000_000;\n\n/** USD per second of Omni footage at one resolution tier. */\nexport function geminiOmniUsdPerSecond(resolution: string | undefined): number {\n const multiplier = GEMINI_OMNI_RESOLUTION_MULTIPLIER[resolution ?? GEMINI_OMNI_DEFAULT_RESOLUTION] ?? 1;\n return GEMINI_OMNI_USD_PER_SECOND * multiplier;\n}\n\n/**\n * How much MORE a clip costs when it is built on a source clip.\n *\n * A measured ceiling, not a derived formula. One real render — 4s, 480p, one\n * source clip — returned 854x480 exactly as asked and then billed $0.8522\n * against the $0.4112 the per-second table predicts: 2.07x. The extra is not in\n * any published SKU, so it cannot be modelled; what IS certain is its direction,\n * and this module's one rule is that an estimate must never land under the\n * charge. Rounded up from the single measurement rather than fitted to it.\n *\n * Re-measure before trusting this to be tight. It is deliberately loose.\n */\nexport const VIDEO_SOURCE_COST_MULTIPLIER = 2.5;\n\n// ---------------------------------------------------------------------------\n// Seedance 2.5 through fal — the route that renders human faces.\n//\n// fal publishes the same token formula as OpenRouter and charges exactly twice\n// per token ($0.0214 per 1,000 against $0.0107), which is why it is the recovery\n// and not the main road. Both apply the same 0.6 discount when a source clip is\n// wired, and neither charges for image or audio references.\n//\n// tokens = (width x height x (input_video_seconds + output_seconds) x 24) / 1024\n//\n// The input-video term is fal's own, and it is why a clip built on a long source\n// costs more than its own length suggests.\n// ---------------------------------------------------------------------------\n\n/** fal's published rate for this model, per 1,000 video tokens. */\nexport const FAL_SEEDANCE_USD_PER_1K_TOKENS = 0.0214;\n\n/**\n * What a fal render REALLY cost, from the billable units it reports back.\n *\n * fal's `x-fal-billable-units` header is the render's video-token count in\n * thousands — the same unit its price is quoted in — so the charge is one\n * multiplication rather than a re-derivation of the frame. Prefer this to\n * `falSeedanceUsdPerSecond` whenever a response is in hand: the estimate quotes\n * the DEAREST frame of the tier on purpose, which is right for a quote and about\n * 3% too high as a charge (a measured 4s/480p clip billed 38.83 units against the\n * 40.18 the widest frame predicts).\n */\nexport function falSeedanceUsdFromBillableUnits(units: number): number {\n return units * FAL_SEEDANCE_USD_PER_1K_TOKENS;\n}\n\n/** Both routes discount a render that is guided by a source clip. */\nexport const SEEDANCE_SOURCE_CLIP_DISCOUNT = 0.6;\n\n/** Frame dimensions per resolution tier, widest shape first — the dearest frame in each. */\nconst SEEDANCE_DEAREST_FRAME: Readonly<Record<string, { width: number; height: number }>> = {\n \"480p\": { width: 992, height: 432 },\n \"720p\": { width: 1112, height: 834 },\n};\n\n/**\n * What one fal render costs, from fal's own formula.\n *\n * Quoted on the DEAREST frame of the tier for the same reason the OpenRouter\n * table is: a 21:9 clip is wider than a 16:9 one at the same nominal resolution,\n * and quoting the narrow one under-quotes the wide one.\n */\nexport function falSeedanceUsdPerSecond(resolution: string | undefined, buildsOnSourceClip = false): number {\n const frame = SEEDANCE_DEAREST_FRAME[resolution ?? DEFAULT_VIDEO_RESOLUTION] ?? SEEDANCE_DEAREST_FRAME[\"720p\"];\n const tokensPerSecond = ((frame?.width ?? 1112) * (frame?.height ?? 834) * 24) / 1024;\n const rate = (tokensPerSecond * FAL_SEEDANCE_USD_PER_1K_TOKENS) / 1000;\n return buildsOnSourceClip ? rate * SEEDANCE_SOURCE_CLIP_DISCOUNT : rate;\n}\n\nexport interface VideoCostInput {\n model: string;\n duration?: number;\n resolution?: string;\n generateAudio?: boolean;\n /**\n * Whether the clip is built on an existing clip. Costs more than the per-second\n * table says — see `VIDEO_SOURCE_COST_MULTIPLIER`.\n */\n buildsOnSourceClip?: boolean;\n /**\n * Whether the request carries reference IMAGES, which is what decides the route\n * — and therefore the price.\n *\n * A reference image MIGHT contain a face, and a face sends the render to fal at\n * twice the price. Nothing can tell in advance which images have faces, so a\n * request that carries any is quoted at the dearer route. Over-quoting a\n * product shot is the cheap mistake; under-quoting every clip with a person in\n * it is the one this module exists to prevent.\n */\n mayRouteToFal?: boolean;\n}\n\n/**\n * What one Omni clip costs, for either version.\n *\n * The preview ignores the tier and renders 720p, so it is quoted at 720p whatever\n * was asked — honouring the request would quote a 360p clip at a third of its\n * charge, on the one model where asking for 360p changes nothing. `isGeminiOmniModel`\n * matches both ids, so the version test lives here rather than beside the caller.\n */\nfunction geminiOmniCostUsd(model: string, duration: number | undefined, resolution: string | undefined): number {\n const tier = isGeminiOmniPreviewModel(model) ? GEMINI_OMNI_DEFAULT_RESOLUTION : resolution;\n const footage = geminiOmniUsdPerSecond(tier) * (duration ?? GEMINI_OMNI_WINDOW_SECONDS);\n return footage + (GEMINI_OMNI_OVERHEAD_TOKENS * GEMINI_OMNI_USD_PER_1M_VIDEO_TOKENS) / 1_000_000;\n}\n\n/**\n * Seedance 2.0 through Replicate or, for a clip that may be refused as a face\n * and recovered, the fal route, whichever is dearer. The recovery renders 2.5\n * whichever Seedance was asked for and drops a tier 2.5 does not have (1080p,\n * 4k) so fal picks its own, priced here at its dearest frame. At 480p that route\n * costs more than Replicate's 2.0 rate.\n */\nfunction seedance20CostUsd(resolution: string | undefined, seconds: number, mayRouteToFal: boolean): number {\n const viaReplicate = seedanceUsdPerSecond(resolution) * seconds;\n if (!mayRouteToFal) return viaReplicate;\n const falTier = resolution === \"480p\" || resolution === \"720p\" ? resolution : undefined;\n return Math.max(viaReplicate, falSeedanceUsdPerSecond(falTier) * seconds);\n}\n\n/**\n * USD estimate for one clip.\n *\n * Veo and Kling bill by provider usage reported in the response, so their true\n * cost is not knowable up front. Rather than quote a flat number that is wrong\n * in the DANGEROUS direction (too low), they are estimated at the most expensive\n * known per-second rate — an over-estimate scales with the clip, and a user who\n * is quoted more than they are charged is never the one who gets hurt.\n */\nexport function estimateVideoCostUsd(input: VideoCostInput): number {\n const { model, duration, resolution, generateAudio, buildsOnSourceClip } = input;\n const seconds = billedSeconds(model, duration ?? DEFAULT_VIDEO_DURATION_S);\n const sourceClipPremium = buildsOnSourceClip ? VIDEO_SOURCE_COST_MULTIPLIER : 1;\n // Omni defaults to its 10s CEILING when the prompt names no length, not to the\n // 5s this module assumes elsewhere — so it resolves its own default before the\n // shared one, or every un-timed clip is quoted at half what it bills. That is\n // the only special case left: 1.1 bills the seconds it renders and the tier it\n // renders them at, both linear and both measured (see the constants above).\n if (isGeminiOmniModel(model)) return geminiOmniCostUsd(model, duration, resolution);\n // 2.5 before the family test, or it would be priced off the Replicate table for\n // 2.0 — which quotes 720p at $0.50/s against its real $0.2326, more than double.\n if (isSeedance25Model(model)) {\n const viaOpenRouter = seedance25UsdPerSecond(resolution) * seconds * sourceClipPremium;\n if (!input.mayRouteToFal) return viaOpenRouter;\n // Either route can serve this clip and only the face filter decides which, so\n // the quote is the DEARER of the two rather than simply the fal one: fal\n // DISCOUNTS a source-clip render while the measured OpenRouter premium raises\n // it, so on that one combination the fal number is the lower of the two and\n // quoting it would land the estimate under the charge.\n return Math.max(viaOpenRouter, falSeedanceUsdPerSecond(resolution, buildsOnSourceClip) * seconds);\n }\n if (isSeedanceModel(model)) return seedance20CostUsd(resolution, seconds, input.mayRouteToFal === true);\n\n // Published per-second SKUs live on each model's registry entry (`billing`).\n const published = perSecondBilling(model)?.usd;\n if (published) {\n const rate = (resolution ? published.byResolution?.[resolution] : undefined) ?? published;\n return (generateAudio ? rate.audio : rate.silent) * seconds;\n }\n\n // Unknown model: quote the dearest rate we know. Over-quoting is the safe\n // direction — a user charged less than they were told is never the one hurt.\n const dearestKnownRate = Math.max(...Object.values(SEEDANCE_USD_PER_SECOND));\n return dearestKnownRate * seconds;\n}\n\n/** The same estimate in credits, through the one rounding rule (`usdToCredits`). */\nexport function estimateVideoCredits(input: VideoCostInput): number {\n return usdToCredits(estimateVideoCostUsd(input));\n}\n","/**\n * What a canvas node costs, in ONE place: the engine's pre-run quote (the CLI\n * node `cost` functions), the backend's per-request cost cap, and the backend's\n * charge all read these rates.\n *\n * Two kinds of number live here and they must not be confused:\n *\n * - RATES and the per-unit charge helpers (`ttsCharactersUsd`, `transcriptionUsd`,\n * …). The backend charges with these on the units the provider actually\n * measured (ElevenLabs' `x-character-count`, Groq's transcribed seconds), so a\n * charge is always rate x real units.\n * - ESTIMATES (`estimateNodeCostUsd`), which fill in the units nobody knows yet\n * with a stated default (30s of music, one minute of lipsync). Those defaults\n * are the same ones the backend falls back to when the provider reports\n * nothing, so a quote and a charge describe the same assumption.\n *\n * Usage-billed nodes (every OpenRouter text/vision/image call) have no rate: the\n * provider reports what the call cost afterwards. `estimateNodeCostUsd` returns\n * null for them and callers keep their own coarse quote.\n */\n\nimport { CREDITS_PER_USD, usdToCredits } from \"./credits\";\nimport { estimateVideoCostUsd, isSeedanceModel } from \"./videoCost\";\n\nexport { CREDITS_PER_USD, usdToCredits } from \"./credits\";\n\n// ---------------------------------------------------------------------------\n// Provider rates. Doc comments carry the pricing source; `credits/COSTS.md` in\n// the backend is the human-readable registry of the same numbers.\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs TTS & Dialogue, billed per character (`x-character-count`, or the\n * total `inputs[].text` length for dialogue, which returns no billing header).\n * Creator tier: $22/month for 100K credits = $0.00022 per character.\n * https://elevenlabs.io/pricing\n */\nexport const ELEVENLABS_USD_PER_CHARACTER = 0.00022;\n\n/**\n * ElevenLabs Music (`/v1/music/detailed`, `/v1/music/video-to-music`): ~100\n * credits per second of audio at $0.00022 = $0.022/s. UNVERIFIED for\n * `music_v2`; see the note on the backend constant's history in COSTS.md.\n */\nexport const ELEVENLABS_MUSIC_USD_PER_SECOND = 0.022;\n\n/** ElevenLabs Sound Effects (`/v1/sound-generation`), same credit ratio as music. */\nexport const ELEVENLABS_SOUND_EFFECT_USD_PER_SECOND = 0.022;\n\n/** ElevenLabs Voice Changer (speech-to-speech): ~1000 characters per minute of source audio. */\nexport const ELEVENLABS_STS_USD_PER_MINUTE = 1000 * ELEVENLABS_USD_PER_CHARACTER;\n\n/**\n * Lipsync, per minute of output. Named for the fal/VEED endpoint it was priced\n * on; the node now runs Sync Labs `lipsync-2` on Replicate and this rate has\n * not been re-verified against that model.\n */\nexport const FALAI_LIPSYNC_PER_MIN_USD = 0.4;\n\n/**\n * Video background removal, per 30 frames: $0.012 with edge refinement, $0.008\n * without (fal VEED fast pricing; the node now runs on Replicate).\n */\nexport const FALAI_VIDEO_BG_REMOVE_REFINED_USD_PER_30_FRAMES = 0.012;\nexport const FALAI_VIDEO_BG_REMOVE_FAST_USD_PER_30_FRAMES = 0.008;\n\n/** Image background removal: a flat per-call telemetry estimate. */\nexport const FALAI_IMAGE_BG_REMOVE_USD_PER_CALL = 0.001;\n\n/** Groq Whisper Large v3 Turbo: $0.04 per hour transcribed, 10s minimum per request. */\nexport const GROQ_WHISPER_COST_PER_HOUR = 0.04;\nexport const GROQ_WHISPER_MINIMUM_SECONDS = 10;\n\n/** Deepgram Nova-3 pre-recorded: $0.0043 per minute. https://deepgram.com/pricing */\nexport const DEEPGRAM_NOVA3_COST_PER_MINUTE = 0.0043;\n\n/** Firecrawl `/v2/scrape`: one credit ($0.001) per request, billed whether or not the page came back. */\nexport const FIRECRAWL_COST_PER_SCRAPE = 0.001;\n\n/**\n * Every rate above plus the credit conversion, as one object: what the\n * backend's `credits/constants.ts` reads, so the ledger and this module cannot\n * carry two copies of a price.\n */\nexport const canvasNodeRates = {\n CREDITS_PER_USD,\n DEEPGRAM_NOVA3_COST_PER_MINUTE,\n ELEVENLABS_MUSIC_USD_PER_SECOND,\n ELEVENLABS_SOUND_EFFECT_USD_PER_SECOND,\n ELEVENLABS_STS_USD_PER_MINUTE,\n ELEVENLABS_USD_PER_CHARACTER,\n FALAI_IMAGE_BG_REMOVE_USD_PER_CALL,\n FALAI_LIPSYNC_PER_MIN_USD,\n FALAI_VIDEO_BG_REMOVE_FAST_USD_PER_30_FRAMES,\n FALAI_VIDEO_BG_REMOVE_REFINED_USD_PER_30_FRAMES,\n FIRECRAWL_COST_PER_SCRAPE,\n GROQ_WHISPER_COST_PER_HOUR,\n GROQ_WHISPER_MINIMUM_SECONDS,\n} as const;\n\n// ---------------------------------------------------------------------------\n// Defaults for units nobody knows before the call. Shared with the backend's\n// own fallbacks so a quote and a charge assume the same thing.\n// ---------------------------------------------------------------------------\n\n/** Music length when neither `music_length_ms` nor the scored clip's length is known. */\nexport const DEFAULT_MUSIC_SECONDS = 30;\n/** Sound-effect length when `duration_seconds` is unset (the model picks its own). */\nexport const DEFAULT_SOUND_EFFECT_SECONDS = 5;\n/** Footage length assumed for background removal when the clip carries no duration. */\nexport const DEFAULT_BACKGROUND_REMOVE_SECONDS = 30;\n/** Audio length assumed for a quote when a transcription or voice conversion input carries no duration. */\nconst DEFAULT_MEDIA_SECONDS = 60;\n\n// ---------------------------------------------------------------------------\n// Per-unit charges on MEASURED units.\n// ---------------------------------------------------------------------------\n\nexport function ttsCharactersUsd(characters: number): number {\n return characters * ELEVENLABS_USD_PER_CHARACTER;\n}\n\nexport function musicSecondsUsd(seconds: number): number {\n return seconds * ELEVENLABS_MUSIC_USD_PER_SECOND;\n}\n\nexport function soundEffectSecondsUsd(seconds: number): number {\n return seconds * ELEVENLABS_SOUND_EFFECT_USD_PER_SECOND;\n}\n\n/** Billed minutes for a voice conversion: the source length, never under a tenth of a minute. */\nexport function voiceConvertMinutes(durationMs: number): number {\n return Math.max(durationMs / 60_000, 0.1);\n}\n\nexport function voiceConvertUsd(minutes: number): number {\n return minutes * ELEVENLABS_STS_USD_PER_MINUTE;\n}\n\n/**\n * Billed minutes for a lipsync: the provider returns no output duration, and the\n * output is bounded by the longer input, so that is what is billed. One minute\n * when neither input carries a duration.\n */\nexport function lipsyncMinutes(audioMs: number | undefined, videoMs: number | undefined): number {\n const durationMs = Math.max(audioMs ?? 0, videoMs ?? 0);\n return durationMs > 0 ? durationMs / 60_000 : 1;\n}\n\nexport function lipsyncUsd(minutes: number): number {\n return minutes * FALAI_LIPSYNC_PER_MIN_USD;\n}\n\n/** Frames billed for background removal: 30fps assumed, at least one 30-frame unit. */\nexport function backgroundRemoveFrames(durationSeconds: number | undefined): number {\n return Math.max(30, Math.ceil((durationSeconds ?? DEFAULT_BACKGROUND_REMOVE_SECONDS) * 30));\n}\n\nexport function videoBackgroundRemoveUsd(durationSeconds: number | undefined, edgeRefinement: boolean): number {\n const unit = edgeRefinement\n ? FALAI_VIDEO_BG_REMOVE_REFINED_USD_PER_30_FRAMES\n : FALAI_VIDEO_BG_REMOVE_FAST_USD_PER_30_FRAMES;\n return (backgroundRemoveFrames(durationSeconds) / 30) * unit;\n}\n\n/** Transcription cost by provider: Groq floors at 10s, Deepgram bills strictly per minute; nothing transcribed costs nothing. */\nexport function transcriptionUsd(transcriber: \"groq\" | \"deepgram\", seconds: number): number {\n if (seconds <= 0) return 0;\n if (transcriber === \"deepgram\") return (seconds / 60) * DEEPGRAM_NOVA3_COST_PER_MINUTE;\n return (Math.max(seconds, GROQ_WHISPER_MINIMUM_SECONDS) / 3600) * GROQ_WHISPER_COST_PER_HOUR;\n}\n\n// ---------------------------------------------------------------------------\n// Pre-dispatch estimates.\n// ---------------------------------------------------------------------------\n\nexport interface NodeCostInput {\n nodeType: string;\n /** Raw params as sent to the backend; read defensively (they arrive pre-validation). */\n params: unknown;\n /** Wired input refs, when known. Durations on them sharpen the estimate. */\n inputs?: unknown;\n /**\n * The caller already knows the cheap video route refuses these references and\n * sends the clip straight to fal. A routing decision, never a provider param.\n */\n preferFal?: boolean;\n}\n\ntype Bag = Record<string, unknown>;\n\nfunction asBag(value: unknown): Bag {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Bag) : {};\n}\n\nfunction num(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\n/** `duration_ms` off a wired ref, top level or mirrored in `metadata`. */\nfunction inputDurationMs(inputs: Bag, slot: string): number | undefined {\n const ref = asBag(inputs[slot]);\n return num(ref.duration_ms) ?? num(asBag(ref.metadata).duration_ms);\n}\n\nfunction slotCount(inputs: Bag, slot: string): number {\n const value = inputs[slot];\n if (Array.isArray(value)) return value.length;\n return value ? 1 : 0;\n}\n\nfunction textLength(value: unknown): number {\n return typeof value === \"string\" ? value.length : 0;\n}\n\nfunction estimateVideoGenerateUsd(params: Bag, inputs: Bag, preferFal: boolean): number | null {\n const model = params.model;\n if (typeof model !== \"string\") return null;\n // A reference-guided Seedance clip with no opening frame is exactly the request\n // the workflow recovers on fal when the likeness filter refuses it, so it is\n // quoted at whichever route is dearer (see `videoBlockRecovery` in the backend).\n const referenceOnly = slotCount(inputs, \"references\") > 0 && !inputs.first_frame;\n return estimateVideoCostUsd({\n model,\n duration: num(params.duration),\n resolution: typeof params.resolution === \"string\" ? params.resolution : undefined,\n generateAudio: typeof params.generate_audio === \"boolean\" ? params.generate_audio : undefined,\n buildsOnSourceClip: slotCount(inputs, \"video_references\") > 0,\n mayRouteToFal: isSeedanceModel(model) && (preferFal || referenceOnly),\n });\n}\n\nfunction estimateMusicUsd(params: Bag, inputs: Bag): number {\n const pinnedMs = num(params.music_length_ms);\n const scoredMs =\n params.model === \"elevenlabs/video-background-music-v1\" ? inputDurationMs(inputs, \"video\") : undefined;\n const seconds = (pinnedMs ?? scoredMs ?? DEFAULT_MUSIC_SECONDS * 1000) / 1000;\n return musicSecondsUsd(seconds);\n}\n\nfunction estimateDialogueUsd(params: Bag): number | null {\n if (!Array.isArray(params.inputs)) return null;\n const characters = params.inputs.reduce<number>((sum, line) => sum + textLength(asBag(line).text), 0);\n return ttsCharactersUsd(characters);\n}\n\n/**\n * Pre-dispatch provider-cost estimate for one node execution, in USD, or null\n * when only the provider's response can say (usage-billed nodes).\n */\nexport function estimateNodeCostUsd(input: NodeCostInput): number | null {\n const params = asBag(input.params);\n const inputs = asBag(input.inputs);\n switch (input.nodeType) {\n case \"video_generate\":\n return estimateVideoGenerateUsd(params, inputs, input.preferFal === true);\n case \"tts\":\n return typeof params.text === \"string\" ? ttsCharactersUsd(params.text.length) : null;\n case \"dialogue\":\n return estimateDialogueUsd(params);\n case \"music\":\n return estimateMusicUsd(params, inputs);\n case \"sound_effect\":\n return soundEffectSecondsUsd(num(params.duration_seconds) ?? DEFAULT_SOUND_EFFECT_SECONDS);\n case \"audio_voice_convert\": {\n const ms = inputDurationMs(inputs, \"audio\") ?? DEFAULT_MEDIA_SECONDS * 1000;\n return voiceConvertUsd(voiceConvertMinutes(ms));\n }\n case \"video_lipsync\":\n return lipsyncUsd(lipsyncMinutes(inputDurationMs(inputs, \"audio\"), inputDurationMs(inputs, \"video\")));\n case \"video_background_remove\": {\n const ms = inputDurationMs(inputs, \"video\");\n return videoBackgroundRemoveUsd(ms === undefined ? undefined : ms / 1000, params.edge_refinement !== false);\n }\n case \"image_background_remove\":\n return FALAI_IMAGE_BG_REMOVE_USD_PER_CALL;\n case \"video_transcribe\": {\n const ms = inputDurationMs(inputs, \"video\") ?? DEFAULT_MEDIA_SECONDS * 1000;\n return transcriptionUsd(params.transcriber === \"deepgram\" ? \"deepgram\" : \"groq\", ms / 1000);\n }\n case \"ingest\":\n return params.strategy === \"firecrawl\" ? FIRECRAWL_COST_PER_SCRAPE : 0;\n case \"voice_select\":\n case \"load_artifact\":\n case \"save_artifact\":\n case \"resize\":\n case \"crop\":\n return 0;\n default:\n return null;\n }\n}\n\n/** `estimateNodeCostUsd` in whole credits, or null when the node is usage-billed. */\nexport function estimateNodeCredits(input: NodeCostInput): number | null {\n const usd = estimateNodeCostUsd(input);\n return usd === null ? null : usdToCredits(usd);\n}\n","/**\n * How an invented person is photographed so they read as somebody real.\n *\n * One wording for every place a face is invented — an avatar's casting portrait in the\n * backend, a described person in a video ad in the CLI — because the face invented there\n * is the face every later frame copies. Judged blind side by side, a candid phone photo of\n * an ordinary person in their own world beat the old ID-photo framing (squared to camera,\n * flat light, grey seamless) 6 of 6, and once in an ad it beat it 6 of 6 again (AV1,\n * scripts/creative-evals/avatar-realism.mjs).\n */\n\n/**\n * The camera is a phone, because that is what real people's photos are taken on. Named,\n * because \"a real photo\" alone still comes back with a professional camera's creamy\n * background blur and compressed perspective — tells a viewer reads as \"produced\".\n */\nexport const PHONE_CAMERA =\n \"Shot on a phone, an iPhone or a Samsung Galaxy, with the main camera in the default photo mode (not portrait mode): slightly wide angle, held by a friend about a metre away or at arm's length, the background mostly in focus the way phone photos are, the phone's own processing — HDR evening out a bright window, a touch of over-sharpening, smoothed noise in the shadows, punchy everyday colour. Slightly imperfect framing. Not a professional camera: no bokeh or creamy background blur, no lens compression, no studio or flash lighting, no colour grade.\";\n\nexport const REAL_SKIN =\n \"Real skin and a real face: visible pores, fine lines, uneven tone, small marks, stray hairs, slight asymmetry. Clothes with real creases.\";\n\nconst NOT_A_MODEL =\n \"Not stock photography, not a model or an influencer: no beauty retouching, no glowing or plastic skin, no perfectly symmetrical features.\";\n\n/** A candid phone portrait of one invented ordinary person: the face a sheet is fused from. */\nexport function candidPersonPortraitPrompt(p: { subject: string; wardrobe?: string; setting?: string }): string {\n return [\n `An unretouched candid phone photo of one ordinary, real person: ${p.subject}`,\n p.wardrobe?.trim() ? `Wearing: ${p.wardrobe.trim()}` : null,\n `Where: ${p.setting?.trim() || \"an everyday place that fits who they are, in daylight\"}, available light only. They look toward the lens mid-conversation with a relaxed, unposed expression. Framed from the head to mid-torso, face clearly visible.`,\n PHONE_CAMERA,\n REAL_SKIN,\n NOT_A_MODEL,\n \"No text, logos or watermarks. One person only.\",\n ]\n .filter((line): line is string => line !== null)\n .join(\"\\n\");\n}\n","import { clipTooHeavyToIngest } from \"./clipWeight.ts\";\nimport { canonicalModelId, type InputKind, MODEL_REGISTRY, type NodeKind, type ParamSchema } from \"./registry\";\n\ntype ModelValidationError =\n | { code: \"unknown_model\"; message: string }\n | { code: \"missing_required_param\"; param: string; message: string }\n | { code: \"unknown_param\"; param: string; message: string }\n | { code: \"invalid_value\"; param: string; message: string }\n | { code: \"missing_input\"; input: InputKind; message: string }\n // Not about one param: every value can be legal and the FILE still be too big to take\n // in, which only shows up after the clip has rendered and billed.\n | { code: \"clip_too_heavy\"; message: string };\n\ntype ModelValidationResult = { ok: true } | { ok: false; errors: ModelValidationError[] };\n\n// Derived from `MODEL_REGISTRY` (the canonical source of truth) rather than a\n// hand-maintained list — adding a kind to the registry forces it here too, so\n// the guard can never silently go stale. `Object.keys` matches own keys only,\n// so inherited prototype names (\"toString\" etc.) correctly return false; it also\n// stays ES2021-clean (the backend typechecks this package under `lib: ES2021`,\n// which rules out `Object.hasOwn`).\nfunction isManagedNodeKind(nodeType: string): nodeType is NodeKind {\n return Object.keys(MODEL_REGISTRY).includes(nodeType);\n}\n\n/**\n * Validate canvas params against the per-model spec. Use for any node whose\n * `nodeType` is one of the managed kinds (image_generate / video_generate /\n * video_lipsync / tts). Nodes outside this set fall back to their generic Zod\n * schema only.\n *\n * `model` is the canonical `<provider>/<model>` id from `params.model`.\n *\n * The `model` field itself is always allowed and not validated as a param.\n */\nfunction checkRequiredParams(\n spec: { required: readonly string[] },\n params: Record<string, unknown>,\n model: string,\n): ModelValidationError[] {\n const errors: ModelValidationError[] = [];\n for (const req of spec.required) {\n if (params[req] === undefined || params[req] === null) {\n errors.push({\n code: \"missing_required_param\",\n param: req,\n message: `missing required param \"${req}\" for model \"${model}\"`,\n });\n }\n }\n return errors;\n}\n\nfunction checkParamValues(\n spec: { params: Record<string, ParamSchema> },\n params: Record<string, unknown>,\n model: string,\n): ModelValidationError[] {\n const errors: ModelValidationError[] = [];\n for (const [key, value] of Object.entries(params)) {\n if (key === \"model\") continue;\n if (value === undefined) continue;\n const schema = spec.params[key];\n if (!schema) {\n errors.push({\n code: \"unknown_param\",\n param: key,\n message: `param \"${key}\" is not supported by model \"${model}\"`,\n });\n continue;\n }\n const valueErr = validateValue(key, value, schema, model);\n if (valueErr) errors.push(valueErr);\n }\n return errors;\n}\n\nexport function validateModelParams(\n nodeType: NodeKind,\n model: string,\n params: Record<string, unknown>,\n): ModelValidationResult {\n // A legacy id validates as the model it stands for (see `MODEL_ALIASES`).\n const spec = MODEL_REGISTRY[nodeType][canonicalModelId(model)];\n if (!spec) {\n return {\n ok: false,\n errors: [{ code: \"unknown_model\", message: `unknown model \"${model}\" for node \"${nodeType}\"` }],\n };\n }\n\n const errors = [...checkRequiredParams(spec, params, model), ...checkParamValues(spec, params, model)];\n if (errors.length > 0) return { ok: false, errors };\n\n // Size, after the params are known to be legal. Every value here can be individually\n // valid and still produce a file too big to take in — the clip renders, bills, and then\n // runs the ingest action out of memory. This is the free gate before the paid one.\n if (nodeType === \"video_generate\") {\n const tooHeavy = clipTooHeavyToIngest(\n typeof params.duration === \"number\" ? params.duration : undefined,\n typeof params.resolution === \"string\" ? params.resolution : undefined,\n );\n if (tooHeavy) return { ok: false, errors: [{ code: \"clip_too_heavy\", message: tooHeavy }] };\n }\n return { ok: true };\n}\n\n/**\n * Validate that every REQUIRED input slot the model declares is wired up.\n * `providedKinds` is the set of input kinds the canvas supplies (e.g.\n * [\"video\", \"audio\"]). Optional input slots are not checked — they only\n * affect UI rendering, not run validity.\n */\nexport function validateModelInputs(\n nodeType: NodeKind,\n model: string,\n providedKinds: readonly InputKind[],\n): ModelValidationResult {\n const spec = MODEL_REGISTRY[nodeType][canonicalModelId(model)];\n if (!spec) {\n return {\n ok: false,\n errors: [{ code: \"unknown_model\", message: `unknown model \"${model}\" for node \"${nodeType}\"` }],\n };\n }\n const have = new Set(providedKinds);\n const errors: ModelValidationError[] = [];\n for (const inputSpec of spec.inputs) {\n if (!have.has(inputSpec.kind)) {\n errors.push({\n code: \"missing_input\",\n input: inputSpec.kind,\n message: `model \"${model}\" requires input of kind \"${inputSpec.kind}\"`,\n });\n }\n }\n if (errors.length > 0) return { ok: false, errors };\n return { ok: true };\n}\n\nfunction validateStringValue(\n key: string,\n value: unknown,\n schema: Extract<ParamSchema, { kind: \"string\" }>,\n model: string,\n): ModelValidationError | null {\n if (typeof value !== \"string\") {\n return { code: \"invalid_value\", param: key, message: `${key}: expected string, got ${typeof value}` };\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return {\n code: \"invalid_value\",\n param: key,\n message: `${key}: \"${value}\" not allowed by \"${model}\" (allowed: ${schema.enum.join(\", \")})`,\n };\n }\n if (schema.maxLength !== undefined && value.length > schema.maxLength) {\n return {\n code: \"invalid_value\",\n param: key,\n message: `${key}: ${value.length} characters exceeds max ${schema.maxLength} for \"${model}\"`,\n };\n }\n return null;\n}\n\nfunction validateNumberValue(\n key: string,\n value: unknown,\n schema: Extract<ParamSchema, { kind: \"number\" }>,\n model: string,\n): ModelValidationError | null {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n return { code: \"invalid_value\", param: key, message: `${key}: expected number, got ${typeof value}` };\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return {\n code: \"invalid_value\",\n param: key,\n message: `${key}: ${value} not allowed by \"${model}\" (allowed: ${schema.enum.join(\", \")})`,\n };\n }\n // A fractional seed is a schema rejection at Replicate and at Vertex, not a\n // rounded one — so it is worth its own check rather than hiding inside a range.\n if (schema.integer && !Number.isInteger(value)) {\n return { code: \"invalid_value\", param: key, message: `${key}: ${value} must be a whole number` };\n }\n if (schema.min !== undefined && value < schema.min) {\n return { code: \"invalid_value\", param: key, message: `${key}: ${value} below min ${schema.min}` };\n }\n if (schema.max !== undefined && value > schema.max) {\n return { code: \"invalid_value\", param: key, message: `${key}: ${value} above max ${schema.max}` };\n }\n return null;\n}\n\nfunction validateValue(key: string, value: unknown, schema: ParamSchema, model: string): ModelValidationError | null {\n switch (schema.kind) {\n case \"string\":\n return validateStringValue(key, value, schema, model);\n case \"number\":\n return validateNumberValue(key, value, schema, model);\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n return { code: \"invalid_value\", param: key, message: `${key}: expected boolean, got ${typeof value}` };\n }\n return null;\n case \"json\":\n return null;\n default: {\n const _exhaustive: never = schema satisfies never;\n return _exhaustive;\n }\n }\n}\n\nexport { isManagedNodeKind };\n","import { z } from \"zod\";\n\n/**\n * The video blueprint: the one description of an ad that every video path agrees on.\n *\n * Three places produce or read it. The backend's `video_deconstruct` node writes it from a\n * reference video, `scaffold-ad` compiles a written spec into it, and the CLI's video\n * scaffolder reads either one and builds the canvas. Kept here so all three name the same\n * fields: a field the scaffolder reads and neither producer writes is a feature nobody can\n * reach, and a field a producer writes under another name is one the scaffolder silently\n * ignores.\n *\n * TOLERANT on purpose. Every field is optional and every object keeps the keys it does not\n * name (`.loose()`): a deconstruct is a model's reading of a video, a degraded answer must\n * still scaffold, and blueprints written by older deconstructs sit in client workspaces as\n * `prompt.json` and must keep parsing. The producers hold their own stricter schemas for\n * what they write; this is the shape the scaffolder can rely on.\n */\n\nconst FrameAssetSchema = z.object({ url: z.string().optional() }).loose().optional();\n\nexport const BlueprintDialogueLineSchema = z\n .object({\n speaker: z.string().optional(),\n line: z.string().optional(),\n // Absolute seconds on the source timeline (the deconstruct emits both).\n start_s: z.number().optional(),\n end_s: z.number().optional(),\n delivery: z.string().optional(),\n voice_description: z.string().optional(),\n // DECON-supplied: is this speaker's FACE visibly speaking in THIS scene? Element\n // presence alone can't answer that — a founder pictured in a polaroid close-up is\n // \"present\" yet the line is voiceover, and treating it as on-camera produced a\n // native Seedance lip-sync clip of a still photograph. `false` pins the line to\n // the VO path; absent keeps the presence-based decision (old blueprints).\n on_camera: z.boolean().optional(),\n })\n .loose();\n\nconst SfxSchema = z\n .object({\n at_s: z.number().optional(),\n duration_s: z.number().optional(),\n sound_effect_prompt: z.string().optional(),\n description: z.string().optional(),\n })\n .loose();\n\n// One constituent stream of a composited scene (split panel / PIP inset / keyed\n// presenter). Each region becomes its own generated clip; the scaffold stacks or\n// overlays them into the scene's picture.\nexport const CompositionRegionSchema = z\n .object({\n // full | top | bottom | left | right | inset\n panel: z.string().optional(),\n // 9-grid anchor for an `inset` presenter box.\n position: z.string().optional(),\n is_presenter: z.boolean().optional(),\n // The cast id shown/speaking in this region (routes lip-sync + element refs).\n cast_ref: z.string().optional(),\n // What the region's content IS: camera | screen_capture | static_graphic |\n // generated. Authoritative for routing when present (regex-over-prose fallback\n // otherwise): screen_capture/static_graphic are rebuilt from REAL surfaces on the\n // overlay layer, never AI-generated.\n kind: z.string().optional(),\n // Opaque id naming the SPECIFIC on-screen document/note/app-state this\n // screen_capture region shows. Two scenes share it only when they show the SAME\n // recording continuing (scrolling/typing/waiting within it) — a genuinely\n // DIFFERENT document/note/recording (a source video splicing two screen captures)\n // gets a different id. Breaks a persistent-layout run into separate surface stubs\n // instead of asking the operator for one screenshot that can't cover both.\n surface_id: z.string().optional(),\n // Camera bubble(s)/inset(s) embedded INSIDE this region's surface (a Loom-style\n // presenter bubble inside a screen recording) — video-in-video the reproduction\n // must re-composite, not paint into the surface.\n nested: z.array(z.object({}).loose()).optional(),\n summary: z.string().optional(),\n frame_prompt: z.string().optional(),\n motion_prompt: z.string().optional(),\n })\n .loose();\n\nexport const SceneCompositionSchema = z\n .object({\n // full_frame (default) | split_screen | pip | keyed_overlay\n layout: z.string().optional(),\n // split_screen only: vertical (top/bottom) | horizontal (left/right).\n split_axis: z.string().optional(),\n regions: z.array(CompositionRegionSchema).optional(),\n })\n .loose();\n\nconst CameraMotionSchema = z.object({ movement: z.string().optional(), detail: z.string().optional() }).loose();\nconst TranscriptWordSchema = z.object({ text: z.string().optional() }).loose();\n\n/**\n * How a scene's picture should be produced, when the producer already knows.\n *\n * Set by a producer that authored the scene rather than read it off a video: `scaffold-ad`\n * marks its closing beat `brand_card`, because it wrote that beat to be a flat brand plate.\n * Without the field the scaffolder can only guess from the prose, which made the WORDING of\n * a prompt a routing contract — the plate's description had to contain one of the words the\n * guess looked for, and an edit that dropped them sent the closing card to a photograph.\n * A deconstructed scene never carries it, and the guess is still what routes those.\n */\nexport const SCENE_ROUTE_HINTS = [\"brand_card\"] as const;\nexport type SceneRouteHint = (typeof SCENE_ROUTE_HINTS)[number];\n\nexport const VideoSceneSchema = z\n .object({\n start_s: z.number().optional(),\n end_s: z.number().optional(),\n duration_s: z.number().optional(),\n summary: z.string().optional(),\n action_detail: z.string().optional(),\n // The scene's spatial layout. Absent/full_frame ⇒ one uncut shot (default path).\n // A layered layout (split_screen/pip/keyed_overlay) with regions ⇒ the scaffold\n // builds one clip per region and stacks/overlays them into the scene picture.\n composition: SceneCompositionSchema.optional(),\n // The capture \"look\" for this scene — selected from the ad-native shoot-mode\n // grammar (the CLI's scaffold/lib/shoot-modes.ts). When absent the scaffold\n // auto-derives a UGC/product mode; a human can override per scene by setting this.\n shoot_mode: z.string().optional(),\n // Diegetic ambient the clip's native audio should carry (no music). When\n // absent the scene falls back to its shoot mode's default ambience.\n ambient: z.string().optional(),\n camera_motion: CameraMotionSchema.optional(),\n start_frame_prompt: z.string().optional(),\n end_frame_prompt: z.string().optional(),\n motion_prompt: z.string().optional(),\n // The scene's role in the ad's persuasion arc (DECON-supplied); drives the\n // script re-craft checklist. Inferred from position when absent.\n narrative_role: z.string().optional(),\n // See `SceneRouteHint`.\n route_hint: z.enum(SCENE_ROUTE_HINTS).optional(),\n // DECON-supplied on the HOOK scene: the engineered physical/emotional state that\n // makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected\n // into the hook's start-frame description so the generator renders that state,\n // not a calm influencer (CCA-11).\n hook_mechanic: z\n .object({ mechanic: z.string().optional(), why_it_stops_scroll: z.string().optional() })\n .loose()\n .optional(),\n // DECON-supplied per-scene location (so a gym hook isn't flattened to \"home\").\n scene_setting: z.string().optional(),\n // How this scene cuts to the next (DECON-supplied). A recognized non-cut type\n // (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the\n // boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is\n // ignored (nothing follows it).\n transition_out: z.object({ type: z.string().optional(), description: z.string().optional() }).loose().optional(),\n dialogue: z.array(BlueprintDialogueLineSchema).optional(),\n sfx: z.array(SfxSchema).optional(),\n // Parsed element by element where they are read (`BlueprintOverlaySchema`,\n // `BlueprintFloatingElementSchema`), so one malformed graphic never rejects the scene.\n overlays: z.array(z.unknown()).optional(),\n floating_elements: z.array(z.unknown()).optional(),\n // DECON-supplied: how much the picture itself moves within the shot. Gates the\n // flash-hold optimization — a sub-2s b-roll flash with REAL subject motion\n // (pouring, spreading, hands working) must stay a real clip; freezing it turns\n // a montage into a slideshow. Absent (old blueprints) keeps the cheap still.\n motion_level: z.enum([\"static\", \"subtle\", \"dynamic\"]).optional(),\n transcript_slice: z.array(TranscriptWordSchema).optional(),\n start_frame_asset: FrameAssetSchema,\n end_frame_asset: FrameAssetSchema,\n // DECON-supplied: true when this scene is a length-split CONTINUATION of the\n // previous one (the SAME physical shot, broken up only because it exceeded the\n // clip ceiling). The scaffold then shares the splice keyframe — this scene's\n // start frame IS the previous scene's end frame — so the join is seamless.\n continues_previous: z.boolean().optional(),\n })\n .loose();\n\nexport const VideoBlueprintSchema = z\n .object({\n source: z.object({ aspect_ratio: z.string().optional(), duration_s: z.number().optional() }).loose().optional(),\n global: z\n .object({\n music: z\n .object({\n present: z.boolean().optional(),\n music_prompt: z.string().optional(),\n // Absolute second the music enters in the reference (the bed often\n // kicks in mid-ad, after the hook). We start the regenerated track here\n // instead of at 0 so the timing matches.\n starts_at_s: z.number().optional(),\n // Populated by the deconstruct when AudD (Shazam-style) recognizes the\n // reference track. We never reuse it — only style the regenerated bed.\n identified_track: z\n .object({ title: z.string().optional(), artist: z.string().optional() })\n .loose()\n .nullish(),\n })\n .loose()\n .optional(),\n cast: z\n .array(\n z\n .object({\n id: z.string().optional(),\n description: z.string().optional(),\n // The deconstruct's note on the target-market localization (e.g. \"native\n // French speaker\") — read to derive the spoken-track language code.\n market_localization_note: z.string().optional(),\n })\n .loose(),\n )\n .optional(),\n voiceover: z\n .object({\n // on_camera | mixed → mouths are on screen (lip-sync candidates);\n // voiceover | none → narration over the picture (no lip-sync).\n mode: z.string().optional(),\n // Which variety of the language the speech is in. Stated because the model\n // picks one otherwise: ads set in Spain came back in Latin American Spanish,\n // and the pinned peninsular voice could not undo it — the re-voicing is\n // speech-to-speech, so it carries the accent across and swaps only the timbre.\n accent: z.string().optional(),\n voice_description: z.string().optional(),\n persona: z.string().optional(),\n })\n .loose()\n .optional(),\n // Visual palette — read only to colour a clean brand-card/CTA plate (the\n // first hex is the dominant brand colour); never to drive frame generation.\n style: z\n .object({ palette: z.array(z.object({ hex: z.string().optional() }).loose()).optional() })\n .loose()\n .optional(),\n // Written by `scaffold-ad` (the deconstruct names its own fields, e.g. `branding.brand_name`):\n // the whole script as one string, where the ad is set, and the brand it closes on.\n transcript: z.string().optional(),\n market: z.string().optional(),\n branding: z.object({ name: z.string().optional(), logo_path: z.string().optional() }).loose().optional(),\n })\n .loose()\n .optional(),\n // Written by `scaffold-ad`: what the beats add up to, so a short ad is caught before it renders.\n estimated_duration_s: z.number().optional(),\n scenes: z.array(VideoSceneSchema).min(1),\n })\n .loose();\n\nconst OverlayStyleSchema = z\n .object({ color_hex: z.string().optional(), background: z.string().optional(), size: z.string().optional() })\n .loose();\n\n/** One on-screen text overlay of a scene (an entry of `scene.overlays`). */\nexport const BlueprintOverlaySchema = z\n .object({\n text: z.string().optional(),\n appears_at_s: z.number().optional(),\n duration_s: z.number().optional(),\n position: z.string().optional(),\n role: z.string().optional(),\n animation: z.string().optional(),\n animation_detail: z.string().optional(),\n style: OverlayStyleSchema.optional(),\n })\n .loose();\n\n/** One non-text graphic of a scene (an entry of `scene.floating_elements`). */\nexport const BlueprintFloatingElementSchema = z\n .object({\n kind: z.string().optional(),\n description: z.string().optional(),\n brand_name: z.string().nullish(),\n what_it_represents: z.string().optional(),\n appears_at_s: z.number().optional(),\n duration_s: z.number().optional(),\n position: z.string().optional(),\n })\n .loose();\n\nexport type VideoBlueprint = z.infer<typeof VideoBlueprintSchema>;\nexport type VideoScene = z.infer<typeof VideoSceneSchema>;\nexport type BlueprintDialogueLine = z.infer<typeof BlueprintDialogueLineSchema>;\nexport type CompositionRegion = z.infer<typeof CompositionRegionSchema>;\nexport type SceneComposition = z.infer<typeof SceneCompositionSchema>;\nexport type BlueprintOverlay = z.infer<typeof BlueprintOverlaySchema>;\nexport type BlueprintFloatingElement = z.infer<typeof BlueprintFloatingElementSchema>;\n","// Max nodes per layer executed at once. Video canvases fan out many\n// `video_generate` clips into one layer, and each worker blocks on a\n// multi-minute poll — so a low bound serializes clips into slow waves. 8 keeps\n// multi-clip renders moving while staying well under the backend job workpool\n// (maxParallelism 32) and typical provider rate limits. Raise per-run with\n// `--parallel`/`--concurrency` or `BAKER_CANVAS_CONCURRENCY`.\nexport const DEFAULT_CONCURRENCY = 8;\n\n/**\n * Pick the node-execution concurrency limit from ordered candidates (e.g.\n * CLI flag, then env var). A candidate counts only if it parses to a finite\n * number >= 1; otherwise the default applies.\n */\nexport function resolveConcurrency(...candidates: Array<string | number | undefined>): number {\n for (const candidate of candidates) {\n if (candidate === undefined || candidate === \"\") continue;\n const n = typeof candidate === \"number\" ? candidate : Number(candidate);\n if (Number.isFinite(n) && n >= 1) return Math.floor(n);\n }\n return DEFAULT_CONCURRENCY;\n}\n\n/**\n * Map `fn` over `items` with at most `limit` calls in flight, settling every\n * item — one rejection never abandons its siblings. Results preserve input\n * order (index i of the result array is item i's outcome).\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<PromiseSettledResult<R>[]> {\n const results: PromiseSettledResult<R>[] = new Array(items.length);\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next++;\n try {\n results[index] = { status: \"fulfilled\", value: await fn(items[index] as T, index) };\n } catch (reason) {\n results[index] = { status: \"rejected\", reason };\n }\n }\n };\n const workers = Array.from({ length: Math.min(limit, items.length) }, worker);\n await Promise.all(workers);\n return results;\n}\n","/**\n * The one recursive walk over a canvas's JSON values: node inputs, params and\n * outputs. Arrays and objects are containers; every other value is a leaf. Each\n * engine pass that used to hand-roll this recursion (ref resolution, slot\n * rendering, cache-key normalization, sha collection, ref discovery) is one of\n * the four shapes below.\n */\n\ntype Json = Record<string, unknown>;\n\nfunction isContainerObject(value: unknown): value is Json {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * Visit every string leaf with its path, in the `a.b[0]` form validation issues\n * report (`[0]` at the root, `k` for a top-level key).\n */\nexport function forEachString(value: unknown, visit: (s: string, path: string) => void, path = \"\"): void {\n if (typeof value === \"string\") {\n visit(value, path);\n return;\n }\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) forEachString(value[i], visit, path ? `${path}[${i}]` : `[${i}]`);\n return;\n }\n if (isContainerObject(value)) {\n for (const [k, v] of Object.entries(value)) forEachString(v, visit, path ? `${path}.${k}` : k);\n }\n}\n\n/**\n * Visit every object, outermost first. An object is never a leaf: the objects\n * nested inside a visited one are visited too (a ref nested in a ref's metadata).\n */\nexport function forEachObject(value: unknown, visit: (o: Json) => void): void {\n if (Array.isArray(value)) {\n for (const v of value) forEachObject(v, visit);\n return;\n }\n if (!isContainerObject(value)) return;\n visit(value);\n for (const v of Object.values(value)) forEachObject(v, visit);\n}\n\n/** A copy with every string leaf replaced by `fn`'s result. */\nexport function mapStrings(value: unknown, fn: (s: string) => unknown): unknown {\n if (typeof value === \"string\") return fn(value);\n if (Array.isArray(value)) return value.map((v) => mapStrings(v, fn));\n if (isContainerObject(value)) {\n const out: Json = {};\n for (const [k, v] of Object.entries(value)) out[k] = mapStrings(v, fn);\n return out;\n }\n return value;\n}\n\n/**\n * A copy in which `replace` may substitute any object whole. Returning undefined\n * keeps walking into that object instead; leaves are returned as they are.\n */\nexport function mapObjects(value: unknown, replace: (o: Json) => unknown): unknown {\n if (Array.isArray(value)) return value.map((v) => mapObjects(v, replace));\n if (!isContainerObject(value)) return value;\n const replaced = replace(value);\n if (replaced !== undefined) return replaced;\n const out: Json = {};\n for (const [k, v] of Object.entries(value)) out[k] = mapObjects(v, replace);\n return out;\n}\n\n/** `mapObjects` for an async replacement. Array elements run concurrently, object keys in order. */\nexport async function mapObjectsAsync(\n value: unknown,\n replace: (o: Json) => Promise<unknown> | undefined,\n): Promise<unknown> {\n if (Array.isArray(value)) return Promise.all(value.map((v) => mapObjectsAsync(v, replace)));\n if (!isContainerObject(value)) return value;\n const replaced = replace(value);\n if (replaced !== undefined) return replaced;\n const out: Json = {};\n for (const [k, v] of Object.entries(value)) out[k] = await mapObjectsAsync(v, replace);\n return out;\n}\n","import { randomBytes } from \"node:crypto\";\n\nconst ENCODING = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\n\n/** A minimal monotonic-ish ULID (Crockford base32). Sufficient for run ids. */\nexport function ulid(now: number = Date.now()): string {\n const time = encodeTime(now);\n const rand = encodeRandom();\n return time + rand;\n}\n\nfunction encodeTime(now: number): string {\n let n = now;\n let out = \"\";\n for (let i = 0; i < 10; i++) {\n const mod = n % 32;\n out = ENCODING[mod] + out;\n n = (n - mod) / 32;\n }\n return out;\n}\n\nfunction encodeRandom(): string {\n const bytes = randomBytes(10);\n let out = \"\";\n for (let i = 0; i < 16; i++) {\n // 16 chars * 5 bits = 80 bits = 10 bytes\n const bitIndex = i * 5;\n const byteIndex = Math.floor(bitIndex / 8);\n const bitOffset = bitIndex % 8;\n const high = bytes[byteIndex] ?? 0;\n const low = bytes[byteIndex + 1] ?? 0;\n const combined = ((high << 8) | low) & 0xffff;\n const shift = 11 - bitOffset;\n const value = (combined >> shift) & 0x1f;\n out += ENCODING[value];\n }\n return out;\n}\n","import { z } from \"zod\";\nimport { MimeType, Sha256Hex } from \"./primitives.ts\";\n\nconst BaseAssetRef = z.object({\n url: z.string().url().optional(),\n path: z.string().optional(),\n sha256: Sha256Hex,\n mime: MimeType,\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport const ImageRef = BaseAssetRef.extend({\n kind: z.literal(\"image\"),\n width: z.number().int().positive().optional(),\n height: z.number().int().positive().optional(),\n});\nexport type ImageRef = z.infer<typeof ImageRef>;\n\nexport const VideoRef = BaseAssetRef.extend({\n kind: z.literal(\"video\"),\n width: z.number().int().positive().optional(),\n height: z.number().int().positive().optional(),\n duration_ms: z.number().nonnegative().optional(),\n});\nexport type VideoRef = z.infer<typeof VideoRef>;\n\nexport const AudioRef = BaseAssetRef.extend({\n kind: z.literal(\"audio\"),\n duration_ms: z.number().nonnegative().optional(),\n});\nexport type AudioRef = z.infer<typeof AudioRef>;\n\nexport const JsonRef = BaseAssetRef.extend({\n kind: z.literal(\"json\"),\n});\nexport type JsonRef = z.infer<typeof JsonRef>;\n\nexport const TextRef = BaseAssetRef.extend({\n kind: z.literal(\"text\"),\n});\nexport type TextRef = z.infer<typeof TextRef>;\n\nexport const FontRef = BaseAssetRef.extend({\n kind: z.literal(\"font\"),\n});\nexport type FontRef = z.infer<typeof FontRef>;\n\nexport const AssetRef = z.discriminatedUnion(\"kind\", [ImageRef, VideoRef, AudioRef, JsonRef, TextRef, FontRef]);\nexport type AssetRef = z.infer<typeof AssetRef>;\n\nexport type AssetKind = AssetRef[\"kind\"];\n\n// ---------------------------------------------------------------------------\n// Ref guards. Each asks a different question on purpose, and all of them live\n// here so the difference is a name rather than a hand-rolled check per module.\n// ---------------------------------------------------------------------------\n\ntype Loose = Record<string, unknown>;\n\nfunction isObject(v: unknown): v is Loose {\n return Boolean(v) && typeof v === \"object\";\n}\n\nexport const ASSET_KINDS: ReadonlySet<string> = new Set([\"image\", \"video\", \"audio\", \"json\", \"text\", \"font\"]);\n\n/**\n * The content identity of a value that carries one: an object's string `sha256`.\n * Cache keys reduce such a value to this, so volatile fields (`url`, `path`,\n * `metadata.ingested_at`) never reach a key.\n */\nexport function contentShaOf(v: unknown): string | null {\n if (isObject(v) && \"sha256\" in v && typeof v.sha256 === \"string\") return v.sha256;\n return null;\n}\n\n/** An asset ref as nodes hand it to each other: a string `kind` and a `sha256`. */\nexport function isAssetRef(v: unknown): v is AssetRef {\n return isObject(v) && typeof v.kind === \"string\" && \"sha256\" in v;\n}\n\nexport function isAssetRefArray(v: unknown): v is AssetRef[] {\n return Array.isArray(v) && v.length > 0 && v.every(isAssetRef);\n}\n\n/** A ref whose bytes sit only at a URL: a local tool needs them downloaded first. */\nexport function isUrlOnlyRef(v: unknown): v is AssetRef & { url: string } {\n return (\n isObject(v) &&\n typeof v.kind === \"string\" &&\n typeof v.url === \"string\" &&\n typeof v.sha256 === \"string\" &&\n typeof v.mime === \"string\" &&\n typeof v.path !== \"string\"\n );\n}\n\n/** A ref a local tool can stage or hand to a browser: a known kind and a url or a local path. */\nexport function isStageableRef(v: unknown): v is AssetRef {\n return (\n isObject(v) &&\n typeof v.kind === \"string\" &&\n ASSET_KINDS.has(v.kind) &&\n typeof v.mime === \"string\" &&\n typeof v.sha256 === \"string\" &&\n (typeof v.url === \"string\" || typeof v.path === \"string\")\n );\n}\n\n/** A stored ref record (cache entries, run records): string `sha256` and `mime`, any other fields. */\nexport type AssetRefLike = Loose & { sha256: string; mime: string };\n\nexport function isAssetRefLike(v: unknown): v is AssetRefLike {\n return isObject(v) && !Array.isArray(v) && typeof v.sha256 === \"string\" && typeof v.mime === \"string\";\n}\n","import { z } from \"zod\";\n\nexport const Sha256Hex = z.string().regex(/^[0-9a-f]{64}$/, \"expected 64-char lower-case hex sha-256\");\nexport type Sha256Hex = z.infer<typeof Sha256Hex>;\n\nexport const MimeType = z.string().regex(/^[a-zA-Z0-9!#$&^_.+-]+\\/[a-zA-Z0-9!#$&^_.+-]+$/, \"expected RFC 6838 mime\");\nexport type MimeType = z.infer<typeof MimeType>;\n","import { canvasAssetOriginsSetting } from \"../../env.ts\";\nimport type { BackendClient } from \"../client/backend-client.ts\";\nimport { forEachObject } from \"../lib/json-walk.ts\";\nimport { type AssetKind, type AssetRefLike, isAssetRefLike } from \"../schema/refs.ts\";\nimport type { AssetStore } from \"./asset-store.ts\";\nimport type { CacheEntry, CacheStore } from \"./cache-store.ts\";\n\n/**\n * Remote canvas cache, backend-mediated. Entries are small JSON pointer\n * records; their AssetRefs must carry content-addressed `canvas-assets/` URLs\n * so any sandbox can rehydrate the bytes from R2 (sha-verified by\n * `ingestRemote`). The layered store makes a fresh sandbox re-run an\n * already-computed canvas at zero credits: local hit → use it; local miss →\n * remote fetch + rehydrate + write back locally.\n *\n * Cache traffic is strictly best-effort: a remote failure NEVER fails the run\n * (a paid render must not die on a cache write), it only costs a warning.\n */\n\nexport const CANVAS_ASSETS_URL_SEGMENT = \"/canvas-assets/\";\n\n/**\n * The origins our asset store serves `canvas-assets/` from: production and dev. Only these are\n * fetched directly by content address; a look-alike path on any other host is just a URL.\n * `BAKER_CANVAS_ASSET_ORIGINS` (comma-separated) overrides, as it does for `validate-creatives`.\n */\nexport function canvasAssetOrigins(setting: string | undefined = canvasAssetOriginsSetting()): string[] {\n return (setting ?? \"https://media.withbaker.com,https://media.dev-baker.com\")\n .split(\",\")\n .map((origin) => origin.trim().replace(/\\/+$/, \"\"))\n .filter(Boolean);\n}\n\n/** True when `url` is served from one of the store's own origins. */\nexport function isCanvasAssetOrigin(url: string): boolean {\n try {\n const { origin } = new URL(url);\n return canvasAssetOrigins().some((allowed) => {\n try {\n return new URL(allowed).origin === origin;\n } catch {\n return false;\n }\n });\n } catch {\n return false;\n }\n}\n\n/**\n * The content address a canvas-assets URL names: `…/canvas-assets/<sha0:2>/<sha>.<ext>`.\n * Null for anything else, including an unsharded or mis-sharded key — the sha has to sit\n * where our store writes it, or the URL is not ours to vouch for.\n */\nexport function parseCanvasAssetUrl(url: string): { sha256: string; ext: string } | null {\n let pathname: string;\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n pathname = parsed.pathname;\n } catch {\n return null;\n }\n const match = /\\/canvas-assets\\/([0-9a-f]{2})\\/([0-9a-f]{64})\\.([a-z0-9]+)$/.exec(pathname);\n if (!match) return null;\n const [, shard, sha256, ext] = match;\n if (shard === undefined || sha256 === undefined || ext === undefined || !sha256.startsWith(shard)) return null;\n return { sha256, ext };\n}\n\n/**\n * A ref counts as persisted only when its url points at OUR content-addressed\n * store entry for THESE bytes. R2 shards keys by the first sha byte, so the\n * canonical key is `canvas-assets/<sha0:2>/<sha>.<ext>` — the sha appears after\n * a 2-hex shard segment, NOT right after \"/canvas-assets/\". (An unsharded\n * layout is tolerated for legacy/seed pointers.) A foreign/ingested URL that\n * merely contains \"/canvas-assets/\" must NOT pass, or persistOutputAssetUrls\n * would skip uploading its bytes and the poisoned pointer would fail every\n * cache PUT and run record downstream.\n *\n * Historically this required the sha right after the segment, which matched no\n * real R2 url — so every render output and cache entry was silently dropped.\n */\nexport function isPersistedAssetRef(ref: AssetRefLike): ref is AssetRefLike & { url: string } {\n const { url, sha256 } = ref;\n if (typeof url !== \"string\" || typeof sha256 !== \"string\") return false;\n return (\n url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256.slice(0, 2)}/${sha256}`) ||\n url.includes(`${CANVAS_ASSETS_URL_SEGMENT}${sha256}`)\n );\n}\n\n/**\n * Every AssetRef-shaped object inside a value. A ref is not a leaf — refs nested\n * inside other refs are collected too, mirroring the backend's persisted-ref\n * validation walk so an entry we upload never carries a ref the server would\n * treat differently.\n */\nexport function collectAssetRefLikes(value: unknown): AssetRefLike[] {\n const out: AssetRefLike[] = [];\n forEachObject(value, (o) => {\n if (isAssetRefLike(o)) out.push(o);\n });\n return out;\n}\n\n/** True when every ref in the entry is rehydratable from the persisted store. */\nexport function entryFullyPersisted(entry: CacheEntry): boolean {\n return collectAssetRefLikes(entry.outputs).every((ref) => isPersistedAssetRef(ref));\n}\n\n/** Deep-clone the entry and drop sandbox-local fields from every ref. */\nexport function stripLocalFields(entry: CacheEntry): CacheEntry {\n const clone = JSON.parse(JSON.stringify(entry)) as CacheEntry;\n for (const ref of collectAssetRefLikes(clone.outputs)) {\n delete ref.path;\n delete ref.bytes;\n }\n return clone;\n}\n\nexport class RemoteCacheStore {\n private readonly client: BackendClient;\n private readonly log: (line: string) => void;\n\n constructor(client: BackendClient, log?: (line: string) => void) {\n this.client = client;\n this.log = log ?? (() => undefined);\n }\n\n async get(cacheKey: string): Promise<CacheEntry | null> {\n return await this.client.getCacheEntry<CacheEntry>(cacheKey);\n }\n\n async put(entry: CacheEntry): Promise<void> {\n // An entry with a local-only ref can't rehydrate elsewhere — keeping it\n // local-only is correct, not an error (the persist hook normally prevents\n // this; it can happen when an asset upload failed mid-run).\n if (!entryFullyPersisted(entry)) {\n this.log(`[cache ] ${entry.cacheKey.slice(0, 12)}… has local-only assets, kept local`);\n return;\n }\n const stripped = stripLocalFields(entry);\n // Mirror the backend intake cap. refs only feed a future GC sweep, so a\n // truncated list degrades GC accounting — never correctness — while an\n // over-cap array would 400 and keep the whole entry out of the shared cache.\n if (stripped.refs.length > MAX_REMOTE_REFS) {\n stripped.refs = stripped.refs.slice(0, MAX_REMOTE_REFS);\n }\n await this.client.putCacheEntry(stripped);\n }\n}\n\nconst MAX_REMOTE_REFS = 512;\n\nexport class LayeredCacheStore implements CacheStore {\n readonly rootDir: string;\n private readonly local: CacheStore;\n private readonly remote: RemoteCacheStore;\n private readonly assets: AssetStore;\n private readonly log: (line: string) => void;\n\n constructor(opts: { local: CacheStore; remote: RemoteCacheStore; assets: AssetStore; log?: (line: string) => void }) {\n this.local = opts.local;\n this.remote = opts.remote;\n this.assets = opts.assets;\n this.rootDir = opts.local.rootDir;\n this.log = opts.log ?? (() => undefined);\n }\n\n async get(cacheKey: string): Promise<CacheEntry | null> {\n const localHit = await this.local.get(cacheKey);\n if (localHit) return localHit;\n\n let remoteEntry: CacheEntry | null;\n try {\n remoteEntry = await this.remote.get(cacheKey);\n } catch (e) {\n this.log(`[cache ] remote lookup failed (${message(e)}) — treating as miss`);\n return null;\n }\n if (!remoteEntry) return null;\n\n let rehydrated: CacheEntry;\n try {\n rehydrated = await this.rehydrate(remoteEntry);\n } catch (e) {\n this.log(`[cache ] ${cacheKey.slice(0, 12)}… rehydration failed (${message(e)}) — treating as miss`);\n return null;\n }\n // Write back locally so subsequent hits in this sandbox skip the network.\n await this.local.put(rehydrated);\n return rehydrated;\n }\n\n async put(entry: CacheEntry): Promise<void> {\n await this.local.put(entry);\n try {\n await this.remote.put(entry);\n } catch (e) {\n this.log(`[cache ] remote write failed (${message(e)}) — entry kept local`);\n }\n }\n\n /**\n * Download every referenced asset into the local content-addressed store\n * (sha-verified) and stamp fresh local paths. Any ref that cannot be\n * rehydrated fails the WHOLE entry — a partially-hydrated cache hit would\n * crash materialization later with a far less actionable error.\n */\n private async rehydrate(entry: CacheEntry): Promise<CacheEntry> {\n const clone = JSON.parse(JSON.stringify(entry)) as CacheEntry;\n for (const ref of collectAssetRefLikes(clone.outputs)) {\n if (!isPersistedAssetRef(ref)) {\n throw new Error(`ref ${ref.sha256.slice(0, 12)}… has no persisted url`);\n }\n const ingested = await this.assets.ingestRemote({\n kind: (typeof ref.kind === \"string\" ? ref.kind : \"json\") as AssetKind,\n url: ref.url,\n sha256: ref.sha256,\n mime: ref.mime,\n metadata: (ref.metadata ?? undefined) as Record<string, unknown> | undefined,\n });\n ref.path = ingested.path;\n }\n return clone;\n }\n}\n\nfunction message(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","import type { ExecCtx } from \"../../engine/context.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { collectAssetRefLikes, isPersistedAssetRef } from \"../../storage/remote-cache-store.ts\";\n\nconst PUT_MAX_ATTEMPTS = 4;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Presign an R2 upload slot for `sha256`/`mime` and PUT the bytes. Returns the\n * public URL a remote node can fetch. Shared by the `ingest` node (which uploads\n * freshly-fetched bytes) and `ensureUploaded` (which uploads an existing\n * local-only asset before it is handed to a remote node).\n *\n * Retried with backoff: one TCP reset on a large body must not cost a paid\n * render its durable copy. Replays are safe — the key is content-addressed, so\n * a repeated PUT writes identical bytes. Each attempt presigns FRESH: a retry\n * after a long stall can straddle the 1-hour signature window, and\n * re-presigning the same sha is idempotent (same key).\n */\nexport async function presignAndPut(args: {\n bytes: Buffer;\n sha256: string;\n mime: string;\n ctx: ExecCtx;\n}): Promise<string> {\n let lastFailure: Error | null = null;\n for (let attempt = 0; attempt < PUT_MAX_ATTEMPTS; attempt++) {\n if (args.ctx.signal?.aborted) break;\n if (attempt > 0) await sleep(500 * 2 ** (attempt - 1) * (1 + Math.random() * 0.25));\n const result = await attemptPresignedPut(args);\n if (result.ok) return result.url;\n lastFailure = result.failure;\n if (!result.retryable) break;\n }\n throw lastFailure ?? new Error(\"upload: aborted before the PUT could start\");\n}\n\ntype PutAttempt = { ok: true; url: string } | { ok: false; failure: Error; retryable: boolean };\n\nasync function attemptPresignedPut(args: {\n bytes: Buffer;\n sha256: string;\n mime: string;\n ctx: ExecCtx;\n}): Promise<PutAttempt> {\n try {\n const { putUrl, publicUrl, conditional } = await args.ctx.client.presignAssetUpload(\n args.sha256,\n args.mime,\n args.ctx.signal,\n undefined,\n true,\n );\n const putRes = await fetch(putUrl, {\n method: \"PUT\",\n body: new Uint8Array(args.bytes),\n // A conditional URL was signed WITH this header, so it must be sent exactly.\n headers: conditional ? { \"Content-Type\": args.mime, \"If-None-Match\": \"*\" } : { \"Content-Type\": args.mime },\n signal: args.ctx.signal,\n });\n if (putRes.ok) return { ok: true, url: publicUrl };\n // 412 on a conditional write: an object already lives at this content\n // address. The key is the sha of these bytes, so the upload's job is done.\n if (conditional && putRes.status === 412) return { ok: true, url: publicUrl };\n return {\n ok: false,\n failure: new Error(`upload: presigned PUT failed ${putRes.status} ${putRes.statusText}`),\n // Only transient statuses warrant a replay — a 400/403 fails identically every attempt.\n retryable: putRes.status >= 500 || putRes.status === 429 || putRes.status === 408,\n };\n } catch (e) {\n // Network reset mid-body (or a presign that couldn't reach the backend) —\n // retryable unless the run itself was aborted.\n return {\n ok: false,\n failure: e instanceof Error ? e : new Error(String(e)),\n retryable: args.ctx.signal?.aborted !== true,\n };\n }\n}\n\n/**\n * Guarantee a remote-fetchable `url` for an asset before it crosses to a remote\n * node. Local-node outputs (ffmpeg/magick via `runCli`, any `runCli` consumer)\n * are ingested into the content-addressed store with a `path` but NO `url`\n * (`LocalAssetStore` is local-only). A remote node receiving such a ref would be\n * told `url: undefined`, which the backend rejects (e.g. `audio_voice_convert\n * requires inputs.audio`). This uploads the local bytes to R2 and stamps `url`.\n * Idempotent: refs that already carry a `url` (ingest/remote outputs) pass through.\n */\nexport async function ensureUploaded(ref: AssetRef, ctx: ExecCtx): Promise<AssetRef> {\n if (ref.url) return ref;\n const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);\n const url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });\n return { ...ref, url } as AssetRef;\n}\n\n/**\n * Stamp a durable `canvas-assets/` URL onto every AssetRef in a node's fresh\n * outputs, uploading local-only bytes (and re-uploading refs whose url points\n * at an external origin — an ingested source URL can die tomorrow; the cache\n * entry and run record that reference it must not). Mutates refs in place —\n * the outputs object was just produced by the node and is not shared yet.\n * URL-only stamping never changes cache keys: inputs hash by sha256 and\n * params are normalized to content identity (see normalizeParamsForCacheKey).\n */\nexport async function persistOutputAssetUrls(outputs: Record<string, unknown>, ctx: ExecCtx): Promise<void> {\n for (const ref of collectAssetRefLikes(outputs)) {\n if (isPersistedAssetRef(ref)) continue;\n const bytes = await ctx.assets.readBytes(ref.sha256, ref.mime);\n ref.url = await presignAndPut({ bytes, sha256: ref.sha256, mime: ref.mime, ctx });\n }\n}\n","const SLOT_RE = /\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g;\n\nexport function extractSlots(template: string): string[] {\n const out = new Set<string>();\n for (const match of template.matchAll(SLOT_RE)) {\n const name = match[1];\n if (name) out.add(name);\n }\n return [...out];\n}\n\nexport function renderTemplate(template: string, values: Record<string, string>): string {\n return template.replace(SLOT_RE, (_full, name: string) => {\n if (Object.hasOwn(values, name)) {\n return values[name] ?? \"\";\n }\n return \"\";\n });\n}\n\nexport function hasUnresolvedSlots(template: string, values: Record<string, string>): string[] {\n const slots = extractSlots(template);\n return slots.filter((s) => !Object.hasOwn(values, s));\n}\n","import { copyFile, link, mkdir, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AssetRef } from \"../schema/refs.ts\";\nimport { extForMime } from \"./asset-store.ts\";\n\ntype OutputWriterOpts = {\n outputsDir: string;\n runId: string;\n};\n\n/**\n * Writes per-run human-readable filenames pointing at content-addressed cache assets.\n * Tries hardlink, falls back to copy.\n */\nexport class OutputWriter {\n readonly runDir: string;\n\n constructor(opts: OutputWriterOpts) {\n this.runDir = path.join(opts.outputsDir, opts.runId);\n }\n\n async ensure(): Promise<void> {\n await mkdir(this.runDir, { recursive: true });\n }\n\n async writeAsset(name: string, ref: AssetRef): Promise<string> {\n await this.ensure();\n const ext = extForMime(ref.mime);\n const dest = path.join(this.runDir, `${name}.${ext}`);\n if (!ref.path) {\n // No on-disk bytes — write a JSON pointer.\n const pointer = path.join(this.runDir, `${name}.json`);\n await writeFile(pointer, JSON.stringify(ref, null, 2));\n return pointer;\n }\n await safeReplace(ref.path, dest);\n return dest;\n }\n\n async writeManifest(name: string, value: unknown): Promise<string> {\n await this.ensure();\n const dest = path.join(this.runDir, `${name}.json`);\n await writeFile(dest, JSON.stringify(value, null, 2));\n return dest;\n }\n}\n\nasync function safeReplace(src: string, dest: string): Promise<void> {\n try {\n await unlink(dest);\n } catch {\n // ignore — likely doesn't exist\n }\n try {\n await link(src, dest);\n return;\n } catch {\n // fall back to copy\n }\n await copyFile(src, dest);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { Readable, Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport type { AssetKind, AssetRef } from \"../schema/refs.ts\";\nimport { sha256Hex } from \"./sha256.ts\";\n\n/**\n * Hard ceiling on any single asset the engine downloads or reads from disk.\n * Generated ad creatives are tens of MB; 2 GiB leaves generous headroom while\n * keeping a hostile or mistaken URL from exhausting the disk.\n */\nexport const MAX_ASSET_BYTES = 2 * 1024 * 1024 * 1024;\n\nconst MIME_TO_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n \"image/avif\": \"avif\",\n \"video/mp4\": \"mp4\",\n \"video/webm\": \"webm\",\n \"video/quicktime\": \"mov\",\n \"audio/mpeg\": \"mp3\",\n \"audio/wav\": \"wav\",\n \"audio/x-wav\": \"wav\",\n \"audio/ogg\": \"ogg\",\n \"audio/mp4\": \"m4a\",\n \"application/json\": \"json\",\n \"text/plain\": \"txt\",\n \"text/markdown\": \"md\",\n \"text/html\": \"html\",\n \"font/ttf\": \"ttf\",\n \"font/otf\": \"otf\",\n \"font/woff\": \"woff\",\n \"font/woff2\": \"woff2\",\n};\n\nexport function extForMime(mime: string): string {\n return MIME_TO_EXT[mime] ?? \"bin\";\n}\n\nexport type IngestRemoteArgs = {\n kind: AssetKind;\n url: string;\n sha256: string;\n mime: string;\n metadata?: Record<string, unknown>;\n};\n\nexport interface AssetStore {\n rootDir: string;\n ingestBytes(args: {\n bytes: Buffer;\n kind: AssetKind;\n mime: string;\n metadata?: Record<string, unknown>;\n }): Promise<AssetRef>;\n ingestRemote(args: IngestRemoteArgs): Promise<AssetRef>;\n /** Local FS path for an asset by sha. Returns undefined if absent. */\n pathFor(sha: string, mime: string): string;\n exists(sha: string, mime: string): Promise<boolean>;\n readBytes(sha: string, mime: string): Promise<Buffer>;\n}\n\nexport class LocalAssetStore implements AssetStore {\n rootDir: string;\n private readonly fetchFn: typeof fetch;\n private readonly maxRemoteAssetBytes: number;\n constructor(rootDir: string, fetchFn: typeof fetch = fetch, opts?: { maxRemoteAssetBytes?: number }) {\n this.rootDir = rootDir;\n this.fetchFn = fetchFn;\n this.maxRemoteAssetBytes = opts?.maxRemoteAssetBytes ?? MAX_ASSET_BYTES;\n }\n\n pathFor(sha: string, mime: string): string {\n const ext = extForMime(mime);\n const shard = sha.slice(0, 2);\n return path.join(this.rootDir, shard, `${sha}.${ext}`);\n }\n\n async exists(sha: string, mime: string): Promise<boolean> {\n try {\n await stat(this.pathFor(sha, mime));\n return true;\n } catch {\n return false;\n }\n }\n\n readBytes(sha: string, mime: string): Promise<Buffer> {\n return readFile(this.pathFor(sha, mime));\n }\n\n async ingestBytes(args: {\n bytes: Buffer;\n kind: AssetKind;\n mime: string;\n metadata?: Record<string, unknown>;\n }): Promise<AssetRef> {\n const sha = sha256Hex(args.bytes);\n const dest = this.pathFor(sha, args.mime);\n await mkdir(path.dirname(dest), { recursive: true });\n if (!(await this.exists(sha, args.mime))) {\n const tmp = `${dest}.tmp-${process.pid}-${randomUUID()}`;\n await writeFile(tmp, args.bytes);\n await rename(tmp, dest);\n }\n return buildRef({ kind: args.kind, sha, mime: args.mime, path: dest, metadata: args.metadata });\n }\n\n async ingestRemote(args: IngestRemoteArgs): Promise<AssetRef> {\n const dest = this.pathFor(args.sha256, args.mime);\n let bytes: Buffer;\n if (await this.exists(args.sha256, args.mime)) {\n bytes = await readFile(dest);\n } else {\n await mkdir(path.dirname(dest), { recursive: true });\n const res = await this.fetchFn(args.url);\n if (!res.ok || !res.body) {\n throw new Error(`asset-store: fetch ${args.url} → ${res.status}`);\n }\n const declared = Number(res.headers.get(\"content-length\"));\n if (Number.isFinite(declared) && declared > this.maxRemoteAssetBytes) {\n throw new Error(\n `asset-store: ${args.url} is ${declared} bytes, exceeds the ${this.maxRemoteAssetBytes} byte limit`,\n );\n }\n const tmp = `${dest}.tmp-${process.pid}-${randomUUID()}`;\n try {\n await pipeline(\n Readable.fromWeb(res.body as never),\n byteLimit(args.url, this.maxRemoteAssetBytes),\n createWriteStream(tmp),\n );\n const written = await readFile(tmp);\n const computed = sha256Hex(written);\n if (computed !== args.sha256 && !cdnReencodedImage(res, args.kind)) {\n throw new Error(`asset-store: sha mismatch for ${args.url} (got ${computed}, expected ${args.sha256})`);\n }\n await rename(tmp, dest);\n bytes = written;\n } catch (e) {\n await unlink(tmp).catch(() => undefined);\n throw e;\n }\n }\n return buildRef({\n kind: args.kind,\n sha: args.sha256,\n mime: args.mime,\n url: args.url,\n path: dest,\n metadata: args.metadata,\n bytes,\n });\n }\n}\n\n/**\n * Whether the CDN in front of our asset bucket re-encoded this image on the way out.\n *\n * `media.withbaker.com` runs Cloudflare Polish, which recompresses images at the edge and\n * marks the response `cf-polished`. The bytes are the same picture in a different file, so\n * a content-addressed download of a render could never match its own sha: 52 static runs\n * failed their resize step on exactly that, after every billed node had finished. Such a\n * response is accepted as the asset it was asked for; any other mismatch still fails,\n * because it is a truncated or wrong file.\n */\nfunction cdnReencodedImage(res: Response, kind: AssetKind): boolean {\n return kind === \"image\" && res.headers.get(\"cf-polished\") !== null;\n}\n\nfunction byteLimit(url: string, maxBytes: number): Transform {\n let total = 0;\n return new Transform({\n transform(chunk: Buffer, _enc, cb) {\n total += chunk.length;\n if (total > maxBytes) {\n cb(new Error(`asset-store: ${url} exceeds the ${maxBytes} byte limit (aborted at ${total} bytes)`));\n return;\n }\n cb(null, chunk);\n },\n });\n}\n\nexport function buildRef(args: {\n kind: AssetKind;\n sha: string;\n mime: string;\n url?: string;\n path?: string;\n metadata?: Record<string, unknown>;\n bytes?: Buffer;\n}): AssetRef {\n const base = {\n sha256: args.sha,\n mime: args.mime,\n url: args.url,\n path: args.path,\n metadata: args.metadata,\n };\n switch (args.kind) {\n case \"image\": {\n const { width, height } = (args.metadata ?? {}) as { width?: number; height?: number };\n return { kind: \"image\", ...base, width, height };\n }\n case \"video\": {\n const { width, height, duration_ms } = (args.metadata ?? {}) as {\n width?: number;\n height?: number;\n duration_ms?: number;\n };\n return { kind: \"video\", ...base, width, height, duration_ms };\n }\n case \"audio\": {\n const { duration_ms } = (args.metadata ?? {}) as { duration_ms?: number };\n return { kind: \"audio\", ...base, duration_ms };\n }\n case \"json\":\n return { kind: \"json\", ...base };\n case \"text\":\n return { kind: \"text\", ...base };\n case \"font\":\n return { kind: \"font\", ...base };\n default: {\n const _exhaustive: never = args.kind;\n throw new Error(`asset-store: unknown kind ${String(_exhaustive)}`);\n }\n }\n}\n","import { createHash } from \"node:crypto\";\n\nexport function sha256Hex(input: Buffer | string): string {\n const h = createHash(\"sha256\");\n h.update(input);\n return h.digest(\"hex\");\n}\n","import { canonicalize } from \"@baker/canvas-contract\";\nimport { sha256Hex } from \"../storage/sha256.ts\";\n\ntype CacheKeyInputs = {\n node_id: string;\n node_version: string;\n params: unknown;\n input_hashes: Record<string, string | string[]>;\n cache_salt?: string;\n /**\n * Extra string fields folded into the cache key alongside `params`. Used\n * by nodes whose output depends on external content not captured by params\n * — e.g. `hyperframe_render` adds `composition_content_sha` so editing a\n * composition's HTML or CSS invalidates the cache automatically.\n *\n * Keys are sorted before hashing to keep the key stable.\n */\n extras?: Record<string, string>;\n};\n\nexport function computeCacheKey(inputs: CacheKeyInputs): string {\n // Sort the input_hashes keys explicitly — canonicalize sorts, but we also\n // freeze the input shape so callers can pass any record without surprises.\n const sortedInputs: Record<string, string | string[]> = {};\n for (const key of Object.keys(inputs.input_hashes).sort()) {\n const v = inputs.input_hashes[key];\n if (v === undefined) continue;\n sortedInputs[key] = Array.isArray(v) ? [...v] : v;\n }\n let sortedExtras: Record<string, string> | undefined;\n if (inputs.extras && Object.keys(inputs.extras).length > 0) {\n sortedExtras = {};\n for (const key of Object.keys(inputs.extras).sort()) {\n const v = inputs.extras[key];\n if (v === undefined) continue;\n sortedExtras[key] = v;\n }\n }\n const payload = {\n node_id: inputs.node_id,\n node_version: inputs.node_version,\n params: inputs.params,\n input_hashes: sortedInputs,\n cache_salt: inputs.cache_salt,\n extras: sortedExtras,\n };\n return sha256Hex(canonicalize(payload));\n}\n","import type { AnyNodeDefinition } from \"./define.ts\";\n\ntype Priceable = Pick<AnyNodeDefinition, \"cost\" | \"params\">;\n\nexport type NodeCredits = { node_id: string; node_type: string; credits: number };\n\ntype CostEstimate = ReturnType<NonNullable<AnyNodeDefinition[\"cost\"]>>;\n\n/**\n * The one place a node's `cost` is called, with params its schema already parsed.\n * Null when the node declares no cost, or its cost throws or is not a number.\n */\nexport function priceNode(def: Priceable, params: unknown): CostEstimate | null {\n if (!def.cost) return null;\n try {\n const estimate = def.cost({ params });\n return Number.isFinite(estimate.credits) ? estimate : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The credit ESTIMATE for a node declaration: its raw params parsed through the\n * node's own schema (defaults applied), then priced. 0 when it declares no cost or\n * its params do not parse (validation reports those). What a run actually spent is\n * what the backend charged; this is only the number known before the call.\n */\nexport function estimateNodeCredits(def: Priceable | undefined, params: unknown): number {\n if (!def?.cost) return 0;\n const parsed = def.params.safeParse(params ?? {});\n if (!parsed.success) return 0;\n return priceNode(def, parsed.data)?.credits ?? 0;\n}\n\n/** Each node's estimate, in canvas order. */\nexport function estimateCanvasCredits(\n canvas: { nodes: readonly { id: string; type: string; params?: unknown }[] },\n registry: { get: (type: string) => Priceable | undefined },\n): NodeCredits[] {\n return canvas.nodes.map((n) => ({\n node_id: n.id,\n node_type: n.type,\n credits: estimateNodeCredits(registry.get(n.type), n.params),\n }));\n}\n\nexport function sumCredits(perNode: readonly NodeCredits[]): number {\n return perNode.reduce((total, n) => total + n.credits, 0);\n}\n","import { z } from \"zod\";\n\n// $ref:<node_id>.<output> — pass-through the full output (often a list)\n// $ref:<node_id>.<output>#<index> — pick a static index\nexport const REF_PREFIX = \"$ref:\";\n\nexport type RefExpr = { kind: \"ref\"; nodeId: string; output: string; index?: number };\n\nexport function parseRefExpr(input: string): RefExpr | null {\n if (!input.startsWith(REF_PREFIX)) return null;\n const rest = input.slice(REF_PREFIX.length);\n const hashIdx = rest.indexOf(\"#\");\n const head = hashIdx === -1 ? rest : rest.slice(0, hashIdx);\n const tail = hashIdx === -1 ? null : rest.slice(hashIdx + 1);\n const dot = head.indexOf(\".\");\n if (dot <= 0) return null;\n const nodeId = head.slice(0, dot);\n const output = head.slice(dot + 1);\n if (!nodeId || !output) return null;\n let index: number | undefined;\n if (tail !== null) {\n // Reject an empty index (`...#`): Number(\"\") is 0 and would silently\n // resolve to element 0 instead of failing on the malformed ref.\n const n = Number(tail);\n if (tail === \"\" || !Number.isInteger(n) || n < 0) return null;\n index = n;\n }\n return { kind: \"ref\", nodeId, output, index };\n}\n\nconst NodeIdRe = /^[a-z][a-z0-9_]*$/;\n\n/**\n * What a node IS in the ad, stated instead of encoded in its id. A scaffold stamps\n * these so `canvas validate` and the scaffold's own post-passes read \"scene 3, region\n * 1, the presenter's clip\" off the node rather than parsing `s3_r1_clip`. Descriptive\n * only: the engine never reads them and they never enter the cache key, so stamping\n * or editing tags never re-renders (or re-bills) anything. Canvases without tags keep\n * the id conventions they were written with.\n */\nexport const NodeTags = z\n .object({\n /** The scene (0-based, in blueprint order) this node renders or voices. */\n scene: z.number().int().min(0).optional(),\n /** The node's job in the assembly, e.g. `clip`, `frame`, `composite`, `spine`, `overlay`, `mux`. */\n role: z.string().min(1).optional(),\n /** The region index inside a composited (split / picture-in-picture) scene. */\n region: z.number().int().min(0).optional(),\n /** True for a speaker's own on-camera voice (lip-synced), false for a voice read over the picture. */\n on_camera: z.boolean().optional(),\n })\n .strict();\nexport type NodeTags = z.infer<typeof NodeTags>;\n\nexport const NodeDecl = z\n .object({\n id: z.string().regex(NodeIdRe, \"node id must be lower_snake_case\"),\n type: z.string().min(1),\n version: z.string().min(1).optional(),\n inputs: z.record(z.string(), z.unknown()).optional(),\n params: z.record(z.string(), z.unknown()).optional(),\n // Regenerate knob. The engine is content-addressed: identical params + inputs\n // return the cached render, so re-running an unchanged node NEVER re-bills or\n // produces a new result. Bump this token (any string/number — a `2`, a `\"v3\"`,\n // a note) and re-run to force THIS node to render fresh; because its new output\n // changes downstream input hashes, everything depending on it regenerates too.\n // This is the declarative \"change a value, re-run, get a new render\" affordance.\n regenerate: z.union([z.string(), z.number()]).optional(),\n tags: NodeTags.optional(),\n })\n .strict();\nexport type NodeDecl = z.infer<typeof NodeDecl>;\n\nconst OutputRef = z\n .object({\n node: z.string(),\n output: z.string(),\n })\n .strict();\n\n/**\n * The timing plan a video scaffold emits so `validate` can prove audio/lip-sync\n * invariants STATICALLY — before any billed render. The engine ignores it at run\n * time; only the validator reads it (see engine/lint/rules/video-timeline.ts). Hand-authored\n * video canvases can add this block to opt into the same checks.\n */\nconst VideoMeta = z\n .object({\n duration_s: z.number(),\n // The one-clock contract: the spine's summed clip lengths and every audio\n // timeline reachable from the final mux must equal `expected_total_s` — the\n // validator recomputes both from the LIVE graph (`-shortest` at the final mux\n // silently truncates the longer stream on any mismatch). Emitted by\n // scaffold-video; optional so hand-authored canvases still parse.\n timeline: z\n .object({\n spine_node: z.string(),\n expected_total_s: z.number(),\n audio_mix_node: z.string().optional(),\n })\n .strict()\n .optional(),\n // Each sequenced voiceover turn on the absolute timeline.\n vo_segments: z\n .array(\n z.object({\n slot: z.string(),\n start_s: z.number(),\n end_s: z.number(),\n scene: z.number().optional(),\n speaker: z.string().optional(),\n }),\n )\n .default([]),\n // Scenes with a single on-camera speaker, voiced NATIVELY by the video model\n // (Seedance generate_audio) then re-voiced to one brand voice. Each entry names\n // the `audio_voice_convert` node the validator confirms is present — the\n // native-audio replacement for the retired post-hoc lipsync check. A bare scene\n // index (legacy) or `{ scene, lipsync_node }` (legacy veed canvases) still parse.\n talking_scenes: z\n .array(\n z.union([\n z.number(),\n z.object({\n scene: z.number(),\n voice_convert_node: z.string(),\n // Advisory: the scene's visual length vs the estimated spoken length, so\n // a reviewer can see a native line that may run past its cut. Not gated.\n scene_s: z.number().optional(),\n est_speech_s: z.number().optional(),\n // Word count of the line est_speech_s was measured for. Together they carry\n // the speaker's OBSERVED pace (from the deconstruct's word timings), so the\n // overrun check budgets re-authored lines at the real rate, not a wps guess.\n speech_words: z.number().optional(),\n }),\n z.object({ scene: z.number(), lipsync_node: z.string() }),\n ]),\n )\n .default([]),\n // Advisory, NOT gated: scenes where a presenter's later same-shot turns play as\n // `tts` over their one native clip (a held shot can hold only ONE lip-synced clip,\n // and voiceover turns can't overlap), so the lips may not match those words. The\n // scaffold surfaces it so the agent can cut away or rely on the burned-in captions.\n lip_sync_caution: z\n .array(z.object({ scene: z.number(), speaker: z.string(), tts_over_native: z.array(z.string()) }))\n .optional(),\n // Advisory, NOT gated by the validator: the reviewable \"which graphic fires\n // on which spoken beat\" map emitted by scaffold-video (per-scene window,\n // spoken line, storyboard frames, scheduled graphics). Free-form rows so the\n // schema stays decoupled from the scaffold's exact shape.\n motion_board: z.array(z.unknown()).optional(),\n // The recurring identity elements the scaffold wired (one shared reference slot\n // per cast member / pet / product / logo). The reference-completeness check reads\n // this to warn when a frame's description mentions an element whose ref isn't\n // wired on that frame node (the wrong-identity-animal failure).\n elements: z\n .array(\n z.object({\n ref: z.string(),\n label: z.string(),\n type: z.string(),\n description: z.string().optional(),\n }),\n )\n .optional(),\n // Per video_generate node: the scene's natural visual span. The validator warns\n // when a clip's span exceeds the assigned model's max clip duration (e.g. a 9.2s\n // scene on Veo, which caps at 8s) — the clip would truncate.\n clip_spans: z.array(z.object({ node: z.string(), span_s: z.number() })).optional(),\n // WHY the graph has the nodes it has: one route per scene (clip / still_hold /\n // screen_still / graphic_plate / brand_card / phrase_slice / phrase_run /\n // composite) + a node-type histogram, stamped by scaffold-video. Read this first\n // when a canvas looks big.\n graph_stats: z\n .object({\n nodes_total: z.number(),\n nodes_billable: z.number(),\n node_types: z.record(z.string(), z.number()),\n scene_routes: z.array(z.object({ scene: z.number(), route: z.string(), window_s: z.number() })),\n })\n .strict()\n .optional(),\n })\n .strict()\n .optional();\n\nconst CanvasMetadata = z\n .object({\n name: z.string().optional(),\n description: z.string().optional(),\n // Free-form, human-facing next-steps guide embedded by scaffolds (e.g.\n // `scaffold-video`) so the editable checklist travels inside the canvas\n // instead of only printing to stdout. Ignored by the engine.\n todo: z.unknown().optional(),\n video: VideoMeta,\n })\n .strict()\n .optional();\n\nexport const CanvasSchema = z\n .object({\n schema: z.literal(\"baker-canvas/1\"),\n cache_salt: z.string().optional(),\n metadata: CanvasMetadata,\n nodes: z.array(NodeDecl).min(1),\n output: OutputRef.optional(),\n })\n .strict();\n\nexport type Canvas = z.infer<typeof CanvasSchema>;\n","import { forEachString } from \"../lib/json-walk.ts\";\nimport { parseRefExpr } from \"../schema/canvas.ts\";\n\ntype GraphNode = { id: string; inputs?: unknown; params?: unknown };\n\n/**\n * The ids a node depends on: every `$ref:` in its inputs AND params that names a\n * node in `known`, in the order they appear. A self-reference is kept, so a cycle\n * check can see it.\n */\nexport function nodeDependencies(node: GraphNode, known: ReadonlySet<string>): Set<string> {\n const deps = new Set<string>();\n const collect = (s: string) => {\n const expr = parseRefExpr(s);\n if (expr && known.has(expr.nodeId)) deps.add(expr.nodeId);\n };\n forEachString(node.inputs, collect);\n forEachString(node.params, collect);\n return deps;\n}\n\n/**\n * Node id → the ids it depends on. The one graph the validator checks for cycles\n * and orphans, the executor schedules, and the dashboard draws before a run.\n */\nexport function buildDependencyGraph(canvas: { nodes: readonly GraphNode[] }): Map<string, Set<string>> {\n const known = new Set(canvas.nodes.map((n) => n.id));\n const graph = new Map<string, Set<string>>();\n for (const n of canvas.nodes) graph.set(n.id, new Set());\n for (const n of canvas.nodes) {\n const deps = graph.get(n.id);\n if (deps) for (const dep of nodeDependencies(n, known)) deps.add(dep);\n }\n return graph;\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { isManagedNodeKind, MODEL_REGISTRY } from \"@baker/canvas-contract\";\nimport { type Canvas, type NodeDecl, parseRefExpr, REF_PREFIX } from \"../schema/canvas.ts\";\nimport { extractSlots, renderTemplate } from \"../schema/prompts.ts\";\nimport type { ValidationIssue } from \"./errors.ts\";\n\nconst PROMPT_OVER_MODEL_LIMIT = \"PROMPT_OVER_MODEL_LIMIT\";\n\ntype Nodes = ReadonlyMap<string, NodeDecl>;\n\n/**\n * The text a slot or param will carry at run time, when it is already on disk or in\n * the canvas: a literal, a `text` node's value, or a local text/json file an `ingest`\n * reads verbatim. Anything a node generates during the run is unknown (undefined).\n */\nasync function staticTextOf(value: unknown, nodes: Nodes): Promise<string | undefined> {\n if (typeof value !== \"string\") return undefined;\n if (!value.startsWith(REF_PREFIX)) return value;\n const expr = parseRefExpr(value);\n if (!expr || expr.kind !== \"ref\" || expr.index !== undefined) return undefined;\n const target = nodes.get(expr.nodeId);\n const params = (target?.params ?? {}) as Record<string, unknown>;\n if (target?.type === \"text\" && expr.output === \"text\" && typeof params.value === \"string\") return params.value;\n if (\n target?.type === \"ingest\" &&\n expr.output === \"asset\" &&\n params.source === \"path\" &&\n (params.expect === \"json\" || params.expect === \"text\") &&\n typeof params.path === \"string\" &&\n path.isAbsolute(params.path)\n ) {\n try {\n return await readFile(params.path, \"utf8\");\n } catch {\n return undefined; // the ingest's own execute reports a missing file\n }\n }\n return undefined;\n}\n\n/** The model's capped string params (`prompt`, `negative_prompt`, `text`) and their caps. */\nfunction modelStringCaps(nodeType: string, params: unknown): Array<{ key: string; max: number; model: string }> {\n if (!isManagedNodeKind(nodeType)) return [];\n const model = (params as Record<string, unknown> | undefined)?.model;\n if (typeof model !== \"string\") return [];\n const spec = MODEL_REGISTRY[nodeType][model];\n if (!spec) return [];\n return Object.entries(spec.params).flatMap(([key, schema]) =>\n schema.kind === \"string\" && schema.maxLength !== undefined ? [{ key, max: schema.maxLength, model }] : [],\n );\n}\n\n/**\n * The run-time half of the same check: the node's params as the executor is about to\n * send them, every `{{slot}}` already filled from what upstream nodes produced. Only\n * validate's figure is a floor (a slot a node generates mid-run is unknown to it);\n * this one is the prompt itself. One message per capped param over its limit.\n */\nexport function renderedPromptOverLimit(nodeId: string, nodeType: string, renderedParams: unknown): string[] {\n const params = (renderedParams ?? {}) as Record<string, unknown>;\n return modelStringCaps(nodeType, params).flatMap(({ key, max, model }) => {\n const value = params[key];\n if (typeof value !== \"string\" || value.length <= max) return [];\n return [\n `\"${nodeId}\" ${key} is ${value.length} characters once its slots are filled, ${value.length - max} over the ${max} that \"${model}\" accepts. ` +\n \"Shorten what feeds its {{slots}} (or the prompt around them) and run again; nothing was sent to the model.\",\n ];\n });\n}\n\n/**\n * One param's text as the model will receive it, with every slot known before the run\n * filled; undefined when it cannot be read or holds no slot (a plain literal is the\n * registry's own MODEL_INVALID_PARAMS check).\n */\nasync function filledParam(\n n: NodeDecl,\n raw: unknown,\n nodes: Nodes,\n): Promise<{ length: number; slots: Record<string, string> } | undefined> {\n const template = await staticTextOf(raw, nodes);\n if (template === undefined) return undefined;\n const names = extractSlots(template);\n if (names.length === 0 && template === raw) return undefined;\n const inputs = (n.inputs ?? {}) as Record<string, unknown>;\n const slots: Record<string, string> = {};\n for (const name of names) {\n const text = await staticTextOf(inputs[name], nodes);\n if (text !== undefined) slots[name] = text;\n }\n return { length: renderTemplate(template, slots).length, slots };\n}\n\nfunction largestSlotNote(slots: Record<string, string>): string {\n const largest = Object.entries(slots).sort((a, b) => b[1].length - a[1].length)[0];\n return largest ? `; {{${largest[0]}}} alone is ${largest[1].length}` : \"\";\n}\n\n/**\n * The length of each capped string param once its `{{slots}}` are filled, checked\n * against the model's own limit. The param alone is already checked by the model\n * registry; the filled-in prompt was not, and the provider only refused it after\n * every node upstream of the generator had run and been paid for. Unknown slots count\n * as empty, so the figure is a floor: a refusal here is certain, never a guess.\n */\nexport async function filledPromptLimitIssues(canvas: Canvas): Promise<ValidationIssue[]> {\n const nodes: Nodes = new Map(canvas.nodes.map((n) => [n.id, n]));\n const issues: ValidationIssue[] = [];\n for (const [i, n] of canvas.nodes.entries()) {\n for (const { key, max, model } of modelStringCaps(n.type, n.params)) {\n const filled = await filledParam(n, (n.params as Record<string, unknown> | undefined)?.[key], nodes);\n if (!filled || filled.length <= max) continue;\n issues.push({\n path: `nodes[${i}].params.${key}`,\n code: PROMPT_OVER_MODEL_LIMIT,\n message:\n `\"${n.id}\" ${key} is ${filled.length} characters once its slots are filled — ${filled.length - max} over the ${max} that \"${model}\" accepts` +\n largestSlotNote(filled.slots) +\n `. Shorten it before running: the model refuses it, after every node before \"${n.id}\" has already been paid for.`,\n received: filled.length,\n expected: `<= ${max} characters`,\n node_id: n.id,\n node_type: n.type,\n });\n }\n }\n return issues;\n}\n","import { mapStrings } from \"../lib/json-walk.ts\";\nimport { parseRefExpr, type RefExpr } from \"../schema/canvas.ts\";\n\nexport type NodeOutputs = Record<string, unknown>;\nexport type AllOutputs = Record<string, NodeOutputs>;\n\ntype ResolveDeps = {\n outputs: AllOutputs;\n};\n\n/**\n * Resolves every `$ref:` string in any JSON-like input against upstream node\n * outputs. Strings that are not refs are passed through unchanged.\n */\nexport function resolveRefs(value: unknown, deps: ResolveDeps): unknown {\n return mapStrings(value, (s) => {\n const expr = parseRefExpr(s);\n return expr ? resolveExpr(expr, deps) : s;\n });\n}\n\nfunction resolveExpr(expr: RefExpr, deps: ResolveDeps): unknown {\n return resolveNodeRef(expr.nodeId, expr.output, expr.index, deps);\n}\n\nfunction resolveNodeRef(nodeId: string, output: string, index: number | undefined, deps: ResolveDeps): unknown {\n const node = deps.outputs[nodeId];\n if (!node) throw new Error(`ref: unknown node \"${nodeId}\"`);\n if (!(output in node)) throw new Error(`ref: node \"${nodeId}\" has no output \"${output}\"`);\n const value = node[output];\n if (index === undefined) return value;\n if (!Array.isArray(value)) {\n throw new Error(`ref: ${nodeId}.${output} is not a list (cannot index #${index})`);\n }\n if (index >= value.length) {\n throw new Error(`ref: index #${index} out of bounds for ${nodeId}.${output} (length ${value.length})`);\n }\n return value[index];\n}\n","/**\n * Kahn's algorithm: produce execution layers from a dependency graph.\n * Each layer contains node ids whose dependencies are all in earlier layers.\n *\n * Throws if the graph has a cycle. The caller should run cycle detection in\n * the validator stage, but we double-check here as a safety net.\n */\nexport function topologicalLayers(graph: ReadonlyMap<string, ReadonlySet<string>>): string[][] {\n const remaining = cloneGraph(graph);\n const layers: string[][] = [];\n while (remaining.size > 0) {\n const ready = collectReady(remaining);\n if (ready.length === 0) {\n throw new Error(`scheduler: cycle detected among ${[...remaining.keys()].join(\", \")}`);\n }\n layers.push(ready);\n removeNodes(remaining, ready);\n }\n return layers;\n}\n\n/**\n * Node ids reachable from `roots` by following dependency edges (each node maps\n * to the set of nodes it depends on). Used for dead-node elimination: only the\n * transitive dependencies of the canvas output need to execute — orphaned nodes\n * left behind by an edit (or a `--out` rewire) would otherwise still run, bill,\n * and abort the whole run on their own failure.\n */\nexport function reachableNodes(graph: ReadonlyMap<string, ReadonlySet<string>>, roots: readonly string[]): Set<string> {\n const reachable = new Set<string>();\n const stack = [...roots];\n while (stack.length > 0) {\n const id = stack.pop();\n if (id === undefined || reachable.has(id) || !graph.has(id)) continue;\n reachable.add(id);\n for (const dep of graph.get(id) ?? []) stack.push(dep);\n }\n return reachable;\n}\n\n/**\n * Restrict a dependency graph to `keep`, dropping nodes outside the set and any\n * edges to dropped nodes. Returns a new graph; the input is untouched.\n */\nexport function subgraph(\n graph: ReadonlyMap<string, ReadonlySet<string>>,\n keep: ReadonlySet<string>,\n): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const [id, deps] of graph) {\n if (!keep.has(id)) continue;\n out.set(id, new Set([...deps].filter((d) => keep.has(d))));\n }\n return out;\n}\n\nfunction cloneGraph(graph: ReadonlyMap<string, ReadonlySet<string>>): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const [k, deps] of graph) out.set(k, new Set(deps));\n return out;\n}\n\nfunction collectReady(remaining: Map<string, Set<string>>): string[] {\n const ready: string[] = [];\n for (const [id, deps] of remaining) if (deps.size === 0) ready.push(id);\n ready.sort();\n return ready;\n}\n\nfunction removeNodes(remaining: Map<string, Set<string>>, ready: readonly string[]): void {\n for (const id of ready) remaining.delete(id);\n for (const deps of remaining.values()) {\n for (const id of ready) deps.delete(id);\n }\n}\n\nexport function findCycle(graph: ReadonlyMap<string, ReadonlySet<string>>): string[] | null {\n const reverseAdj = buildReverseAdj(graph);\n const color = new Map<string, number>();\n for (const k of graph.keys()) color.set(k, 0);\n const stack: string[] = [];\n for (const id of graph.keys()) {\n if ((color.get(id) ?? 0) !== 0) continue;\n const found = dfsCycle(id, color, stack, reverseAdj);\n if (found) return found;\n }\n return null;\n}\n\nfunction buildReverseAdj(graph: ReadonlyMap<string, ReadonlySet<string>>): Map<string, string[]> {\n const reverseAdj = new Map<string, string[]>();\n for (const [node, deps] of graph) {\n for (const dep of deps) {\n const arr = reverseAdj.get(dep) ?? [];\n arr.push(node);\n reverseAdj.set(dep, arr);\n }\n }\n return reverseAdj;\n}\n\nfunction dfsCycle(\n u: string,\n color: Map<string, number>,\n stack: string[],\n reverseAdj: Map<string, string[]>,\n): string[] | null {\n color.set(u, 1);\n stack.push(u);\n const children = reverseAdj.get(u) ?? [];\n for (const v of children) {\n const c = color.get(v) ?? 0;\n if (c === 1) {\n const idx = stack.indexOf(v);\n return [...stack.slice(idx), v];\n }\n if (c === 0) {\n const r = dfsCycle(v, color, stack, reverseAdj);\n if (r) return r;\n }\n }\n color.set(u, 2);\n stack.pop();\n return null;\n}\n","import path from \"node:path\";\nimport { type InputKind, isManagedNodeKind, validateModelInputs, validateModelParams } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { forEachString } from \"../lib/json-walk.ts\";\nimport { looksLikeHttpUrl } from \"../lib/url.ts\";\nimport { type Canvas, CanvasSchema, type NodeDecl, parseRefExpr, REF_PREFIX, type RefExpr } from \"../schema/canvas.ts\";\nimport { extractSlots } from \"../schema/prompts.ts\";\nimport { ASSET_KINDS } from \"../schema/refs.ts\";\nimport { estimateCanvasCredits, type NodeCredits, sumCredits } from \"./credits.ts\";\nimport { type AnyNodeDefinition, type InputSource, resolveOutputKinds, type StaticInputKind } from \"./define.ts\";\nimport { didYouMean } from \"./didyoumean.ts\";\nimport { isBlocking, type ValidationIssue } from \"./errors.ts\";\nimport { buildDependencyGraph } from \"./graph.ts\";\nimport type { LintContext as Ctx } from \"./lint/context.ts\";\nimport { runDeepLintRules, runLintRules } from \"./lint/registry.ts\";\nimport { elementMentionKeywords } from \"./lint/rules/video-prompt.ts\";\nimport { overlayTimingIssues } from \"./lint/rules/video-timeline.ts\";\nimport { filledPromptLimitIssues } from \"./prompt-limit.ts\";\nimport type { NodeRegistry } from \"./registry.ts\";\nimport { findCycle } from \"./scheduler.ts\";\n\n// Pure helpers of the domain rules that other modules import from the validator.\nexport { elementMentionKeywords, overlayTimingIssues };\n\nconst MODEL_INPUT_KINDS = new Set<InputKind>([\"image\", \"video\", \"audio\"]);\n\ntype ValidationResult =\n | { ok: true; canvas: Canvas; estimatedCredits: number; warnings?: ValidationIssue[] }\n | { ok: false; issues: ValidationIssue[] };\n\ntype ValidationDeep = ValidationResult & {\n /** Per-node credit estimate, in canvas order. Useful for `validate` cost previews. */\n perNodeCredits?: NodeCredits[];\n};\n\nconst STAGE_CODES = {\n PARSE: \"PARSE_ERROR\",\n SHAPE: \"SHAPE_ERROR\",\n UNKNOWN_NODE: \"UNKNOWN_NODE_TYPE\",\n PARAMS: \"INVALID_PARAMS\",\n PARAMS_EXTRA: \"INVALID_PARAMS_EXTRA\",\n MODEL_PARAMS: \"MODEL_INVALID_PARAMS\",\n MODEL_INPUTS: \"MODEL_INVALID_INPUTS\",\n REF_UNRESOLVED: \"UNRESOLVED_REF\",\n REF_OUT_OF_BOUNDS: \"REF_INDEX_OUT_OF_BOUNDS\",\n REF_TYPE: \"REF_TYPE_MISMATCH\",\n DUPLICATE_ID: \"DUPLICATE_NODE_ID\",\n CYCLE: \"GRAPH_CYCLE\",\n SLOT: \"PROMPT_SLOT_UNRESOLVED\",\n OUTPUT: \"OUTPUT_REF_INVALID\",\n ORPHAN_NODE: \"ORPHAN_NODE\",\n INGEST_PATH_URL_HEALED: \"INGEST_PATH_URL_HEALED\",\n} as const;\n\nexport function validateCanvas(input: unknown, registry: NodeRegistry): ValidationResult {\n const issues: ValidationIssue[] = [];\n const shape = CanvasSchema.safeParse(input);\n if (!shape.success) {\n pushZodIssues(issues, shape.error, \"\", STAGE_CODES.SHAPE);\n return { ok: false, issues };\n }\n const canvas = shape.data;\n const idToIndex = buildIdToIndex(canvas);\n const ctx: Ctx = { canvas, registry, issues, idToIndex };\n\n checkDuplicateIds(ctx);\n checkUnknownNodeTypes(ctx);\n if (issues.some((i) => i.code === STAGE_CODES.UNKNOWN_NODE)) return { ok: false, issues };\n\n checkAllParams(ctx);\n checkIngestPathUrlHeals(ctx);\n checkAllModelParams(ctx);\n checkAllRefs(ctx);\n checkAllModelInputs(ctx);\n checkCycle(ctx);\n checkAllSlots(ctx);\n const estimatedCredits = sumCredits(estimateCanvasCredits(canvas, registry));\n checkOutputRef(ctx);\n checkOrphanNodes(ctx);\n runLintRules(ctx);\n\n // Warnings are advisory: they never block. Only error-severity issues make the\n // canvas invalid; on failure we still return every issue (warnings included) so\n // nothing is dropped from the report.\n const hasBlocking = issues.some(isBlocking);\n if (hasBlocking) return { ok: false, issues };\n const warnings = issues.filter((i) => !isBlocking(i));\n return { ok: true, canvas, estimatedCredits, warnings: warnings.length > 0 ? warnings : undefined };\n}\n\n/**\n * Async second pass that lets each node run its own `validateExtra` hook.\n * Use this from `canvas validate` so composition-meta checks (and any\n * future async validators) surface as standard validation issues.\n *\n * Returns the same result the sync pass would, with `perNodeCredits` added\n * when the canvas is structurally OK.\n */\nexport async function validateCanvasDeep(input: unknown, registry: NodeRegistry): Promise<ValidationDeep> {\n const shallow = validateCanvas(input, registry);\n if (!shallow.ok) return shallow;\n const canvas = shallow.canvas;\n const issues: ValidationIssue[] = [];\n const ctx: Ctx = { canvas, registry, issues, idToIndex: buildIdToIndex(canvas) };\n const inputKindOf = (value: unknown) => staticInputKind(ctx, value);\n const inputSourceOf = (value: unknown) => staticInputSource(ctx, value);\n\n for (let i = 0; i < canvas.nodes.length; i++) {\n const n = canvas.nodes[i];\n if (!n) continue;\n const def = registry.get(n.type);\n if (!def?.validateExtra) continue;\n let extra: Awaited<ReturnType<typeof def.validateExtra>>;\n try {\n extra = await def.validateExtra({\n rawParams: n.params,\n rawInputs: n.inputs,\n nodeId: n.id,\n inputKindOf,\n inputSourceOf,\n });\n } catch (e) {\n issues.push({\n path: `nodes[${i}].params`,\n code: STAGE_CODES.PARAMS_EXTRA,\n message: (e as Error).message,\n node_id: n.id,\n node_type: n.type,\n });\n continue;\n }\n for (const item of extra) {\n issues.push({\n path: `nodes[${i}].${item.path}`,\n code: STAGE_CODES.PARAMS_EXTRA,\n message: item.message,\n node_id: n.id,\n node_type: n.type,\n });\n }\n }\n\n await runDeepLintRules(canvas, issues);\n issues.push(...(await filledPromptLimitIssues(canvas)));\n\n const hasBlocking = issues.some(isBlocking);\n if (hasBlocking) return { ok: false, issues };\n // Carry the shallow pass's advisory warnings through, plus any warning-severity\n // deep-validator issues, onto the ok result.\n const warnings = [...(shallow.warnings ?? []), ...issues.filter((i) => !isBlocking(i))];\n\n const perNodeCredits = estimateCanvasCredits(canvas, registry);\n\n return {\n ok: true,\n canvas,\n estimatedCredits: shallow.estimatedCredits,\n perNodeCredits,\n warnings: warnings.length > 0 ? warnings : undefined,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Stage helpers\n// ---------------------------------------------------------------------------\n\nfunction buildIdToIndex(canvas: Canvas): Map<string, number> {\n const m = new Map<string, number>();\n for (let i = 0; i < canvas.nodes.length; i++) {\n const n = canvas.nodes[i];\n if (n) m.set(n.id, i);\n }\n return m;\n}\n\nfunction checkDuplicateIds(ctx: Ctx): void {\n const seen = new Set<string>();\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n) continue;\n if (seen.has(n.id)) {\n ctx.issues.push({\n path: `nodes[${i}].id`,\n code: STAGE_CODES.DUPLICATE_ID,\n message: `duplicate node id \"${n.id}\"`,\n node_id: n.id,\n });\n }\n seen.add(n.id);\n }\n}\n\nfunction checkUnknownNodeTypes(ctx: Ctx): void {\n const known = ctx.registry.ids();\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n || ctx.registry.has(n.type)) continue;\n ctx.issues.push({\n path: `nodes[${i}].type`,\n code: STAGE_CODES.UNKNOWN_NODE,\n message: `unknown node type \"${n.type}\"`,\n received: n.type,\n did_you_mean: didYouMean(n.type, known),\n node_id: n.id,\n node_type: n.type,\n });\n }\n}\n\nfunction checkAllParams(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n) continue;\n const def = ctx.registry.get(n.type);\n if (!def) continue;\n const parsed = def.params.safeParse(n.params ?? {});\n if (parsed.success) continue;\n pushZodIssues(ctx.issues, parsed.error, `nodes[${i}].params`, STAGE_CODES.PARAMS, n.id, n.type);\n }\n}\n\n/**\n * The ingest schema silently heals a `source:\"path\"` node whose value is an http(s) URL into a\n * `source:\"url\"` ingest (so the run works instead of dying at execute with file_not_found). Surface\n * that heal as an advisory: the run behaves correctly, but the on-disk canvas JSON still literally\n * says `source:\"path\"`, so the operator should correct it. Non-blocking — the canvas stays runnable.\n */\nfunction checkIngestPathUrlHeals(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n || n.type !== \"ingest\") continue;\n const params = (n.params ?? {}) as Record<string, unknown>;\n if (params.source !== \"path\" || !looksLikeHttpUrl(params.path)) continue;\n ctx.issues.push({\n path: `nodes[${i}].params.source`,\n code: STAGE_CODES.INGEST_PATH_URL_HEALED,\n message: `ingest \"${n.id}\" declares source:\"path\" but its value is a URL — running it as source:\"url\". Update the canvas JSON to source:\"url\" so it matches what baker runs.`,\n severity: \"warning\",\n node_id: n.id,\n node_type: n.type,\n });\n }\n}\n\nfunction checkAllModelParams(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n || !isManagedNodeKind(n.type)) continue;\n const params = (n.params ?? {}) as Record<string, unknown>;\n const model = params.model;\n if (typeof model !== \"string\" || model.length === 0) continue; // shape validator will have flagged this\n const r = validateModelParams(n.type, model, params);\n if (r.ok) continue;\n for (const err of r.errors) {\n ctx.issues.push({\n path: `nodes[${i}].params${\"param\" in err ? `.${err.param}` : \"\"}`,\n code: STAGE_CODES.MODEL_PARAMS,\n message: err.message,\n node_id: n.id,\n node_type: n.type,\n });\n }\n }\n}\n\nfunction checkAllRefs(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n) continue;\n walkRefs(n.inputs, (refStr, jsonPath) => {\n checkOneRef(ctx, n, i, refStr, jsonPath, \"inputs\");\n });\n walkRefs(n.params, (refStr, jsonPath) => {\n checkOneRef(ctx, n, i, refStr, jsonPath, \"params\");\n });\n }\n}\n\n/**\n * For every managed-kind node (image_generate / video_generate / video_lipsync\n * / tts / music / dialogue / sound_effect), confirm the model's REQUIRED input\n * kinds are wired. The kind of each wired ref comes from the target node's\n * declared `outputKinds[slot]`. Refs that can't be resolved (missing target,\n * bad output, etc.) are skipped — `checkAllRefs` already surfaced those.\n */\nfunction checkAllModelInputs(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n || !isManagedNodeKind(n.type)) continue;\n const params = (n.params ?? {}) as Record<string, unknown>;\n const model = params.model;\n if (typeof model !== \"string\" || model.length === 0) continue;\n const providedKinds = collectProvidedKinds(ctx, n);\n const r = validateModelInputs(n.type, model, providedKinds);\n if (r.ok) continue;\n for (const err of r.errors) {\n ctx.issues.push({\n path: `nodes[${i}].inputs`,\n code: STAGE_CODES.MODEL_INPUTS,\n message: err.message,\n node_id: n.id,\n node_type: n.type,\n });\n }\n }\n}\n\nfunction collectProvidedKinds(ctx: Ctx, n: NodeDecl): InputKind[] {\n const kinds = new Set<InputKind>();\n walkRefs(n.inputs, (refStr) => {\n const kind = resolveRefKind(ctx, refStr);\n if (kind) kinds.add(kind);\n });\n return [...kinds];\n}\n\nfunction resolveRefKind(ctx: Ctx, refStr: string): InputKind | null {\n const target = refTarget(ctx, refStr);\n const kind = target ? refOutputKind(target) : undefined;\n return kind && MODEL_INPUT_KINDS.has(kind as InputKind) ? (kind as InputKind) : null;\n}\n\ntype RefTarget = { expr: RefExpr; node: NodeDecl; def: AnyNodeDefinition };\n\n/** The node and definition a `$ref:` string points at, when both exist. */\nfunction refTarget(ctx: Ctx, refStr: string): RefTarget | null {\n const expr = parseRefExpr(refStr);\n if (!expr) return null;\n const targetIdx = ctx.idToIndex.get(expr.nodeId);\n const node = targetIdx === undefined ? undefined : ctx.canvas.nodes[targetIdx];\n const def = node ? ctx.registry.get(node.type) : undefined;\n return node && def ? { expr, node, def } : null;\n}\n\n/** The asset kind a ref's output carries, as its node declares it. */\nfunction refOutputKind({ expr, node, def }: RefTarget): string | undefined {\n const targetParams = def.params.safeParse(node.params ?? {});\n const resolvedKinds = resolveOutputKinds(def.outputKinds, targetParams.success ? targetParams.data : {});\n // Nodes with dynamic outputs (ffmpeg) declare each output's kind on the instance\n // (`params.outputs.<name>.kind`); honor that so an ffmpeg-produced audio/video can\n // satisfy a typed model input (e.g. audio_voice_convert reading extracted audio).\n const declaredKind = (node.params as { outputs?: Record<string, { kind?: string }> } | undefined)?.outputs?.[\n expr.output\n ]?.kind;\n return resolvedKinds[expr.output] ?? declaredKind;\n}\n\n/**\n * What a raw input value will hold at run time, read off the node it references:\n * a whole list output wired without `#<index>` arrives as a list, not one asset,\n * and a plain-value output (a `text` node's string) is no asset at all.\n */\nfunction staticInputKind(ctx: Ctx, value: unknown): StaticInputKind {\n if (Array.isArray(value)) return \"list\";\n if (typeof value !== \"string\") return value && typeof value === \"object\" ? \"unknown\" : \"value\";\n if (!value.startsWith(REF_PREFIX)) return \"value\";\n const target = refTarget(ctx, value);\n if (!target) return \"unknown\";\n const outputSchema = unwrap(shapeOf(target.def.outputs)?.[target.expr.output] ?? z.unknown());\n if (outputSchema instanceof z.ZodArray && target.expr.index === undefined) return \"list\";\n const kind = refOutputKind(target);\n if (kind && ASSET_KINDS.has(kind)) return kind as StaticInputKind;\n const plain = outputSchema instanceof z.ZodString || outputSchema instanceof z.ZodNumber;\n return plain || outputSchema instanceof z.ZodBoolean ? \"value\" : \"unknown\";\n}\n\n/**\n * Where a raw input's file comes from before the run: the workspace file or URL an\n * `ingest` reads (a relative path resolves against the working directory, as the ingest\n * does). Undefined for anything a node produces while the run is under way.\n */\nfunction staticInputSource(ctx: Ctx, value: unknown): InputSource | undefined {\n if (typeof value !== \"string\" || !value.startsWith(REF_PREFIX)) return undefined;\n const target = refTarget(ctx, value);\n if (!target || target.node.type !== \"ingest\" || target.expr.output !== \"asset\" || target.expr.index !== undefined)\n return undefined;\n const parsed = target.def.params.safeParse(target.node.params ?? {});\n if (!parsed.success) return undefined;\n const params = parsed.data as { source?: unknown; path?: unknown; url?: unknown };\n if (params.source === \"path\" && typeof params.path === \"string\") return { path: path.resolve(params.path) };\n if (params.source === \"url\" && typeof params.url === \"string\") return { url: params.url };\n return undefined;\n}\n\nfunction checkOneRef(\n ctx: Ctx,\n n: NodeDecl,\n i: number,\n refStr: string,\n jsonPath: string,\n field: \"inputs\" | \"params\",\n): void {\n if (!refStr.startsWith(REF_PREFIX)) return;\n const expr = parseRefExpr(refStr);\n if (!expr || expr.kind !== \"ref\") {\n ctx.issues.push({\n path: `nodes[${i}].${field}.${jsonPath}`,\n code: STAGE_CODES.REF_UNRESOLVED,\n message: `malformed ref \"${refStr}\"`,\n node_id: n.id,\n node_type: n.type,\n });\n return;\n }\n const targetIdx = ctx.idToIndex.get(expr.nodeId);\n if (targetIdx === undefined) {\n ctx.issues.push({\n path: `nodes[${i}].${field}.${jsonPath}`,\n code: STAGE_CODES.REF_UNRESOLVED,\n message: `ref \"${refStr}\" — no node with id \"${expr.nodeId}\"`,\n did_you_mean: didYouMean(expr.nodeId, [...ctx.idToIndex.keys()]),\n node_id: n.id,\n node_type: n.type,\n });\n return;\n }\n checkRefOutput(ctx, n, i, jsonPath, field, refStr, expr.nodeId, expr.output, targetIdx);\n}\n\nfunction checkRefOutput(\n ctx: Ctx,\n n: NodeDecl,\n i: number,\n jsonPath: string,\n field: \"inputs\" | \"params\",\n refStr: string,\n nodeId: string,\n output: string,\n targetIdx: number,\n): void {\n const target = ctx.canvas.nodes[targetIdx];\n if (!target) return;\n const targetDef = ctx.registry.get(target.type);\n if (!targetDef) return;\n const outputNames = listShapeKeys(targetDef.outputs);\n if (!outputNames || outputNames.includes(output)) return;\n ctx.issues.push({\n path: `nodes[${i}].${field}.${jsonPath}`,\n code: STAGE_CODES.REF_UNRESOLVED,\n message: `ref \"${refStr}\" — node \"${nodeId}\" (${target.type}) has no output \"${output}\"`,\n did_you_mean: didYouMean(output, outputNames),\n node_id: n.id,\n node_type: n.type,\n });\n}\n\nfunction checkCycle(ctx: Ctx): void {\n const graph = buildDependencyGraph(ctx.canvas);\n const cycle = findCycle(graph);\n if (cycle) {\n ctx.issues.push({ path: \"nodes\", code: STAGE_CODES.CYCLE, message: `cycle detected: ${cycle.join(\" → \")}` });\n }\n}\n\nfunction checkAllSlots(ctx: Ctx): void {\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (!n) continue;\n if (!ctx.registry.get(n.type)) continue;\n checkSlotsForNode(ctx, n, i);\n }\n}\n\nfunction checkSlotsForNode(ctx: Ctx, n: NodeDecl, _i: number): void {\n const slots = new Set<string>();\n forEachString(n.params, (text) => {\n for (const s of extractSlots(text)) slots.add(s);\n });\n if (slots.size === 0) return;\n const inputKeys = Object.keys(n.inputs ?? {});\n for (const slot of slots) {\n if (inputKeys.includes(slot)) continue;\n ctx.issues.push({\n path: `nodes[${_i}].params`,\n code: STAGE_CODES.SLOT,\n message: `prompt slot \"{{${slot}}}\" has no matching input on node \"${n.id}\"`,\n did_you_mean: didYouMean(slot, inputKeys),\n node_id: n.id,\n node_type: n.type,\n });\n }\n}\n\n/**\n * Warn on nodes unreachable from `output` — every ref edge walked backward from the\n * output node. The engine still executes orphans (Kahn layers cover the whole graph),\n * so an orphaned billable node silently burns credits for pixels nothing consumes —\n * almost always a wiring bug (a consumer was skipped but its producer still emitted).\n * `ingest` nodes are exempt: parking a free ingest (the scaffold's editable `prompt`\n * blueprint, an operator's spare asset) is a legitimate pattern.\n */\nfunction checkOrphanNodes(ctx: Ctx): void {\n const out = ctx.canvas.output;\n if (!out || !ctx.idToIndex.has(out.node)) return;\n const graph = buildDependencyGraph(ctx.canvas);\n const reachable = new Set<string>();\n const stack = [out.node];\n while (stack.length > 0) {\n const id = stack.pop();\n if (id === undefined || reachable.has(id)) continue;\n reachable.add(id);\n for (const dep of graph.get(id) ?? []) stack.push(dep);\n }\n for (const n of ctx.canvas.nodes) {\n if (reachable.has(n.id) || n.type === \"ingest\") continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id)}]`,\n code: STAGE_CODES.ORPHAN_NODE,\n severity: \"warning\",\n message: `node \"${n.id}\" (${n.type}) is not reachable from output \"${out.node}\" — it will still execute (and bill, if billable) but nothing consumes its result; wire it in or remove it`,\n });\n }\n}\n\nfunction checkOutputRef(ctx: Ctx): void {\n const out = ctx.canvas.output;\n if (!out) return;\n const targetIdx = ctx.idToIndex.get(out.node);\n if (targetIdx === undefined) {\n ctx.issues.push({\n path: \"output.node\",\n code: STAGE_CODES.OUTPUT,\n message: `output references unknown node \"${out.node}\"`,\n did_you_mean: didYouMean(out.node, [...ctx.idToIndex.keys()]),\n });\n return;\n }\n const target = ctx.canvas.nodes[targetIdx];\n if (!target) return;\n const targetDef = ctx.registry.get(target.type);\n if (!targetDef) return;\n const outs = listShapeKeys(targetDef.outputs);\n if (!outs || outs.includes(out.output)) return;\n ctx.issues.push({\n path: \"output.output\",\n code: STAGE_CODES.OUTPUT,\n message: `node \"${out.node}\" (${target.type}) has no output \"${out.output}\"`,\n did_you_mean: didYouMean(out.output, outs),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction pushZodIssues(\n issues: ValidationIssue[],\n err: z.ZodError,\n pathPrefix: string,\n code: string,\n nodeId?: string,\n nodeType?: string,\n): void {\n for (const issue of err.issues) {\n const tail = pathToString(issue.path);\n const path = pathPrefix ? (tail ? `${pathPrefix}.${tail}` : pathPrefix) : tail;\n issues.push({\n path,\n code,\n message: issue.message,\n received: issue.code === \"invalid_type\" ? (issue as { received?: unknown }).received : undefined,\n node_id: nodeId,\n node_type: nodeType,\n });\n }\n}\n\nfunction pathToString(path: ReadonlyArray<PropertyKey>): string {\n return path\n .map((p) => (typeof p === \"number\" ? `[${p}]` : `.${String(p)}`))\n .join(\"\")\n .replace(/^\\./, \"\");\n}\n\n/** Every `$ref:` string in a value, with its dotted path. */\nfunction walkRefs(value: unknown, cb: (refStr: string, path: string) => void): void {\n forEachString(value, (s, path) => {\n if (s.startsWith(REF_PREFIX)) cb(s, path);\n });\n}\n\nfunction listShapeKeys(schema: z.ZodType): string[] | undefined {\n const shape = shapeOf(schema);\n return shape ? Object.keys(shape) : undefined;\n}\n\nfunction shapeOf(schema: z.ZodType): Record<string, z.ZodType> | undefined {\n const obj = unwrap(schema);\n return obj instanceof z.ZodObject ? (obj.shape as Record<string, z.ZodType>) : undefined;\n}\n\nfunction unwrap(schema: z.ZodType): z.ZodType {\n let cur: z.ZodType = schema;\n for (let i = 0; i < 8; i++) {\n const inner = (cur as unknown as { _def?: { innerType?: z.ZodType } })._def?.innerType;\n if (!inner) return cur;\n cur = inner;\n }\n return cur;\n}\n","/**\n * True when `value` is an http(s) URL — the machine-decidable signal that separates a `source:\"url\"`\n * ingest from a `source:\"path\"` one. Shared so the ingest schema's auto-heal, the load-time path\n * resolver, and the validator's advisory all agree on exactly what counts as a URL.\n */\nexport function looksLikeHttpUrl(value: unknown): value is string {\n return typeof value === \"string\" && /^https?:\\/\\//i.test(value.trim());\n}\n","import type { z } from \"zod\";\nimport type { AssetRef } from \"../schema/refs.ts\";\nimport type { ExecCtx } from \"./context.ts\";\n\ntype CostEstimate = { credits: number; seconds_estimate: number };\n\n/**\n * A function of a node's params, declared as a METHOD so it is checked\n * bivariantly: that is what lets a concrete `NodeDefinition<I, P, O>` stand in\n * for the type-erased `AnyNodeDefinition` a registry holds, with no cast at every\n * registration. The registry only ever calls it with params its own schema parsed.\n */\ntype ParamsFn<P, R> = { fn(params: P): R }[\"fn\"];\n\nexport type CostFn<P> = ParamsFn<{ params: P }, CostEstimate>;\n\nexport type ExecuteArgs<I, P> = {\n inputs: I;\n params: P;\n ctx: ExecCtx;\n};\n\n/**\n * Node-supplied extra validation hook. Runs after the node's static Zod\n * schema passes — used by nodes whose param shape depends on external state\n * (e.g. `hyperframe_render` consults the composition's meta.json).\n * Returns issue messages keyed by the dotted param path to surface in\n * `canvas validate` output. Empty array means OK.\n */\ntype ValidateExtraIssue = { path: string; message: string };\n\n/**\n * What a raw input will hold at run time, as far as the graph says before the\n * run: an asset kind, `list` (a whole list output rather than one element of it),\n * `value` (not an asset at all), or `unknown` (only running it would tell).\n */\nexport type StaticInputKind = AssetRef[\"kind\"] | \"list\" | \"value\" | \"unknown\";\n\n/** Where an input's file comes from before the run: a workspace file or a URL an `ingest` reads. */\nexport type InputSource = { path: string } | { url: string };\n\nexport type ValidateExtraFn = (args: {\n rawParams: unknown;\n rawInputs: unknown;\n /** The canvas node id, when validating a whole canvas. */\n nodeId?: string;\n /** Reads a raw input value's kind off the node it references. */\n inputKindOf?: (value: unknown) => StaticInputKind;\n /** Where a raw input's file comes from before the run; undefined for one a node generates. */\n inputSourceOf?: (value: unknown) => InputSource | undefined;\n}) => Promise<ValidateExtraIssue[]> | ValidateExtraIssue[];\n\n/**\n * Node-supplied cache-key extras hook. Returns extra fields the executor\n * folds into the cache key alongside `params` — used by nodes whose output\n * depends on external content (e.g. composition directory contents).\n */\nexport type CacheKeyExtrasFn = (args: { params: unknown }) => Promise<Record<string, string>>;\n\n/**\n * Per-output asset-kind declaration. Either a static map (most nodes — each\n * named output produces a fixed kind) or a function of params (e.g. the\n * `ingest` node, whose output kind is decided by `params.expect`).\n *\n * Validators and the dashboard read this to type the output port statically\n * without running the node. Remote nodes also use it at runtime so the\n * delegate ingestor knows how to wrap returned bytes.\n */\nexport type OutputKindsSpec<P> = Record<string, AssetRef[\"kind\"]> | ParamsFn<P, Record<string, AssetRef[\"kind\"]>>;\n\nexport function resolveOutputKinds<P>(\n spec: OutputKindsSpec<P> | undefined,\n params: P,\n): Record<string, AssetRef[\"kind\"]> {\n if (!spec) return {};\n return typeof spec === \"function\" ? spec(params) : spec;\n}\n\n/**\n * What a node's output will measure, known before anything runs: its length and\n * raster. Each field is present only when the params (and the timing of what is\n * wired in) settle it; an absent field means \"only running it would tell\".\n */\nexport type StaticTiming = { duration_s?: number; width?: number; height?: number };\n\n/**\n * Node-supplied static timing hook: the output's length and size from its parsed\n * params plus the static timing of each wired input (keyed by input slot, absent\n * when upstream cannot say). Pure; `canvas validate` calls it to prove the\n * timeline without running anything.\n */\nexport type StaticTimingFn<P> = ParamsFn<\n { params: P; inputs: Readonly<Record<string, StaticTiming | undefined>> },\n StaticTiming\n>;\n\nexport type NodeDefinition<I = unknown, P = unknown, O = unknown> = {\n id: string;\n version: string;\n category: string;\n summary: string;\n when_to_use?: string;\n inputs: z.ZodType<I>;\n params: z.ZodType<P>;\n outputs: z.ZodType<O>;\n /** \"local\" runs in-process; \"remote\" delegates to Convex via ctx.client */\n location: \"local\" | \"remote\";\n /**\n * Local node that only forwards asset refs and never reads their bytes\n * (e.g. `collect`). Exempts its upstream producers from the forced R2→disk\n * download local consumers otherwise trigger.\n */\n passthroughRefs?: true;\n cost?: CostFn<P>;\n validateExtra?: ValidateExtraFn;\n cacheKeyExtras?: CacheKeyExtrasFn;\n outputKinds?: OutputKindsSpec<P>;\n staticTiming?: StaticTimingFn<P>;\n execute: ParamsFn<ExecuteArgs<I, P>, Promise<O>>;\n};\n\n/**\n * Any node definition, its input/param/output types erased: what a registry\n * holds and the executor runs. Every `defineNode(...)` result is assignable to it.\n */\nexport type AnyNodeDefinition = NodeDefinition<unknown, unknown, unknown>;\n\nexport function defineNode<I, P, O>(def: NodeDefinition<I, P, O>): NodeDefinition<I, P, O> {\n return def;\n}\n","/** Damerau-Levenshtein distance with single-edit awareness. */\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n const dp = makeDp(m, n);\n for (let i = 1; i <= m; i++) fillRow(dp, a, b, i, n);\n return (dp[m] as number[])[n] ?? 0;\n}\n\nfunction makeDp(m: number, n: number): number[][] {\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array<number>(n + 1).fill(0));\n for (let i = 0; i <= m; i++) (dp[i] as number[])[0] = i;\n for (let j = 0; j <= n; j++) (dp[0] as number[])[j] = j;\n return dp;\n}\n\nfunction fillRow(dp: number[][], a: string, b: string, i: number, n: number): void {\n const rowI = dp[i] as number[];\n const rowPrev = dp[i - 1] as number[];\n const rowPrev2 = i >= 2 ? (dp[i - 2] as number[]) : undefined;\n for (let j = 1; j <= n; j++) {\n rowI[j] = stepCost(a, b, i, j, rowI, rowPrev, rowPrev2);\n }\n}\n\nfunction stepCost(\n a: string,\n b: string,\n i: number,\n j: number,\n rowI: number[],\n rowPrev: number[],\n rowPrev2: number[] | undefined,\n): number {\n const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;\n const del = (rowPrev[j] ?? 0) + 1;\n const ins = (rowI[j - 1] ?? 0) + 1;\n const sub = (rowPrev[j - 1] ?? 0) + cost;\n let best = Math.min(del, ins, sub);\n if (\n i > 1 &&\n j > 1 &&\n rowPrev2 &&\n a.charCodeAt(i - 1) === b.charCodeAt(j - 2) &&\n a.charCodeAt(i - 2) === b.charCodeAt(j - 1)\n ) {\n best = Math.min(best, (rowPrev2[j - 2] ?? 0) + 1);\n }\n return best;\n}\n\nexport function didYouMean(query: string, candidates: readonly string[], max = 3): string[] {\n if (candidates.length === 0 || !query) return [];\n const threshold = Math.max(1, Math.floor(query.length / 3) + 1);\n const scored: { c: string; d: number }[] = [];\n for (const c of candidates) {\n const d = distance(query.toLowerCase(), c.toLowerCase());\n if (d <= threshold) scored.push({ c, d });\n }\n scored.sort((a, b) => a.d - b.d || a.c.localeCompare(b.c));\n return scored.slice(0, max).map((s) => s.c);\n}\n","import type { Canvas, NodeDecl } from \"../../schema/canvas.ts\";\nimport { parseRefExpr, REF_PREFIX } from \"../../schema/canvas.ts\";\nimport type { ValidationIssue } from \"../errors.ts\";\nimport type { NodeRegistry } from \"../registry.ts\";\n\n/** What a validation check reads, and the issue list it appends to. */\nexport type LintContext = {\n canvas: Canvas;\n registry: NodeRegistry;\n issues: ValidationIssue[];\n idToIndex: Map<string, number>;\n};\n\ntype Severity = \"error\" | \"warning\";\n\n/**\n * A domain rule: what it knows about making a video, an image or a mix, as opposed\n * to whether the canvas is a well-formed graph. Declares every issue code it can\n * raise and that code's severity; the runner holds it to that declaration.\n */\nexport type LintRule = {\n name: string;\n codes: Readonly<Record<string, Severity>>;\n check: (ctx: LintContext) => void;\n};\n\n/** A rule that reads files the canvas points at, so it only runs in the deep pass. */\nexport type DeepLintRule = {\n name: string;\n codes: Readonly<Record<string, Severity>>;\n check: (canvas: Canvas, issues: ValidationIssue[]) => Promise<void>;\n};\n\n/** The node a `$ref:<id>.<output>` expression points at, when it is one. */\nexport function refNodeOf(ctx: LintContext, value: unknown): NodeDecl | null {\n if (typeof value !== \"string\" || !value.startsWith(REF_PREFIX)) return null;\n const parsed = parseRefExpr(value);\n if (!parsed) return null;\n const idx = ctx.idToIndex.get(parsed.nodeId);\n return idx === undefined ? null : (ctx.canvas.nodes[idx] as NodeDecl);\n}\n\n/**\n * Whether the canvas states what its nodes are (`tags`). A tagged canvas is read by\n * its tags; only an untagged one (written before tags existed) falls back to the\n * `s<scene>_…` id conventions its scaffold used.\n */\nexport function isTagged(ctx: LintContext): boolean {\n return ctx.canvas.nodes.some((n) => n.tags !== undefined);\n}\n\nexport type VideoMetaT = NonNullable<NonNullable<Canvas[\"metadata\"]>[\"video\"]>;\n\n/** A check that only applies to a canvas carrying a video scaffold's timing plan. */\nexport function onVideoCanvas(check: (ctx: LintContext, meta: VideoMetaT) => void): (ctx: LintContext) => void {\n return (ctx) => {\n const meta = ctx.canvas.metadata?.video;\n if (meta) check(ctx, meta);\n };\n}\n","import type { NodeDecl } from \"../../../schema/canvas.ts\";\nimport { type LintContext, type LintRule, refNodeOf } from \"../context.ts\";\n\nconst CODES = {\n DUCK_UNITS: \"AUDIO_DUCK_UNITS\",\n MUSIC_BED_TOO_LOUD: \"AUDIO_MUSIC_BED_TOO_LOUD\",\n MUSIC_BED_NOT_DUCKED: \"AUDIO_MUSIC_BED_NOT_DUCKED\",\n MUSIC_BED_SCORED_TO_ARC: \"AUDIO_MUSIC_BED_SCORED_TO_ARC\",\n} as const;\n\n// ---------------------------------------------------------------------------\n// The music bed under a voice: a level, and a ducker that knows its units.\n//\n// Both numbers below were paid for. A bed at -12 dB crowded the voice, so the\n// scaffolder pinned -20 dB; an agent hand-building a finishing canvas then\n// measured the mix across a window that included the SILENT end card, read\n// \"too quiet\", raised the whole bed to -13 dB, and the client rejected the ad as\n// \"la musica esta demasiada alta\". The same canvas passed `attack: 0.05,\n// release: 0.45` to the sidechain ducker — seconds, where ffmpeg takes\n// milliseconds — so the bed snapped back to full level between syllables, which\n// is what \"too loud\" sounds like even at a correct static level.\n//\n// These checks run on ANY canvas carrying an `audio_timeline`, not just a\n// scaffolded one, because a hand-built finishing canvas is exactly where the\n// scaffolder's settled numbers are re-derived from scratch.\n// ---------------------------------------------------------------------------\n\n/** A music bed under speech lives at or below this. See the note above. */\nconst MUSIC_BED_MAX_GAIN_DB = -18;\n\n/** Below this many ms, a sidechain attack can only be a seconds value. */\nconst DUCK_MIN_ATTACK_MS = 0.5;\n\n/** Below this many ms, the release lets the bed pump back up between words. */\nconst DUCK_MIN_RELEASE_MS = 10;\n\n/** Node types whose audio output IS someone speaking. */\nconst SPEECH_NODE_TYPES = new Set([\"tts\", \"dialogue\", \"audio_voice_convert\", \"video_lipsync\"]);\n\n/** A slot named for what it carries, for speech extracted by a bare ffmpeg node. */\nconst SPEECH_SLOT_RE = /voice|speech|\\bvo\\b|^vo|_vo|dialog|narration/i;\n\nconst MUSIC_SLOT_RE = /music|score|\\bbed\\b/i;\n\ntype MixTrack = { slot: string; gain_db?: number; duration_s?: number };\n\ntype DuckSpec = { track?: unknown; attack?: unknown; release?: unknown };\n\nfunction audioTimelineTracks(node: NodeDecl): MixTrack[] {\n const tracks = (node.params as Record<string, unknown> | undefined)?.tracks;\n if (!Array.isArray(tracks)) return [];\n return tracks.filter(\n (t): t is MixTrack => typeof t === \"object\" && t !== null && typeof (t as MixTrack).slot === \"string\",\n );\n}\n\n/** Every `audio_timeline` in the canvas, checked on its own. */\nfunction checkAudioBedMix(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type === \"audio_timeline\") checkOneAudioMix(ctx, n);\n }\n}\n\n/**\n * The three checks on one `audio_timeline`. Track kinds are read from the WIRING\n * (which node feeds the slot) and fall back to the slot's name, so a bed\n * ingested from a file still counts as music.\n */\nfunction checkOneAudioMix(ctx: LintContext, node: NodeDecl): void {\n const tracks = audioTimelineTracks(node);\n if (tracks.length === 0) return;\n const duck = (node.params as Record<string, unknown> | undefined)?.duck as DuckSpec | undefined;\n checkDuckUnits(ctx, node, duck);\n\n const sourceType = (slot: string): string | null => refNodeOf(ctx, node.inputs?.[slot])?.type ?? null;\n const speech = tracks.filter((t) => {\n const type = sourceType(t.slot);\n return (type !== null && SPEECH_NODE_TYPES.has(type)) || SPEECH_SLOT_RE.test(t.slot);\n });\n if (speech.length === 0) return;\n // A WINDOWED music track is the sanctioned way to lift the bed over a silent\n // tail (an end card), so it is exempt from both checks below — the rule is\n // about the bed that runs under the speech.\n const beds = tracks.filter(\n (t) => (sourceType(t.slot) === \"music\" || MUSIC_SLOT_RE.test(t.slot)) && t.duration_s === undefined,\n );\n if (beds.length === 0) return;\n\n checkBedLevels(ctx, node, beds);\n checkBedDucked(ctx, node, beds, speech, duck);\n for (const bed of beds) checkBedNotScored(ctx, node, refNodeOf(ctx, node.inputs?.[bed.slot]));\n}\n\n// A bed BRIEFED AS A SCORE: movement asked for in the music prompt of a bed that\n// plays under somebody talking. `video-background-music-v1` watches the cut, so a\n// description that tracks the speaker's emotion (\"quiet and slightly tense while\n// she is overwhelmed … resolving warm and confident\") comes back as film score,\n// and the client's verdict on that exact cut was \"muy dramática\". Narrow on\n// purpose: these words are only wrong UNDER SPEECH, which is the only case this\n// runs in.\nconst MUSIC_ARC_RE =\n /\\b(?:emotional arc|follows? (?:her|his|their|the) arc|swell(?:s|ing)?|crescendo|climax|build(?:s|ing) (?:to|into)|resolv(?:e|es|ing)|dramatic|epic|tense|tension)\\b/i;\n\n/** Advisory: the bed under a voice was briefed to move with the speaker's emotion. */\nfunction checkBedNotScored(ctx: LintContext, node: NodeDecl, source: NodeDecl | null): void {\n if (source?.type !== \"music\") return;\n const params = source.params as Record<string, unknown> | undefined;\n // Scoped to the model that WATCHES the cut. `music-v1` composes blind from a\n // prompt, and `scaffold-video` deliberately briefs it with the reference ad's\n // own arc (`musicArcDigest`) — a considered decision, not a defect to warn on.\n if (params?.model !== \"elevenlabs/video-background-music-v1\") return;\n const brief = [params?.description, params?.prompt].filter((v): v is string => typeof v === \"string\").join(\" \");\n const found = MUSIC_ARC_RE.exec(brief);\n if (!found) return;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(source.id) ?? -1}].params.description`,\n code: CODES.MUSIC_BED_SCORED_TO_ARC,\n severity: \"warning\",\n node_id: source.id,\n node_type: source.type,\n message: `\"${source.id}\" briefs the bed with \"${found[0]}\", and \"${node.id}\" mixes it under speech — this model watches the cut, so movement asked for here comes back as a film score (a testimonial briefed that way was rejected as \"muy dramática\"). Under a voice, name ONE steady character and forbid movement: no build, no drop, no climax, nothing that follows the arc`,\n });\n}\n\n/** Blocking: a sidechain attack/release given in seconds, where ffmpeg takes ms. */\nfunction checkDuckUnits(ctx: LintContext, node: NodeDecl, duck: DuckSpec | undefined): void {\n const index = ctx.idToIndex.get(node.id) ?? -1;\n if (typeof duck?.attack === \"number\" && duck.attack < DUCK_MIN_ATTACK_MS) {\n ctx.issues.push({\n path: `nodes[${index}].params.duck.attack`,\n code: CODES.DUCK_UNITS,\n node_id: node.id,\n node_type: node.type,\n message: `\"${node.id}\" ducks with attack ${duck.attack} — this parameter is MILLISECONDS, so that is instantaneous. Use 5-50 (default 5)`,\n });\n }\n if (typeof duck?.release === \"number\" && duck.release < DUCK_MIN_RELEASE_MS) {\n ctx.issues.push({\n path: `nodes[${index}].params.duck.release`,\n code: CODES.DUCK_UNITS,\n node_id: node.id,\n node_type: node.type,\n message: `\"${node.id}\" ducks with release ${duck.release} — this parameter is MILLISECONDS, so the bed jumps back to full level between syllables and the mix sounds like the music is too loud. Use 200-800 (default 300); 0.45 is a seconds value`,\n });\n }\n}\n\n/** Advisory: the bed under the voice is louder than the settled ceiling. */\nfunction checkBedLevels(ctx: LintContext, node: NodeDecl, beds: readonly MixTrack[]): void {\n const index = ctx.idToIndex.get(node.id) ?? -1;\n for (const bed of beds) {\n if ((bed.gain_db ?? 0) <= MUSIC_BED_MAX_GAIN_DB) continue;\n const level = bed.gain_db === undefined ? \"full level (no gain_db)\" : `${bed.gain_db} dB`;\n ctx.issues.push({\n path: `nodes[${index}].params.tracks`,\n code: CODES.MUSIC_BED_TOO_LOUD,\n severity: \"warning\",\n node_id: node.id,\n node_type: node.type,\n message: `\"${node.id}\" runs the music bed \"${bed.slot}\" at ${level} under speech — a bed belongs at ${MUSIC_BED_MAX_GAIN_DB} dB or below (the scaffolder pins -20; -13 was rejected by a client as too loud). If a silent tail sounds empty, add the same bed as a SECOND windowed track (start_s + duration_s) with its own higher gain instead of raising this one`,\n });\n }\n}\n\n/** Advisory: a bed mixed flat under a voice, with no sidechain duck naming it. */\nfunction checkBedDucked(\n ctx: LintContext,\n node: NodeDecl,\n beds: readonly MixTrack[],\n speech: readonly MixTrack[],\n duck: DuckSpec | undefined,\n): void {\n if (beds.some((bed) => bed.slot === duck?.track)) return;\n const quoted = (list: readonly MixTrack[]) => list.map((t) => `\"${t.slot}\"`).join(\", \");\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(node.id) ?? -1}].params.duck`,\n code: CODES.MUSIC_BED_NOT_DUCKED,\n severity: \"warning\",\n node_id: node.id,\n node_type: node.type,\n message: `\"${node.id}\" mixes a music bed (${quoted(beds)}) under speech (${quoted(speech)}) with no sidechain duck on it — a flat bed fights the voice in every gap. Add params.duck { track: \"${beds[0]?.slot}\", against: [${quoted(speech)}] }`,\n });\n}\n\n/** One rule, because its four checks run per mix and report in that order. */\nexport const audioBedMixRule: LintRule = {\n name: \"audio-bed-mix\",\n codes: {\n [CODES.DUCK_UNITS]: \"error\",\n [CODES.MUSIC_BED_TOO_LOUD]: \"warning\",\n [CODES.MUSIC_BED_NOT_DUCKED]: \"warning\",\n [CODES.MUSIC_BED_SCORED_TO_ARC]: \"warning\",\n },\n check: checkAudioBedMix,\n};\n","/**\n * Per-model GENERATION PROFILES. Each model we call — video (Seedance, Veo) and\n * image (gpt-image, Gemini, Recraft) — has a documented best-practice prompt\n * structure, length budget, dialogue/audio convention, negative-prompt mechanism,\n * and request-param set. The scaffolder used to emit ONE prompt shape and set none\n * of the param levers; these profiles are the single seam where each model's rules\n * live, read by BOTH the prompt builders and the validator so the two can't drift.\n *\n * Sources (see the session research briefs): fal.ai/learn + BytePlus ModelArk for\n * Seedance; ai.google.dev + cloud.google.com for Veo; OpenAI Cookbook + OpenRouter\n * image docs for gpt-image; ai.google.dev for Gemini image.\n */\nimport { type IMAGE_GENERATE_MODELS, MODEL_REGISTRY, type VIDEO_GENERATE_MODELS } from \"@baker/canvas-contract\";\n\n/** Veo permits generating adult humans from a keyframe — the sanctioned escape from\n * ByteDance's real-person filter; it is also the ONLY legal value for image-to-video\n * in the EU/UK/CH/MENA. Set on every Veo clip. */\nconst VEO_PERSON_GENERATION = \"allow_adult\";\n/** Veo frequently burns in captions/subtitles even when unasked (a known model\n * artifact); the dedicated negative_prompt param is the real suppression lever\n * (an inline \"no subtitles\" line alone is unreliable). Phrased as absences. */\nconst VEO_NEGATIVE_PROMPT =\n \"subtitles, captions, on-screen text, watermark, logo, warped face, distorted hands, extra fingers, low quality\";\n/** Kling has a real `negative_prompt` param (forwarded top-level by the backend);\n * phrase as absences, same family as Veo's. */\nconst KLING_NEGATIVE_PROMPT =\n \"warped face, distorted hands, extra fingers, morphing, flicker, on-screen text, watermark, low quality\";\n/** Kling's prompt-adherence dial — higher hugs the prompt (good for a scripted\n * scroll-stopper), lower gives the model more motion freedom. */\nconst KLING_CFG_SCALE = 0.7;\n\n/**\n * A video model's clip-prompt + request-param profile. `dialogueDirective` and the\n * literal `Dialogue: \"…\"` marker it emits are the parse contract `nativeDialogueOf`\n * (and the validator's speech-fit gates) read — both profiles MUST keep that marker.\n */\nexport type ClipPromptProfile = {\n id: \"seedance\" | \"veo\" | \"kling\" | \"omni\";\n /** The talking-head directive carrying the verbatim spoken line. */\n dialogueDirective(line: string, accent?: string): string;\n /** Model-specific hard rules appended to every clip prompt (may be empty). */\n extraDirectives: string[];\n /** Affirmative quality/stability directives for models with NO negative_prompt param\n * (Seedance). Emitted as a positive \"keep it stable\" tail, NOT an \"Avoid:\" list: Seedance\n * has no negative field, and inline negation (\"no warped hands\") is weak grammar around a\n * strong activation that tends to SUMMON the very artifact — the fal.ai guide's rule is\n * \"phrase everything positively\" (see seedance-mechanics.md §3). Empty for models that take\n * a real negative_prompt param (Veo, Kling), which parse literal exclusions correctly. */\n stabilityDirectives: string[];\n /** The image-to-video instruction to hold the source frame's framing/palette. */\n keyframeInstruction: string;\n /** Soft word budget for one clip prompt — the validator warns above it. */\n wordBudget: number;\n /** Request params merged onto every `video_generate` node for this model. */\n paramDefaults: Record<string, unknown>;\n};\n\n// Both video profiles now put delivery in PROSE, not [brackets]: the Seedance 2.0\n// guide (and Veo's) carry emotion in the wording around the quoted line; Seedance\n// renders literal brackets and Veo burns them as on-screen text.\nconst SPEAKS_PROSE = (line: string, accent?: string) =>\n `The person speaks to camera; lip-sync follows the dialogue verbatim, with delivery and emotion carried in the wording itself (no bracketed cues).` +\n // The model picks a variety of the language on its own, and picks the commonest one:\n // three ads set in Spain came back in Latin American Spanish. Pinning a peninsular voice\n // does not fix it — the re-voicing is speech-to-speech, so it carries the delivery across\n // and swaps only the timbre, and the accent lives in the delivery. It has to be said HERE,\n // where the speech is made.\n `${accent ? ` The speech is in ${accent}.` : \"\"} Dialogue: \"${line}\"`;\n\nexport const SEEDANCE_PROFILE: ClipPromptProfile = {\n id: \"seedance\",\n dialogueDirective: SPEAKS_PROSE,\n extraDirectives: [],\n // Seedance has no negative_prompt field. Phrase the anti-artifact intent as the POSITIVE\n // state we want (crisp hands, one stable face, smooth coherent motion, locked identity) —\n // an \"Avoid: warped fingers, morphing faces\" list tends to plant those very artifacts.\n stabilityDirectives: [\n \"hands and fingers crisp and anatomically correct\",\n \"one consistent face and identity across every frame\",\n \"smooth, temporally coherent motion that holds steady frame to frame\",\n ],\n keyframeInstruction: \"Preserve the composition and colors of the first frame; change only the motion described.\",\n // ~120 words is the Seedance quality sweet spot: past it the model starts dropping beats and\n // the attention budget dilutes. The validator warns above this; composite scenes that stack\n // many brief `parts` are the usual offenders.\n wordBudget: 120,\n paramDefaults: {},\n};\n\nexport const VEO_PROFILE: ClipPromptProfile = {\n id: \"veo\",\n dialogueDirective: SPEAKS_PROSE,\n // Veo's known failure mode: quoted dialogue triggers burned-in subtitles. Belt\n // (inline) and braces (the negative_prompt param, below) — the param is the real lever.\n extraDirectives: [\"No subtitles, no captions, no on-screen text of any kind.\"],\n stabilityDirectives: [], // Veo takes a negative_prompt PARAM instead (paramDefaults).\n keyframeInstruction: \"Describe the transition and camera move between the frames; the frames fix the content.\",\n wordBudget: 200,\n paramDefaults: { person_generation: VEO_PERSON_GENERATION, negative_prompt: VEO_NEGATIVE_PROMPT },\n};\n\nexport const KLING_PROFILE: ClipPromptProfile = {\n id: \"kling\",\n dialogueDirective: SPEAKS_PROSE,\n // Belt (inline) and braces (the negative_prompt param) — the param is the real lever.\n extraDirectives: [\"Single, deliberate camera move — no whip pans or rapid cuts within the clip.\"],\n stabilityDirectives: [], // Kling takes a negative_prompt PARAM instead (paramDefaults).\n keyframeInstruction: \"Preserve the composition and colors of the first frame; animate the motion described.\",\n wordBudget: 200,\n paramDefaults: { negative_prompt: KLING_NEGATIVE_PROMPT, cfg_scale: KLING_CFG_SCALE },\n};\n\nexport const OMNI_PROFILE: ClipPromptProfile = {\n id: \"omni\",\n dialogueDirective: SPEAKS_PROSE,\n // Omni renders speech and ambience natively in the same pass, so the prompt\n // carries the audio direction; there is no separate audio toggle to set.\n extraDirectives: [\"No subtitles, no captions, no on-screen text of any kind.\"],\n // Omni exposes NO negative_prompt param — the API rejects the field outright —\n // so the anti-artifact intent has to be carried positively, Seedance-style,\n // rather than as an \"Avoid:\" list Veo would have taken as a param.\n stabilityDirectives: [\n \"hands and fingers crisp and anatomically correct\",\n \"one consistent face and identity across every frame\",\n \"smooth, temporally coherent motion that holds steady frame to frame\",\n ],\n keyframeInstruction: \"Preserve the composition and colors of the first frame; change only the motion described.\",\n // Omni reasons over a 1M-token context and rewards a fuller brief than the\n // diffusion-style models, which dilute past their sweet spot.\n wordBudget: 200,\n // Nothing to set. Every knob the other profiles use (negative_prompt,\n // person_generation, cfg_scale) is a pre-dispatch 400 on this model.\n paramDefaults: {},\n};\n\n/**\n * The prompt profile of every curated video model, by exact registry id. `null` is a\n * deliberate \"no profile yet\": the scaffolder falls back to the Seedance prompt shape\n * and the lint rule says so. Typed over the whole roster, so a model added to the\n * registry does not compile until someone decides which profile it gets.\n *\n * Omni has its own entry although it shares Google's namespace with Veo: it takes\n * none of Veo's params, and Veo's negative_prompt default is a 400 there.\n */\nexport const CLIP_PROFILES: Readonly<Record<(typeof VIDEO_GENERATE_MODELS)[number], ClipPromptProfile | null>> = {\n \"bytedance/seedance-2.0\": SEEDANCE_PROFILE,\n \"bytedance/seedance-2.5\": SEEDANCE_PROFILE,\n \"google/gemini-omni-1.1-flash\": OMNI_PROFILE,\n \"google/gemini-omni-flash\": OMNI_PROFILE,\n \"google/veo-3.1\": VEO_PROFILE,\n \"google/veo-3.1-fast\": VEO_PROFILE,\n \"google/veo-3.1-lite\": VEO_PROFILE,\n \"kwaivgi/kling-v3.0-pro\": KLING_PROFILE,\n \"x-ai/grok-imagine-video\": null,\n \"x-ai/grok-imagine-video-1.5\": null,\n \"alibaba/wan-2.7\": null,\n \"alibaba/wan-3.0\": null,\n \"runway/gen-4.5\": null,\n \"alibaba/happyhorse-1.1\": null,\n};\n\n/** The profile for a video model id; undefined = unprofiled or unregistered model. */\nexport function clipProfileFor(modelId: string): ClipPromptProfile | undefined {\n return (CLIP_PROFILES as Readonly<Record<string, ClipPromptProfile | null>>)[modelId] ?? undefined;\n}\n\n/**\n * A clip's persuasion INTENT, derived by the scaffolder from the scene's role.\n * The recipe layer keys \"astonishing default\" param overrides off this so an\n * operator doesn't have to know each model's knobs.\n */\nexport type ClipIntent = \"hook\" | \"hero\" | \"cta\" | \"body\";\n\n/**\n * Opinionated per-intent param overrides, merged AFTER a clip's base params so\n * they can intentionally raise the quality dial for the beats that earn it.\n * Only emits params the backend actually forwards for the model — never an inert\n * knob. Currently: hero beats claim the photoreal resolution ceiling; hook beats\n * on Kling max out prompt adherence so the scripted scroll-stopper renders exactly.\n *\n * Takes the MODEL ID, not just the profile, because the two Omni versions share\n * one profile and disagree about exactly this: 1.1 declares a `resolution` enum\n * and the legacy preview has no such param, so a hero beat scaffolded onto the\n * preview would fail `baker canvas validate` as an unknown_param. Asking the\n * registry is also what keeps this honest for whatever the roster holds next —\n * a hardcoded family test was what got the previous version of this wrong.\n */\nexport function clipParamRecipe(\n model: string,\n profile: ClipPromptProfile,\n intent: ClipIntent,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n // The hero/reveal beat is where cine quality matters most — pay for 1080p, on\n // every model that renders it.\n if (intent === \"hero\" && videoModelRenders(model, \"1080p\")) {\n out.resolution = \"1080p\";\n }\n if (intent === \"hook\" && profile.id === \"kling\") {\n out.cfg_scale = 0.85;\n }\n return out;\n}\n\n/** Whether the model's registry entry lists this resolution. */\nfunction videoModelRenders(model: string, resolution: string): boolean {\n const schema = MODEL_REGISTRY.video_generate[model]?.params.resolution;\n return schema?.kind === \"string\" && (schema.enum ?? []).includes(resolution);\n}\n\n/** The native line a scaffolded clip prompt asks the video model to speak, when present. */\nexport function nativeDialogueOf(prompt: unknown): string | undefined {\n if (typeof prompt !== \"string\") return undefined;\n const m = prompt.match(/Dialogue: \"(.*)\"/);\n return m?.[1]?.trim() || undefined;\n}\n\n/**\n * The job a picture in a static brief does, and so where it goes: the model reads the images\n * before the text and gives the first the most weight, so what must stay exact leads and a\n * layout reference, when there is one at all, trails.\n */\nexport type BriefImageRole = \"base\" | \"identity\" | \"product\" | \"prop\" | \"logo\" | \"type\" | \"layout\";\n\n/**\n * How a static ad brief is written for one family of image models (`static-brief.ts`\n * compiles every brief through these). Each rule names its source in the research notes\n * (`.context/prompting-*.md`, `.context/image-prompting-research-2026.md`); the bench\n * results that confirm or change them are added beside each rule as they land.\n */\nexport type ImageBriefRules = {\n /** Short labelled sections (gpt-image), or sentences that give the reason (Gemini). */\n structure: \"labelled_sections\" | \"flowing_sentences\";\n /** The most words one brief may take; the compiler trims the scene to fit and never the copy. */\n wordBudget: number;\n /** The order pictures are attached and numbered in (\"Image 1\", \"Image 2\", …), one role each. */\n referenceOrder: readonly BriefImageRole[];\n /** How the exact text is given. */\n text: {\n /** One numbered, quoted line per string, each asked for once, then an allow-list. */\n numberedQuotes: true;\n /** Symbols (®, ™, €) are quoted inline and also named in words. */\n nameSymbols: true;\n /** Accented letters are listed as letters to keep. */\n keepAccents: boolean;\n };\n /** How exclusions are phrased: one closing \"Avoid:\" list, or a positive closing sentence. */\n constraints: \"avoid_list_last\" | \"positive_last\";\n /**\n * The realism vocabulary for a photo: capture cues in plain words, never lens figures. A phone\n * capture (selfie, UGC) gets the unpolished cues; any other photo keeps its own light; people\n * in any photo get the real-person cues.\n */\n realism: {\n native: PhotoRealism;\n produced: PhotoRealism;\n /** How people in a photo look: real skin, lit faces, ordinary expressions. */\n people: string;\n };\n /** What a brief asks the request for, before the registry snaps it to the pinned model. */\n params: { n: number; quality?: string; imageSize?: string };\n};\n\n/** One kind of photo's realism cues. */\nexport type PhotoRealism = {\n /** The phrase that switches the model into photo mode. */\n photo: string;\n /** Texture and honesty cues, phrased as the profile phrases constraints. */\n texture: string;\n /** Why the photo looks the way it does, for a model that reasons from purpose (empty when it does not help). */\n purpose: string;\n};\n\n/**\n * An image model's frame-prompt + request-param profile. The motion-brief skeleton\n * is model-agnostic, but constraint PLACEMENT and the realism cue are not: OpenAI's\n * gpt-image weights early text as compositional guidance, so exclusions must come\n * LAST and \"photorealistic\" must be stated explicitly; Gemini reads exclusions fine\n * inline; Recraft renders vector art, so no photoreal cue. `brief` is how a static ad\n * brief is written for the family.\n */\nexport type ImageModelProfile = {\n id: \"gpt-image\" | \"gemini\" | \"recraft\";\n /** Where the clean-plate / no-text exclusions block sits in the prompt. */\n constraintPlacement: \"last\" | \"inline\";\n /** Emit an explicit \"photorealistic\" cue (photoreal models only). */\n photorealCue: boolean;\n /** Request params merged onto every `image_generate` node for this model. */\n paramDefaults: Record<string, unknown>;\n brief: ImageBriefRules;\n};\n\n// Identity and product first, a layout reference last or not at all: OpenAI staff, \"the\n// images come before the text prompt\" and only the first gets \"extra richness\"\n// (prompting-openai-gpt-image.md §0.7, §3); Google, \"Keep the person's facial features exactly\n// the same as Image 1\" (prompting-google-gemini-image.md §4). The edit base leads an edit\n// because a mask and the \"change only\" instruction apply to the first image (openai §4).\nconst REFERENCE_ORDER: readonly BriefImageRole[] = [\"base\", \"identity\", \"product\", \"prop\", \"logo\", \"type\", \"layout\"];\n\nconst GPT_IMAGE_BRIEF: ImageBriefRules = {\n // \"organize the prompt as scene, subject, details, and constraints, using labeled sections\";\n // \"written like a creative brief\" (openai §1, S1/S5 §4.6).\n structure: \"labelled_sections\",\n // Bench E1: this structure vs the pre-rebuild wrapper + blueprint JSON on 181 paired ads:\n // shippable 8.3% vs 4.4%, near-clone of the source 7% vs 17%, 0 lint contradictions vs 11 codes on\n // every legacy prompt, 15% cheaper (BENCH.md#E1; pairwise pending).\n // Official examples run 40-150 words; the synthesis caps briefs at 150-400\n // (image-prompting-research-2026.md §6, openai §1). Ours carried 1,615 (prompt-audit.md §1).\n wordBudget: 400,\n referenceOrder: REFERENCE_ORDER,\n // Quote each line, say how many times it appears, allow-list it, spell symbols out, keep\n // accents: openai §2 (\"Render the tagline exactly once\", \"Include ONLY this card text\").\n text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },\n // Official prompts close on a Constraints block or a \"No…\" sentence (openai §7).\n constraints: \"avoid_list_last\",\n realism: {\n // \"include the word 'photorealistic' directly in the prompt\" (openai §5, S5).\n // \"real texture (pores, wrinkles, fabric wear, imperfections)\"; camera specs are \"cues,\n // not a guarantee\" (openai §5), so capture is described in words, never figures.\n native: {\n photo: \"photorealistic: a real, unedited phone photo\",\n texture: \"real texture and wear on every surface, natural light, light sensor noise, unposed and unretouched\",\n purpose: \"\",\n },\n // Brief v2: a studio or hero shot keeps its own light; the phone cues made them candid and busy\n // (BENCH.md § E1, the judge's remaining reasons), and the user found the old prompt's more polished.\n produced: {\n photo: \"photorealistic: a real photograph\",\n texture: \"real textures, true-to-life light and colour\",\n purpose: \"\",\n },\n // The user: people look \"too AI\", faces too dark; wants natural, real-looking people (BENCH.md § \"E1 v2\"),\n // and again on the final sets, \"more natural please\" (§ \"Fix round\"); and real people's photos are\n // taken on a phone, not a professional camera (AV2, scripts/creative-evals/avatar-realism.mjs).\n people: \"candid people shot on a phone: real skin, natural light, relaxed expressions\",\n },\n // n 1: the variants are one call per direction, not one n=3 call. Same cost (n is billed per\n // image, E0) and returns 3/3 either way, but one n=3 call gave >=2 distinct options in 25% of\n // ads vs 39% for three calls (n=99/100; n=2 + 1 call: 31% vs 36% on 39 shared ads)\n // (.context/static-review/BENCH.md#E3).\n // quality max: the deterministic checks tie (shippable 9-12% on every step, n=100), but on the\n // E4 gallery (30 rated pairs) the user's win rate is max 69%, xhigh 54%, high 47%, Flare 34%,\n // and the judge's on the same pairs 73%, 65%, 39%, 31%. Max costs $0.166 per image vs $0.085\n // for xhigh and 80 s vs 44 s (BENCH.md#E4, § \"E1 v2\"); a round renders twice in a row (the\n // options, then each size), so three ads took most of an hour. Oct 2026 the user chose xhigh.\n params: { n: 1, quality: \"xhigh\", imageSize: \"2K\" },\n};\n\nconst GEMINI_IMAGE_BRIEF: ImageBriefRules = {\n // \"Use Natural Language & Full Sentences… as if you were briefing a human artist\", and\n // give the why (prompting-google-gemini-image.md §2). Bench E1 on Gemini 3 Pro Image, 182 paired\n // ads: shippable 6.0% vs 7.1% for the legacy prompt (within the -2pp gate), near-clone 6% vs 15%\n // (BENCH.md#E1; pairwise pending).\n structure: \"flowing_sentences\",\n // Google gives no limit; the synthesis budget holds (research §6).\n wordBudget: 400,\n referenceOrder: REFERENCE_ORDER,\n // Quote each string and name the font per line (gemini §3, Cloud guide per-line pattern).\n text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },\n // \"Use positive framing … 'empty street' instead of 'no cars'\" (gemini §2).\n constraints: \"positive_last\",\n realism: {\n // Capture-device language, never \"hyperrealistic\"/\"8k\" tag soup (gemini §6).\n native: {\n photo: \"a candid photo taken on a phone\",\n texture: \"real texture and wear on every surface, natural light, a little sensor grain, unposed and unretouched\",\n // Pro reasons from purpose: say why it is lo-fi (gemini §2, §6).\n purpose: \"because a polished picture reads as an ad and this one has to read as a real post\",\n },\n produced: { photo: \"a real photograph\", texture: \"real textures and true-to-life light and colour\", purpose: \"\" },\n people:\n \"the people look real and candid, shot on a phone, with natural skin, natural light and relaxed expressions\",\n },\n // No `n` on Gemini image (gemini §0.4); Pro 2K costs the same as 1K and small text blurs\n // at 1K (gemini §0.3).\n params: { n: 1, imageSize: \"2K\" },\n};\n\n// Recraft: \"Quote all text and define the typographic system\", poster order format →\n// background → typography (research §1, Recraft V4). Vector art, so no photo vocabulary.\nconst RECRAFT_BRIEF: ImageBriefRules = {\n structure: \"labelled_sections\",\n wordBudget: 400,\n referenceOrder: REFERENCE_ORDER,\n text: { numberedQuotes: true, nameSymbols: true, keepAccents: true },\n constraints: \"positive_last\",\n realism: {\n native: { photo: \"a flat vector illustration\", texture: \"clean shapes and even fills\", purpose: \"\" },\n produced: { photo: \"a flat vector illustration\", texture: \"clean shapes and even fills\", purpose: \"\" },\n people: \"\",\n },\n params: { n: 1 },\n};\n\nexport const GPT_IMAGE_PROFILE: ImageModelProfile = {\n id: \"gpt-image\",\n constraintPlacement: \"last\",\n photorealCue: true,\n // OpenRouter forwards `quality`; gpt-image already processes inputs at high fidelity\n // automatically, so we deliberately do NOT send `input_fidelity`.\n //\n // No `quality` here any more: it is the one default that depends on WHICH gpt-image is\n // pinned. 2.5 added `xhigh` and `max` above `high`, and the older entries would refuse\n // them at validate — so callers ask for the step they want through\n // `bestSupportedQuality` and get the best the chosen model actually has.\n paramDefaults: {},\n // gpt-image-2 has no `xhigh` (openai §0.2): the brief asks for `high`, its top step.\n brief: { ...GPT_IMAGE_BRIEF, params: { n: 1, quality: \"high\" } },\n};\n\n/** gpt-image-2.5 (Sunburst, and Flare once registered): same prompt rules, the 2.5 quality ladder. */\nexport const GPT_IMAGE_25_PROFILE: ImageModelProfile = {\n ...GPT_IMAGE_PROFILE,\n brief: GPT_IMAGE_BRIEF,\n};\n\nexport const GEMINI_IMAGE_PROFILE: ImageModelProfile = {\n id: \"gemini\",\n constraintPlacement: \"inline\",\n photorealCue: true,\n // NO `quality`. OpenRouter accepts the field on a Gemini image call and returns\n // 200 — but its per-endpoint schema does not list it and the cost is identical\n // with and without, so Google drops it: an inert knob. Worse, the registry\n // (correctly) does not declare it for Gemini, so emitting it fails `baker canvas\n // validate` with `unknown_param` before the call is ever made.\n paramDefaults: {},\n brief: GEMINI_IMAGE_BRIEF,\n};\n\nexport const RECRAFT_IMAGE_PROFILE: ImageModelProfile = {\n id: \"recraft\",\n constraintPlacement: \"inline\",\n photorealCue: false,\n paramDefaults: {},\n brief: RECRAFT_BRIEF,\n};\n\n/** The prompt profile of every curated image model, by exact registry id. */\nexport const IMAGE_PROFILES: Readonly<Record<(typeof IMAGE_GENERATE_MODELS)[number], ImageModelProfile | null>> = {\n \"openai/gpt-image-2.5-sunburst\": GPT_IMAGE_25_PROFILE,\n \"openai/gpt-image-2\": GPT_IMAGE_PROFILE,\n \"openai/gpt-5.4-image-2\": GPT_IMAGE_PROFILE,\n \"google/gemini-3.1-flash-image\": GEMINI_IMAGE_PROFILE,\n \"google/gemini-3.1-flash-lite-image\": GEMINI_IMAGE_PROFILE,\n \"google/gemini-3-pro-image\": GEMINI_IMAGE_PROFILE,\n \"google/gemini-3.1-flash-image-preview\": GEMINI_IMAGE_PROFILE,\n \"google/gemini-3-pro-image-preview\": GEMINI_IMAGE_PROFILE,\n \"recraft/recraft-v4.1-pro-vector\": RECRAFT_IMAGE_PROFILE,\n};\n\n/** The profile for an image model id; undefined = unprofiled or unregistered model. */\nexport function imageProfileFor(modelId: string): ImageModelProfile | undefined {\n return (IMAGE_PROFILES as Readonly<Record<string, ImageModelProfile | null>>)[modelId] ?? undefined;\n}\n","import { bestSupportedQuality, nearestSupportedImageSize, supportsParam } from \"@baker/canvas-contract\";\nimport { imageProfileFor } from \"../../lib/prompt-profiles.ts\";\n\n/**\n * What every scaffolded `image_generate` render asks its model for besides the prompt and\n * the shape: a size tier and a quality step, each snapped to what the model accepts, plus\n * the model's profile defaults.\n *\n * Snapped rather than pinned, because one scaffold writes one canvas for whichever model is\n * chosen: gpt-image has no size knob at all (asking for one is `unknown_param`), Nano Banana\n * 2 Lite renders 1K only, and the older gpt-image entries stop at `high`.\n */\nexport function imageRenderParams(model: string, want: { size: string; quality: string }): Record<string, unknown> {\n const quality = bestSupportedQuality(\"image_generate\", model, want.quality);\n return {\n ...(supportsParam(\"image_generate\", model, \"image_size\")\n ? { image_size: nearestSupportedImageSize(\"image_generate\", model, want.size) }\n : {}),\n ...(imageProfileFor(model)?.paramDefaults ?? {}),\n ...(quality ? { quality } : {}),\n };\n}\n","/**\n * The part of a delivered frame that platform chrome leaves clear, as fractions of it.\n *\n * One owner for statics and video, because the chrome is the same: a caption, a logo or a\n * call to action set under the profile row or the reply bar is not seen, whichever\n * scaffolder drew it.\n *\n * Each field is the fraction lost at that edge, so the clear band runs from `top` to\n * `1 - bottom` of the height (16%–63% on a Reels 9:16). Prompts, crops and review checks read\n * these numbers from here (`staticSafeArea`, `videoSafeArea`, `safeRectPx`) and never restate them.\n */\nimport { AD_PLACEMENTS, type AdPlacement, verticalCover } from \"@baker/canvas-contract\";\n\nexport type SafeArea = { top: number; bottom: number; left: number; right: number };\n\n/**\n * Stories cover the top ~14% (profile, progress bar) and the bottom ~20% (reply bar, CTA\n * sticker), Reels the bottom ~35% (caption, audio line, buttons). Two points of margin past\n * each: a logo set exactly on the 14% line read to the review (and would read to a viewer) as\n * sitting under the profile bar. The sides carry no interface, only the design's margin: 56 of\n * 57 side-edge flags on audited 9:16s (Sept 2026) were lines the viewer sees.\n *\n * The part each interface really draws over (`cover`) is calibrated on 256 hand-labelled 9:16\n * flags (Oct 2026, `check-audit.mjs`): a box ending past 69% of the height, or starting above\n * 8%, was under the interface in 79% / 77% of the audit's calls; the 16% / 63% lines agreed on\n * 52% / 61%. Reels draws its buttons in the outer 8% from 60% of the height down (a line ending\n * 3% from the right edge at half height was seen). The numbers live in the placement registry\n * (`@baker/canvas-contract` `AD_PLACEMENTS`), with every other platform's.\n */\nconst STORIES = placementOf(\"meta_stories\");\nconst REELS = placementOf(\"meta_reels\");\n\nfunction placementOf(id: string): AdPlacement {\n const found = AD_PLACEMENTS.find((p) => p.id === id);\n if (!found) throw new Error(`no placement ${id} in the registry`);\n return found;\n}\n\n/** Reels' like, comment and share buttons: a column down the right side, `width` wide, from `from` of the height down. */\nexport type RightRail = { width: number; from: number };\n\nexport type CoveredArea = { top: number; bottom: number; rail?: RightRail };\n\n/** Reels covers more of the bottom than Stories: its safe area ends this far up. */\nexport const REELS_SAFE_BOTTOM = REELS.safe.bottom;\n\n/** A feed video has no chrome over its frame, only a modest margin from its edges. */\nconst FEED: SafeArea = { top: 0.06, bottom: 0.06, left: 0.07, right: 0.07 };\n\n/**\n * A feed image has no chrome over it either: Meta's Facebook Feed and Instagram Feed image\n * specs (ads guide) give a ratio and a tolerance and no safe zone, and the picture shows\n * whole. So the margin is the design's, not the app's: 4% (43px on a 1080-wide 4:5) keeps\n * words off the edge without crowding the layout. The video feed's 6%/7% flagged 61% of\n * the bench's cached 4:5 reads, when 10% had a word actually at the edge.\n */\nconst FEED_IMAGE: SafeArea = { ...placementOf(\"meta_feed\").safe };\n\n/**\n * The placement whose interface decides a static's safe area: Meta's Reels or Stories, another\n * app's full-screen vertical feed (TikTok, Snapchat, YouTube Shorts), or a feed nothing covers.\n */\nexport type SafeAreaPlacement = \"reels\" | \"stories\" | \"vertical\" | \"feed\";\n\n/**\n * A static's safe area, and the placement it is kept clear of: `covered`, the part that\n * placement's interface really draws over; `rail`, the margin kept around the buttons down the\n * lower right; `surface`, the interface in the viewer's words (\"Reels\", \"Reels and TikTok\").\n */\nexport type StaticSafeArea = SafeArea & {\n placement: SafeAreaPlacement;\n surface: string;\n covered?: CoveredArea;\n rail?: RightRail;\n};\n\n/**\n * Placements that run the ad on every surface of Facebook or Instagram, Reels included:\n * Advantage+ (`automatic`, as the viewer read off an ad set names it) and a platform whose\n * positions the ad set left open (a bare `facebook` or `instagram`, or `meta`).\n */\nconst EVERY_SURFACE: ReadonlySet<string> = new Set([\"automatic\", \"facebook\", \"instagram\", \"meta\"]);\n\n/** The full-screen vertical placements of other apps, by any name they are given, and their name to a viewer. */\nconst OTHER_VERTICAL: ReadonlyArray<{ id: string; names: readonly string[]; surface: string }> = [\n { id: \"tiktok_feed\", names: [\"tiktok\", \"tiktok_feed\"], surface: \"TikTok\" },\n { id: \"snapchat_snap_ads\", names: [\"snapchat\", \"snapchat_snap_ads\", \"snap\"], surface: \"Snapchat\" },\n { id: \"youtube_shorts\", names: [\"youtube\", \"youtube_shorts\", \"shorts\"], surface: \"YouTube Shorts\" },\n];\n\n/** Which of Meta's vertical placements the names reach: Reels (and so Stories) under Advantage+, Stories alone, or none. */\nfunction metaVertical(\n named: readonly string[],\n): { id: string; placement: \"reels\" | \"stories\"; surface: string } | null {\n if (named.length === 0 || named.some((p) => p.includes(\"reels\") || EVERY_SURFACE.has(p))) {\n return { id: REELS.id, placement: \"reels\", surface: \"Reels\" };\n }\n return named.some((p) => p.includes(\"stor\")) ? { id: STORIES.id, placement: \"stories\", surface: \"Stories\" } : null;\n}\n\n/**\n * A static ad's safe area: the one owner of the numbers the render prompt, the crops and the\n * review all state.\n *\n * A 9:16 runs in Reels as well as Stories under Advantage+ placements, so by default it keeps\n * clear of the Stories header and the Reels footer. `placements` are where the ad runs: Meta\n * positions in any of Meta's spellings (targeting `reels`/`story`, insights\n * `instagram_reels`/`facebook_stories`, effective `REELS`/`STORY`), and the other apps' vertical\n * feeds by platform or placement id (`tiktok`, `snapchat`, `youtube_shorts`). None, or an empty\n * list, means Advantage+, and so do `automatic` and a bare `facebook`/`instagram`/`meta`. Meta\n * names only ever narrow the band: Stories without Reels gives the Reels footer back, and\n * neither leaves only the feed margin. Several apps together keep clear of all of them.\n */\nexport function staticSafeArea(format: string, placements?: readonly string[]): StaticSafeArea {\n const feed: StaticSafeArea = { ...FEED_IMAGE, placement: \"feed\", surface: \"the feed\" };\n if (format !== \"9:16\") return feed;\n const named = (placements ?? []).map((p) => p.toLowerCase());\n const meta = metaVertical(named);\n const others = OTHER_VERTICAL.filter((o) => named.some((p) => o.names.includes(p)));\n const cover = verticalCover([...(meta ? [meta.id] : []), ...others.map((o) => o.id)]);\n if (!cover) return feed;\n return {\n ...cover.safe,\n placement: meta?.placement ?? \"vertical\",\n surface: [...(meta ? [meta.surface] : []), ...others.map((o) => o.surface)].join(\" and \"),\n covered: { top: cover.top, bottom: cover.bottom, ...(cover.rail ? { rail: cover.rail } : {}) },\n ...(cover.railMargin ? { rail: cover.railMargin } : {}),\n };\n}\n\n/** A 9:16 video's captions and lower thirds keep 6% off each side, clear of Reels' right-side buttons. */\nconst VIDEO_VERTICAL_SIDE = 0.06;\n\n/**\n * A video ad's safe area. A 9:16 video runs in Reels as well as Stories, so it keeps clear\n * of the Stories header and the Reels footer, the larger of each. Its sides keep the video's\n * own margin: the statics' narrower one was measured on statics.\n */\nexport function videoSafeArea(format: string): SafeArea {\n return format === \"9:16\"\n ? { ...STORIES.safe, bottom: REELS_SAFE_BOTTOM, left: VIDEO_VERTICAL_SIDE, right: VIDEO_VERTICAL_SIDE }\n : FEED;\n}\n\n/** A rectangle in pixels, from the frame's top-left corner. */\nexport type PixelRect = { x: number; y: number; width: number; height: number };\n\n/**\n * The clear part of a `width`×`height` frame, in pixels: where every word, logo and button\n * sits. Rounded inward, so a box inside it is inside the safe area at any size, which is\n * what a crop, a layout and an OCR band check each need to be able to rely on.\n */\nexport function safeRectPx(area: SafeArea, width: number, height: number): PixelRect {\n const x = Math.ceil(atPx(area.left * width));\n const y = Math.ceil(atPx(area.top * height));\n const right = Math.floor(atPx((1 - area.right) * width));\n const bottom = Math.floor(atPx((1 - area.bottom) * height));\n return { x, y, width: right - x, height: bottom - y };\n}\n\n/** Drops floating-point noise (0.93 × 1000 = 929.9999…) before an edge is rounded inward. */\nfunction atPx(value: number): number {\n return Number(value.toFixed(6));\n}\n\n/**\n * The area a 9:16 is held to where it blocks: where it runs in Stories as well as Reels (Advantage+,\n * or both named) that is Stories' header and reply bar (and any other app's interface named), since\n * what only Reels' taller footer covers is still seen in Stories and the review only notes it. A\n * Reels-only ad set has no Stories to be seen in, so Reels' footer blocks. Pure.\n */\nexport function blockingArea(format: string, placements?: readonly string[]): StaticSafeArea {\n return staticSafeArea(format, blockingPlacements(placements));\n}\n\n/**\n * The placements whose interface blocks a line: where the ad runs in Reels and Stories, Stories and\n * the other apps named; otherwise the placements as given.\n */\nexport function blockingPlacements(placements?: readonly string[]): string[] {\n const named = (placements ?? []).map((p) => p.toLowerCase());\n const storiesToo =\n named.length === 0 ||\n named.some((p) => EVERY_SURFACE.has(p)) ||\n (named.some((p) => p.includes(\"reels\")) && named.some((p) => p.includes(\"stor\")));\n if (!storiesToo) return [...(placements ?? [])];\n const others = (placements ?? []).filter((p) => OTHER_VERTICAL.some((o) => o.names.includes(p.toLowerCase())));\n return [...others, \"stories\"];\n}\n\n/** The share of each edge kept clear around the screen, beyond what an interface covers. */\nconst MARGIN = 0.035;\n\n/**\n * Where the screen may sit on one size, as fractions of each edge. A feed size keeps a margin; a\n * 9:16 keeps clear of what its placements really draw where the review blocks (`blockingArea`:\n * Stories' header and reply bar under Advantage+) plus a margin, so a screen is as large as it can be and\n * still never under the interface. Pure.\n */\nexport function nativeLayoutInsets(\n format: string,\n placements?: readonly string[],\n): { top: number; bottom: number; left: number; right: number } {\n const area = blockingArea(format, placements);\n if (!area.covered) {\n return {\n top: Math.max(area.top, MARGIN),\n bottom: Math.max(area.bottom, MARGIN),\n left: Math.max(area.left, MARGIN),\n right: Math.max(area.right, MARGIN),\n };\n }\n const rail = area.covered.rail;\n // A rail covers the right edge only below `from`: a screen whose bottom clears it keeps the edge.\n const bottom = area.covered.bottom + MARGIN;\n const railReached = rail ? 1 - bottom > rail.from : false;\n return {\n top: area.covered.top + MARGIN,\n bottom,\n left: Math.max(area.left, MARGIN),\n right: Math.max(area.right, railReached && rail ? rail.width + MARGIN / 2 : 0, MARGIN),\n };\n}\n","/**\n * What a platform list command prints per row: a compact projection by default,\n * the dotted paths `--fields` names, or the raw record under `--full`. Ad platforms\n * return every field they were asked for on every row, so passing rows through\n * spends the agent's context on fields it rarely reads.\n */\n\nexport function asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\n/** Reads one dotted path (`content.textAd.landingPage`) out of a record. */\nexport function readPath(row: Record<string, unknown>, path: string): unknown {\n let current: unknown = row;\n for (const segment of path.split(\".\")) {\n const record = asRecord(current);\n if (!record) return undefined;\n current = record[segment];\n }\n return current;\n}\n\nexport function projectFields(rows: Array<Record<string, unknown>>, paths: string[]): Array<Record<string, unknown>> {\n return rows.map((row) => {\n const projected: Record<string, unknown> = {};\n for (const path of paths) {\n const value = readPath(row, path);\n if (value !== undefined) {\n projected[path] = value;\n }\n }\n return projected;\n });\n}\n\nexport interface ShapedRows {\n rows: Array<Record<string, unknown>>;\n /**\n * The columns the csv/md writers must use. Those writers derive a header from\n * row 0 when given none, which silently drops any key the first row lacks — so\n * the shaping decision carries its own columns; `--full` carries every key any\n * row has.\n */\n fields?: string[];\n /** A `--fields` path no row carries — named, or it reads as a column that is empty. */\n hints?: string[];\n}\n\nexport interface CompactProjection<Row> {\n row: (row: Row) => Record<string, unknown>;\n columns: readonly string[];\n}\n\n/** `--fields` wins over `--full`, which wins over the compact default. */\nexport function shapeListRows<Row extends object>(\n rows: Row[],\n args: Record<string, unknown>,\n compact: CompactProjection<Row>,\n): ShapedRows {\n const raw = rows as unknown as Array<Record<string, unknown>>;\n const fields = typeof args.fields === \"string\" ? args.fields : undefined;\n if (fields) {\n const paths = fields\n .split(\",\")\n .map((field) => field.trim())\n .filter((field) => field.length > 0);\n if (paths.length > 0) {\n // A column the compact row derives (`created_date`, `creative_id`) is one an agent has\n // seen and will name, so a path the raw record lacks is read off the compact row.\n const projected = projectFields(raw, paths).map((row, i) => {\n const missing = paths.filter((path) => row[path] === undefined);\n const source = rows[i];\n if (missing.length === 0 || source === undefined) return row;\n const compactRow = compact.row(source);\n for (const path of missing) {\n if (compactRow[path] !== undefined) row[path] = compactRow[path];\n }\n return row;\n });\n const unmatched = paths.filter((path) => projected.every((row) => row[path] === undefined));\n if (projected.length === 0 || unmatched.length === 0) return { rows: projected, fields: paths };\n const carried = new Set([...compact.columns, ...raw.flatMap((row) => Object.keys(row))]);\n return {\n rows: projected,\n fields: paths,\n hints: [\n `--fields matched nothing for: ${unmatched.join(\", \")}. The fields these rows carry are: ${[...carried].join(\", \")} (reach nested ones with dots, e.g. targeting.geo_locations).`,\n ],\n };\n }\n }\n if (args.full) {\n // Platforms omit empty fields, so row 0 alone would drop a column a later row carries.\n const columns = new Set<string>();\n for (const row of raw) for (const key of Object.keys(row)) columns.add(key);\n return { rows: raw, fields: [...columns] };\n }\n return { rows: rows.map(compact.row), fields: [...compact.columns] };\n}\n","import { asRecord } from \"./commands/ads/list-rows.ts\";\n\n/**\n * Who will see an ad, read off what the ad set actually targets rather than what\n * anyone hopes it targets — or, with no ad set, off the Knowledge and the brief.\n * `baker ads meta adsets --audience` prints it; a creative records it in its\n * `_definition.md` (`viewer`), and the viewer check plays it.\n */\n\nexport const VIEWER_TEMPERATURE_VALUES = [\"cold\", \"warm\", \"hot\"] as const;\nexport const VIEWER_KNOWS_VALUES = [\"brand\", \"offer\", \"product\"] as const;\nexport const VIEWER_SOURCE_VALUES = [\"targeting\", \"knowledge\"] as const;\n\nexport type ViewerTemperature = (typeof VIEWER_TEMPERATURE_VALUES)[number];\nexport type ViewerKnows = (typeof VIEWER_KNOWS_VALUES)[number];\n\n/**\n * Where the viewer lives and how they are spoken to (D1): the country (`global` for a version\n * that runs in several), the language variant, the currency and the form of address.\n */\nexport interface ViewerMarket {\n country: string;\n language?: string;\n currency?: string;\n address?: string;\n}\n\nexport interface ViewerProfile {\n temperature: ViewerTemperature;\n knows: ViewerKnows[];\n placements: string[];\n source: (typeof VIEWER_SOURCE_VALUES)[number];\n reasons: string[];\n /** Where they live: read off the ad set's countries, else COMPANY.md. */\n market?: ViewerMarket;\n}\n\ntype MarketFacts = {\n name: string;\n names: RegExp;\n language: string;\n languageName: string;\n currency: string;\n address: string;\n};\n\n/** The markets our ads run in, with how an ad speaks there. A country not listed keeps its code alone. */\nconst MARKETS: Readonly<Record<string, MarketFacts>> = {\n ES: {\n name: \"Spain\",\n names: /(?<![\\p{L}\\d])(spain|españa|espana)(?![\\p{L}\\d])/iu,\n language: \"es-ES\",\n languageName: \"Spanish from Spain\",\n currency: \"EUR\",\n address: \"tú\",\n },\n MX: {\n name: \"Mexico\",\n names: /(?<![\\p{L}\\d])(mexico|méxico)(?![\\p{L}\\d])/iu,\n language: \"es-MX\",\n languageName: \"Mexican Spanish\",\n currency: \"MXN\",\n address: \"tú\",\n },\n AR: {\n name: \"Argentina\",\n names: /(?<![\\p{L}\\d])(argentina)(?![\\p{L}\\d])/iu,\n language: \"es-AR\",\n languageName: \"Argentinian Spanish\",\n currency: \"ARS\",\n address: \"vos\",\n },\n CO: {\n name: \"Colombia\",\n names: /(?<![\\p{L}\\d])(colombia)(?![\\p{L}\\d])/iu,\n language: \"es-CO\",\n languageName: \"Colombian Spanish\",\n currency: \"COP\",\n address: \"tú\",\n },\n CL: {\n name: \"Chile\",\n names: /(?<![\\p{L}\\d])(chile)(?![\\p{L}\\d])/iu,\n language: \"es-CL\",\n languageName: \"Chilean Spanish\",\n currency: \"CLP\",\n address: \"tú\",\n },\n PE: {\n name: \"Peru\",\n names: /(?<![\\p{L}\\d])(peru|perú)(?![\\p{L}\\d])/iu,\n language: \"es-PE\",\n languageName: \"Peruvian Spanish\",\n currency: \"PEN\",\n address: \"tú\",\n },\n BR: {\n name: \"Brazil\",\n names: /(?<![\\p{L}\\d])(brazil|brasil)(?![\\p{L}\\d])/iu,\n language: \"pt-BR\",\n languageName: \"Brazilian Portuguese\",\n currency: \"BRL\",\n address: \"você\",\n },\n PT: {\n name: \"Portugal\",\n names: /(?<![\\p{L}\\d])(portugal)(?![\\p{L}\\d])/iu,\n language: \"pt-PT\",\n languageName: \"European Portuguese\",\n currency: \"EUR\",\n address: \"tu\",\n },\n GB: {\n name: \"the United Kingdom\",\n names: /(?<![\\p{L}\\d])(united kingdom|uk|britain|england)(?![\\p{L}\\d])/iu,\n language: \"en-GB\",\n languageName: \"British English\",\n currency: \"GBP\",\n address: \"you\",\n },\n IE: {\n name: \"Ireland\",\n names: /(?<![\\p{L}\\d])(ireland)(?![\\p{L}\\d])/iu,\n language: \"en-IE\",\n languageName: \"Irish English\",\n currency: \"EUR\",\n address: \"you\",\n },\n US: {\n name: \"the United States\",\n names: /(?<![\\p{L}\\d])(united states|usa|u\\.s\\.)(?![\\p{L}\\d])/iu,\n language: \"en-US\",\n languageName: \"American English\",\n currency: \"USD\",\n address: \"you\",\n },\n DE: {\n name: \"Germany\",\n names: /(?<![\\p{L}\\d])(germany|deutschland)(?![\\p{L}\\d])/iu,\n language: \"de-DE\",\n languageName: \"German\",\n currency: \"EUR\",\n address: \"du\",\n },\n FR: {\n name: \"France\",\n names: /(?<![\\p{L}\\d])(france)(?![\\p{L}\\d])/iu,\n language: \"fr-FR\",\n languageName: \"French\",\n currency: \"EUR\",\n address: \"vous\",\n },\n IT: {\n name: \"Italy\",\n names: /(?<![\\p{L}\\d])(italy|italia)(?![\\p{L}\\d])/iu,\n language: \"it-IT\",\n languageName: \"Italian\",\n currency: \"EUR\",\n address: \"tu\",\n },\n};\n\n/**\n * The market an ad set's countries make: one country is its own market; several are one global\n * version in the campaign's language, with market-neutral people, places and money. Pure.\n */\nexport function marketOfCountries(countries: readonly string[]): ViewerMarket | undefined {\n const codes = [...new Set(countries.map((c) => c.trim().toUpperCase()).filter(Boolean))];\n if (codes.length === 0) return undefined;\n if (codes.length > 1) return { country: \"global\" };\n const code = codes[0] as string;\n const facts = MARKETS[code];\n return facts\n ? { country: code, language: facts.language, currency: facts.currency, address: facts.address }\n : { country: code };\n}\n\n/** The market COMPANY.md names on its market or geography line, when it names one. Pure. */\nexport function marketFromCompanyDoc(doc: string | null | undefined): ViewerMarket | undefined {\n const line = (doc ?? \"\")\n .split(\"\\n\")\n .find((l) =>\n /(?<![\\p{L}\\d])(markets?|geo|geograph\\w*|country|countries|país|paises|países|mercados?)(?![\\p{L}\\d])/iu.test(l),\n );\n if (!line) return undefined;\n const found = Object.entries(MARKETS)\n .filter(([, facts]) => facts.names.test(line))\n .map(([code]) => code);\n return marketOfCountries(found);\n}\n\n/** The market in the viewer's own terms, for a model that plays them. Pure. */\nexport function describeMarket(market: ViewerMarket): string {\n if (market.country === \"global\") return \"a person in any of the countries the ad runs in\";\n const facts = MARKETS[market.country];\n const where = facts?.name ?? market.country;\n const language = facts && market.language === facts.language ? facts.languageName : market.language;\n return [\n `a person living in ${where}`,\n language ? ` who reads ${language} natively` : \"\",\n market.currency ? `, pays in ${market.currency}` : \"\",\n market.address ? ` and is spoken to as ${market.address}` : \"\",\n ].join(\"\");\n}\n\n/** The country a market names, in words (\"Mexico\"); null for a global version. */\nexport function marketCountryName(market: ViewerMarket): string | null {\n return market.country === \"global\" ? null : (MARKETS[market.country]?.name ?? market.country);\n}\n\nexport interface AdSetTargetingInput {\n targeting?: unknown;\n}\n\nexport interface CustomAudienceInput {\n id?: unknown;\n name?: unknown;\n subtype?: unknown;\n}\n\nexport interface CampaignInput {\n objective?: unknown;\n}\n\nfunction text(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\n/** The `{ id, name }` entries of a targeting list such as `custom_audiences`. */\nfunction audienceRefs(value: unknown): Array<{ id: string; name: string }> {\n if (!Array.isArray(value)) return [];\n return value.flatMap((entry) => {\n const ref = asRecord(entry);\n const id = text(ref?.id);\n return id ? [{ id, name: text(ref?.name) }] : [];\n });\n}\n\n/** What one custom audience's members have already been through with the brand. */\ninterface AudienceTier {\n temperature: ViewerTemperature;\n knows: ViewerKnows[];\n}\n\ninterface AudienceRead extends AudienceTier {\n /** Who its members are, in the words a reason prints. */\n who: string;\n}\n\n/**\n * A pixel or event audience's subtype says where its members came from, not what they did:\n * \"Purchasers 180d\" and \"All visitors 30d\" are both WEBSITE. The name is the one place an\n * account records the event, in the advertiser's own language.\n */\nconst BUYERS = /purchas|buyer|customer|client|compra|cliente|achat|acheteur|kunde|käufer/i;\nconst LOOKALIKE_NAME = /lookalike|\\blal\\b|similar|parecid/i;\n\nconst COLD: AudienceTier = { temperature: \"cold\", knows: [] };\n\nfunction tierOf(audience: CustomAudienceInput | undefined, ref: { name: string }): AudienceRead {\n const subtype = text(audience?.subtype).toUpperCase();\n const name = text(audience?.name) || ref.name;\n // Meta names a lookalike after its seed (\"Lookalike (GB, 1%) - Purchasers\"), so it is ruled out before the name is read.\n if (subtype === \"LOOKALIKE\" || (!subtype && LOOKALIKE_NAME.test(name))) {\n return { ...COLD, who: \"a lookalike: strangers who resemble the seed\" };\n }\n if (BUYERS.test(name)) return { temperature: \"hot\", knows: [\"brand\", \"offer\", \"product\"], who: \"buyers\" };\n if (subtype === \"WEBSITE\") return { temperature: \"warm\", knows: [\"brand\", \"offer\"], who: \"site visitors\" };\n if (subtype === \"CUSTOM\") {\n return { temperature: \"hot\", knows: [\"brand\", \"offer\", \"product\"], who: \"an uploaded customer list\" };\n }\n // Every other custom audience is people who met the brand somewhere: a Page, a profile, a video, the app.\n return { temperature: \"warm\", knows: [\"brand\"], who: \"people who engaged with the brand\" };\n}\n\nexport function classifyAudience(\n adSet: AdSetTargetingInput,\n audiences: readonly CustomAudienceInput[],\n campaign: CampaignInput | undefined,\n): ViewerProfile {\n const targeting = asRecord(adSet.targeting) ?? {};\n const byId = new Map(audiences.map((audience) => [text(audience.id), audience]));\n const includedRefs = audienceRefs(targeting.custom_audiences);\n const included = includedRefs.map((ref) => tierOf(byId.get(ref.id), ref));\n const reasons: string[] = [\n includedRefs.length === 0\n ? \"No custom audience: Meta finds new people from age, place and interests, so every viewer is a stranger.\"\n : `Reaches ${includedRefs.map((ref, i) => `\"${ref.name || ref.id}\" (${included[i]?.who})`).join(\", \")}.`,\n ];\n const unlisted = includedRefs.filter((ref) => !byId.has(ref.id));\n if (unlisted.length > 0) {\n reasons.push(`${quoteNames(unlisted)} is not in this account's audience list, so it was read from its name alone.`);\n }\n let reached = coldestOf(included);\n if (asRecord(targeting.targeting_automation)?.advantage_audience === 1 && included.length > 0) {\n reached = belowHot(reached);\n reasons.push(\n \"Advantage+ audience treats the chosen audiences as suggestions and reaches past them, so part of the delivery goes to people who have never bought.\",\n );\n }\n const excludedRefs = audienceRefs(targeting.excluded_custom_audiences);\n if (excludedRefs.length > 0) reasons.push(`Keeps out ${quoteNames(excludedRefs)}.`);\n if (excludedRefs.some((ref) => tierOf(byId.get(ref.id), ref).temperature === \"hot\")) {\n reached = belowHot(reached);\n reasons.push(\"Buyers are excluded, so nobody reached has used the product.\");\n }\n const objective = text(campaign?.objective);\n if (reached.temperature === \"cold\" && ASKS_TO_ACT.test(objective)) {\n reasons.push(\n `Campaign objective ${objective} asks a stranger to act on first contact, so the ad alone must say what is offered and to whom.`,\n );\n }\n const countries = strings(asRecord(targeting.geo_locations)?.countries);\n const market = marketOfCountries(countries);\n if (market?.country === \"global\") {\n reasons.push(\n `Runs in ${countries.join(\", \")}: one version for every market, with no country's places, money or faces.`,\n );\n }\n const placements = placementsOf(targeting);\n if (placements.length === 0) {\n reasons.push(\n \"Advantage+ placements: Meta picks the surface, so the ad must also work full-screen in Stories and Reels, not only in the feed.\",\n );\n }\n return {\n temperature: reached.temperature,\n knows: reached.knows,\n placements: placements.length > 0 ? placements : [\"automatic\"],\n source: \"targeting\",\n reasons,\n ...(market ? { market } : {}),\n };\n}\n\n/**\n * The objective says what the campaign wants from a viewer, never who the viewer is:\n * a conversion campaign on broad targeting still meets strangers. So it adds a reason\n * and never moves the temperature.\n */\nconst ASKS_TO_ACT =\n /^(OUTCOME_SALES|OUTCOME_LEADS|OUTCOME_APP_PROMOTION|CONVERSIONS|LEAD_GENERATION|PRODUCT_CATALOG_SALES)$/;\n\nfunction strings(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === \"string\") : [];\n}\n\n/**\n * `instagram_positions` calls the feed `stream` and Facebook's Reels are already\n * `facebook_reels`; one `<platform>_<surface>` spelling reads the same for both.\n */\nfunction placementsOf(targeting: Record<string, unknown>): string[] {\n return strings(targeting.publisher_platforms).flatMap((platform) => {\n const positions = strings(targeting[`${platform}_positions`]);\n // A platform with no positions of its own is every surface on it.\n if (positions.length === 0) return [platform];\n return positions.map((position) => {\n const surface = platform === \"instagram\" && position === \"stream\" ? \"feed\" : position;\n return surface.startsWith(`${platform}_`) ? surface : `${platform}_${surface}`;\n });\n });\n}\n\nfunction quoteNames(refs: ReadonlyArray<{ id: string; name: string }>): string {\n return refs.map((ref) => `\"${ref.name || ref.id}\"`).join(\", \");\n}\n\n/** Only buyers know the product itself; anyone short of that knows at most the brand and its offer. */\nfunction belowHot(tier: AudienceTier): AudienceTier {\n if (tier.temperature !== \"hot\") return tier;\n return { temperature: \"warm\", knows: tier.knows.filter((known) => known !== \"product\") };\n}\n\nconst WARMTH: Record<ViewerTemperature, number> = { cold: 0, warm: 1, hot: 2 };\n\n/**\n * Meta delivers to anyone in ANY included audience, so the ad has to work on the\n * coldest of them, and it can only lean on what every one of them already knows.\n */\nfunction coldestOf(tiers: readonly AudienceTier[]): AudienceTier {\n const [first, ...rest] = tiers;\n if (!first) return COLD;\n return rest.reduce<AudienceTier>(\n (acc, tier) => ({\n temperature: WARMTH[tier.temperature] < WARMTH[acc.temperature] ? tier.temperature : acc.temperature,\n knows: acc.knows.filter((known) => tier.knows.includes(known)),\n }),\n first,\n );\n}\n\n/** The awareness stages a creative's `awareness` field takes (the scaffold `creatives` collection). */\nexport const AWARENESS_VALUES = [\"unaware\", \"problem_aware\", \"solution_aware\", \"product_aware\", \"most_aware\"] as const;\n\nexport type Awareness = (typeof AWARENESS_VALUES)[number];\n\n/** Until a viewer is aware of the product, they know nothing of the brand behind it either. */\nconst BY_AWARENESS: Record<Awareness, AudienceTier> = {\n unaware: COLD,\n problem_aware: COLD,\n solution_aware: COLD,\n product_aware: { temperature: \"warm\", knows: [\"brand\", \"offer\"] },\n most_aware: { temperature: \"hot\", knows: [\"brand\", \"offer\", \"product\"] },\n};\n\n/**\n * A persona segment says what the viewer already did with the brand only when it is\n * defined by it (\"Existing customers up for renewal\", \"Leads who went quiet\"). Phrases,\n * not words: \"Marketing leads at SaaS firms\" are job titles, not leads.\n */\nconst CUSTOMER_SEGMENT =\n /\\b(existing|current|past|former|returning|repeat)\\s+(customers?|clients?|buyers?|members?|subscribers?|users?)\\b|\\bclientes\\s+(actuales|existentes)\\b|\\bantiguos\\s+clientes\\b/i;\nconst CONTACT_SEGMENT =\n /\\b(leads?|prospects?)\\s+(who|that)\\b|\\b(site|website|web)\\s+visitors\\b|\\babandon(ed|s)?\\s+(carts?|checkouts?|forms?)\\b|\\btrial\\s+users\\b|\\bvisitantes\\b/i;\n\nfunction tierOfSegment(segment: string): AudienceTier | undefined {\n if (CUSTOMER_SEGMENT.test(segment)) return { temperature: \"hot\", knows: [\"brand\", \"offer\", \"product\"] };\n if (CONTACT_SEGMENT.test(segment)) return { temperature: \"warm\", knows: [\"brand\", \"offer\"] };\n return undefined;\n}\n\nexport interface KnowledgeViewerInput {\n /** The segment line (frontmatter `segment`, else the title) of each persona the ad is for. */\n personas: readonly string[];\n /** The awareness stage the concept was written for, when the brief set one. */\n awareness?: Awareness;\n}\n\n/**\n * The viewer when there is no ad set to read. The Knowledge describes who a persona\n * is, almost never what they already know about the brand, so the stranger is the\n * default and only an explicit signal warms it.\n */\nexport function viewerFromKnowledge(input: KnowledgeViewerInput): ViewerProfile {\n const reasons = [\"No ad set to read, so this viewer comes from the Knowledge and the brief, not from targeting.\"];\n let tier = COLD;\n if (input.awareness) {\n tier = BY_AWARENESS[input.awareness];\n reasons.push(`The concept is written for a ${input.awareness.replace(\"_\", \"-\")} viewer.`);\n } else {\n const warmed = input.personas.flatMap((segment) => {\n const read = tierOfSegment(segment);\n return read ? [{ segment, read }] : [];\n });\n if (warmed.length > 0 && warmed.length === input.personas.length) {\n tier = coldestOf(warmed.map((entry) => entry.read));\n reasons.push(\n `The persona is defined by what they already did with the brand: ${warmed.map((entry) => `\"${entry.segment}\"`).join(\", \")}.`,\n );\n } else {\n reasons.push(\"Nothing on file says the viewer knows the brand: design for a stranger.\");\n }\n }\n return { temperature: tier.temperature, knows: tier.knows, placements: [\"automatic\"], source: \"knowledge\", reasons };\n}\n","/**\n * The static ad spec: what the agent writes in a static ad's `prompt.json`, and the one\n * shape every reader of that file goes through.\n *\n * The spec says what the ad is, never how to prompt a model for it: who will see it, the\n * one thing it tells them, each line of copy and whether it is lettered on something in the\n * scene or set as designed type, the scene and its mood, the brand, the pictures it grounds\n * on (each with one role), and an optional direction note. `static-brief.ts` compiles it\n * into the prompt each model is sent, so the model's rules live in one place and the agent\n * never writes a model prompt.\n *\n * A `prompt.json` written before the spec is the describe pass's blueprint of the source\n * ad, rewritten by the agent. `readAdSpec` reads it through `adSpecFromBlueprint`, a pure\n * adapter, so existing creatives render from the same compiler without a migration.\n */\nimport { z } from \"zod\";\nimport type { ConceptClass } from \"../lib/image-model-router.ts\";\nimport { sanitizeId } from \"./lib/elements.ts\";\nimport { NativeSurfaceSchema } from \"./native-surface.ts\";\nimport { conceptHasLogo, isInScene, winningMechanisms } from \"./static-copy.ts\";\n\n/**\n * The `schema` a `prompt.json` written as an ad spec carries: /3 adds the concept the ad is built\n * on, the market, how the ad shows the brand and how a stranger recognises it; /2 still reads.\n * Anything else is an older blueprint.\n */\nexport const AD_SPEC_SCHEMA = \"baker-static-ad/3\";\nexport const AD_SPEC_SCHEMAS = [\"baker-static-ad/2\", AD_SPEC_SCHEMA] as const;\n\nconst Hex = z\n .string()\n .regex(/^#[0-9a-fA-F]{6}$/, \"a colour is a 6-digit hex like #123551\")\n .transform((h) => h.toLowerCase());\n\n/** One line of copy. `in_scene` is lettered on something in the picture; `designed` is set in the brand's face. */\nconst AdCopyLine = z\n .object({\n text: z.string().trim().min(1),\n lettering: z.enum([\"in_scene\", \"designed\"]),\n /** headline | body | cta | caption | fine_print | label. */\n role: z.string().optional(),\n /** In-scene lines: the object it is written on (a whiteboard, a phone's notes app, a sticky note). */\n on: z.string().optional(),\n /** How it looks: an in-scene line's own lettering (chunky navy marker), a designed line's size or treatment. */\n look: z.string().optional(),\n /** Where it sits, by what is around it (\"top of the board\", \"under the headline\"). */\n position: z.string().optional(),\n })\n .strict();\nexport type AdCopyLine = z.infer<typeof AdCopyLine>;\n\n/** The role a picture plays: whose identity it fixes, which product it shows, or a prop in the scene. */\nexport const AD_REFERENCE_ROLES = [\"identity\", \"product\", \"prop\"] as const;\nexport type AdReferenceRole = (typeof AD_REFERENCE_ROLES)[number];\n\nconst AdReference = z\n .object({\n /** A role-based label, never a brand's: PERSON_1, ANIMAL_1, PRODUCT_1, BADGE_1, PROP_1. */\n id: z\n .string()\n .regex(\n /^(PERSON|ANIMAL|PRODUCT|BADGE|PROP)_\\d+$/,\n \"a reference id is PERSON_1, ANIMAL_1, PRODUCT_1, BADGE_1 or PROP_1\",\n ),\n role: z.enum(AD_REFERENCE_ROLES),\n /** The canvas ingest node that holds its picture (bookkeeping, never sent to a model). */\n slot: z.string().optional(),\n /** How it appears in this ad. */\n what: z.string().trim().min(1),\n /** What must stay exactly as in the picture, when more than the default for its role. */\n keep: z.string().optional(),\n /** The company avatar cast in this role (`baker avatars list`): a licensed face, never an invented one. */\n avatar: z.string().optional(),\n /**\n * A picture of ours that shows it (a path from the creative's folder, or a URL): every run\n * wires it into the renders (`ref_<id>`), so a picture is named here, never hand-wired.\n */\n image: z.string().trim().min(1).optional(),\n })\n .strict();\nexport type AdReference = z.infer<typeof AdReference>;\n\n/**\n * Where the viewer lives and how they are spoken to: the language variant (es-ES, es-MX, pt-BR,\n * en-GB…), the currency and the form of address (tú, usted, vos). Read off the ad set's countries,\n * else COMPANY.md (`scaffold-viewer.ts`). One per creative: a campaign that runs in several\n * countries gets one global version with market-neutral people, places and money.\n */\nconst Market = z\n .object({\n /** ISO country code (ES, MX), or \"global\" for a version that runs in several. */\n country: z.string().trim().min(2),\n language: z.string().trim().min(2).optional(),\n currency: z.string().trim().min(1).optional(),\n /** How the copy addresses the viewer: tú, usted, vos, você, du, Sie, vous… */\n address: z.string().trim().min(1).optional(),\n })\n .strict();\nexport type AdMarket = z.infer<typeof Market>;\n\nconst Viewer = z\n .object({\n /** Who will see the ad, in the words a marketer would use. */\n who: z.string().trim().min(1),\n market: Market.optional(),\n /** What they already know about the brand and the offer. */\n knows: z.string().optional(),\n temperature: z.enum([\"cold\", \"warm\", \"hot\"]).optional(),\n /** Where they meet it (feed, Stories, Reels). */\n placement: z.string().optional(),\n })\n .strict();\nexport type AdViewer = z.infer<typeof Viewer>;\n\nconst Scene = z\n .object({\n description: z.string().trim().min(1),\n /** A photograph, a designed graphic, or an illustration. */\n look: z.enum([\"photo\", \"designed\", \"illustration\"]),\n /** How a photo was taken, in plain words (handheld at standing height, window light, faint glare). */\n capture: z.string().optional(),\n mood: z.string().optional(),\n /** How the frame is arranged, by what fills each part of it. */\n layout: z.string().optional(),\n })\n .strict();\nexport type AdScene = z.infer<typeof Scene>;\n\nconst Brand = z\n .object({\n name: z.string().optional(),\n /** The words the brand's logo prints (its wordmark), so a check reads them as the logo, never as copy. */\n lockup: z.array(z.string().trim().min(1)).optional(),\n /** The brand's face in words, when no specimen of it is wired (\"a heavy geometric sans\"). */\n typeface: z.string().optional(),\n ground: Hex.optional(),\n ink: Hex.optional(),\n accent: Hex.optional(),\n cta: Hex.optional(),\n palette: z.array(Hex).default([]),\n })\n .strict();\nexport type AdBrand = z.infer<typeof Brand>;\n\n/** A colour tied to the object that carries it. A hex outside the brand palette never reaches a model. */\nconst ColorUse = z\n .object({ object: z.string().trim().min(1), name: z.string().trim().min(1), hex: Hex.optional() })\n .strict();\nexport type AdColorUse = z.infer<typeof ColorUse>;\n\nconst Mechanism = z.object({ device: z.string().trim().min(1), why_it_works: z.string().optional() }).strict();\n\n/**\n * Another brand on the ad: the one the offer is about (the car brand a dealer's loan is for, a\n * partner, the platform an app runs on), the review platform a rating comes from, or a category\n * signal: its logo may appear, exactly as that brand draws it. Never the source ad's advertiser\n * or a competitor.\n */\nconst ThirdPartyBrand = z\n .object({\n name: z.string().trim().min(1),\n /** Where it shows in this ad, by what carries it (\"the car the loan is for\"). */\n what: z.string().optional(),\n /** The reference (BADGE_1) that holds a picture of its logo, when one is wired. */\n logo: z\n .string()\n .regex(/^(PERSON|ANIMAL|PRODUCT|BADGE|PROP)_\\d+$/, \"a logo is a reference id like BADGE_1\")\n .optional(),\n /**\n * Why its logo is on our ad: `partner` (the offer is about it), `proof` (the review platform a\n * rating comes from), `category_signal` (how a stranger knows in a second what this is: the\n * Instagram and TikTok marks on a social media tool's ad).\n */\n role: z.enum([\"partner\", \"proof\", \"category_signal\"]).optional(),\n })\n .strict();\nexport type AdThirdPartyBrand = z.infer<typeof ThirdPartyBrand>;\n\n/** One thing the reference wins on, what it takes, and what we put in its place. */\nconst LoadBearing = z\n .object({\n /** The element, in a few words (\"the free gift\", \"a peer's face\", \"the notes app\"). */\n element: z.string().trim().min(1),\n /** What it takes to carry it (\"something the viewer wants for free\"). */\n needs: z.string().optional(),\n /** Whether we have it as it is, can adapt it, or have nothing for it. */\n have: z.enum([\"have\", \"adapt\", \"missing\"]),\n /** What of ours carries it in this ad. */\n ours: z.string().optional(),\n })\n .strict();\nexport type AdLoadBearing = z.infer<typeof LoadBearing>;\n\n/**\n * Why the reference won and what of it this ad keeps (`baker winning-ads fit` seeds it). Audited\n * Sept 2026: of 33 creatives built from winning ads, 12 lost what made the reference win. The\n * layers of why it wins: the disguise it wears, the voice it speaks in, the trigger it pulls, what\n * stops the scroll, the logic that convinces.\n */\nconst Concept = z\n .object({\n reference_ad_id: z.string().optional(),\n why_it_wins: z\n .object({\n disguise: z.string().optional(),\n voice: z.string().optional(),\n trigger: z.string().optional(),\n attention: z.string().optional(),\n logic: z.string().optional(),\n })\n .strict()\n .default({}),\n load_bearing: z.array(LoadBearing).default([]),\n verdict: z.enum([\"use\", \"adapt\", \"reject\"]),\n /** How the idea transfers to us, in a sentence. Bookkeeping: never compiled into a render. */\n transfer_note: z.string().optional(),\n })\n .strict();\nexport type AdConcept = z.infer<typeof Concept>;\n\nexport const AdSpecSchema = z\n .object({\n schema: z.enum(AD_SPEC_SCHEMAS),\n viewer: Viewer.optional(),\n /** The one thing the ad tells the viewer. Intent, never printed. */\n message: z.string().optional(),\n copy: z.array(AdCopyLine).default([]),\n scene: Scene,\n brand: Brand.optional(),\n colors: z.array(ColorUse).default([]),\n references: z.array(AdReference).default([]),\n /** The ad shows the brand's logo (the real file, wired on the canvas). */\n logo: z.boolean().default(false),\n logo_position: z.string().optional(),\n /** Other brands the offer is about, whose logos the ad may show. */\n third_party_brands: z.array(ThirdPartyBrand).default([]),\n /**\n * Words the scene prints by itself, never copy: a phone's status bar and app chrome, a code\n * editor's line numbers, a product's own label, a platform's badge. The render may draw them\n * and the review never calls them extra words.\n */\n scene_text: z.array(z.string().trim().min(1)).default([]),\n /** The formats the ad is made for; the canvas delivers them. */\n formats: z.array(z.string()).optional(),\n /** An optional note on direction for this concept. */\n direction: z.string().optional(),\n /** The device the ad is built on, and why it works on a stranger. Judged, never drawn. */\n mechanism: z.array(Mechanism).default([]),\n /** Why the reference won and what this ad keeps of it (schema /3). */\n concept: Concept.optional(),\n /**\n * How the ad shows the brand, decided by the concept: `native` (a disguise: a note, a post, a\n * chat) carries the logo as the sender or avatar and no big button; `standard` the logo large\n * and a solid button. Unset: standard.\n */\n brand_presence: z.enum([\"native\", \"standard\"]).optional(),\n /** What kind of product a stranger takes it for in one second, never its format (\"a trade course\", \"a payroll app\"). */\n recognition: z.string().optional(),\n /**\n * The ad is a screen people know (a post on X, a WhatsApp chat, a wall of Trustpilot reviews, a\n * note, a Slack message), drawn exactly from HTML at every size (`native-surface.ts`); the image\n * model renders only the photos it holds. For a native disguise, never a painted imitation.\n */\n native: NativeSurfaceSchema.optional(),\n // Bookkeeping: read by the agent and the judge's context, never compiled into a render.\n source: z.unknown().optional(),\n reference: z.unknown().optional(),\n notes: z.unknown().optional(),\n })\n .strict();\nexport type AdSpec = z.infer<typeof AdSpecSchema>;\n\n/** The canvas node a reference's own `image` is wired as (`syncStaticAdReferences`). */\nexport function referenceNodeId(reference: Pick<AdReference, \"id\">): string {\n return `ref_${reference.id.toLowerCase()}`;\n}\n\n/** Whether `raw` is a `prompt.json` written as an ad spec (rather than an older blueprint). */\nexport function isAdSpec(raw: unknown): boolean {\n const schema = raw && typeof raw === \"object\" ? (raw as { schema?: unknown }).schema : undefined;\n return (AD_SPEC_SCHEMAS as readonly unknown[]).includes(schema);\n}\n\n/**\n * What the ad keeps of the reference it is built on, in words every reader states the same way:\n * each load-bearing element with what of ours carries it (\"the gift (two accounts free\n * forever)\"), and the idea in its layers. Null when the spec keeps no concept. Pure.\n */\nexport function conceptSummary(spec: Pick<AdSpec, \"concept\">): { mustKeep: string[]; idea?: string } | null {\n const concept = spec.concept;\n if (!concept || concept.verdict === \"reject\") return null;\n // What of ours carries each element: the reference's own form (\"held in hands\") is not ours to keep.\n const mustKeep = concept.load_bearing.map((l) =>\n l.ours?.trim() ? `${l.ours.trim()} (the role ${l.element} plays)` : l.element,\n );\n const { disguise, voice } = concept.why_it_wins;\n const idea = [disguise?.trim(), voice?.trim() ? `in the voice of ${voice.trim()}` : \"\"].filter(Boolean).join(\", \");\n return { mustKeep, ...(idea ? { idea } : {}) };\n}\n\n/**\n * What stops a spec from rendering, in the agent's words: a reference the fit check rejected,\n * and a load-bearing element we have nothing for and named nothing in its place. Every problem is\n * a reason to pick another reference, not to render a weaker copy. Pure.\n */\nexport function conceptProblems(spec: Pick<AdSpec, \"concept\">): string[] {\n const concept = spec.concept;\n if (!concept) return [];\n const problems: string[] = [];\n if (concept.verdict === \"reject\") {\n problems.push(\n \"The reference was judged not to fit this company (`concept.verdict: reject`): what it wins on is something we cannot supply. Do not stop or ask: check the next candidates from your search (`baker winning-ads fit <adId>`, about three) and scaffold this concept on the first that fits.\",\n );\n }\n for (const element of concept.load_bearing) {\n if (element.have !== \"missing\" || element.ours?.trim()) continue;\n problems.push(\n `\"${element.element}\" is what the reference wins on and we have nothing for it: name what of ours carries it (\\`ours\\`), or pick another reference.`,\n );\n }\n return problems;\n}\n\nconst PLACEHOLDER = \"[TODO\";\n\n/**\n * What of the ad is still a placeholder: a spec started from an idea (`baker creatives make`\n * with no reference) leaves the scene and the copy for the agent to write. Pure.\n */\nexport function unwrittenProblems(spec: Pick<AdSpec, \"scene\" | \"copy\">): string[] {\n const problems: string[] = [];\n if (spec.scene.description.includes(PLACEHOLDER)) {\n problems.push(\"The scene is still a placeholder: write what the viewer sees in `scene.description`.\");\n }\n const lines = spec.copy.filter((line) => line.text.includes(PLACEHOLDER));\n if (lines.length > 0) {\n problems.push(`${lines.length} line(s) of copy are still placeholders: write each as it prints, or drop it.`);\n }\n return problems;\n}\n\n/**\n * The ad spec a `prompt.json` holds, whichever shape it was written in. An ad spec is\n * validated (a typo throws with the path, before anything bills); an older blueprint is read\n * through `adSpecFromBlueprint`, naming the source advertiser the library recorded so it is\n * scrubbed from anything compiled.\n */\nexport function readAdSpec(raw: unknown): AdSpec {\n if (isAdSpec(raw)) return AdSpecSchema.parse(raw);\n const advertiser = (raw as { reference?: { advertiser?: unknown } } | null)?.reference?.advertiser;\n return adSpecFromBlueprint(raw, { scrub: typeof advertiser === \"string\" ? [advertiser] : [] });\n}\n\n/** Lettering drawn by hand, in words: handwritten, hand-lettered, scrawled, chalked, in marker. */\nexport const HAND_LETTERED =\n /\\b(?:hand[- ]?(?:written|writing|lettered|lettering|drawn)|handwrit\\w*|scrawl\\w*|scribbl\\w*|markers?|felt[- ]?(?:tip|pen)s?|chalked|(?:written|drawn|lettered|painted) by hand)\\b/i;\n\n/**\n * Lettering written by hand, by the way it is drawn or the surface it is on: post-its,\n * whiteboards, chalkboards, notepads, marker, chalk, hand-lettering.\n */\nexport const HANDWRITING = new RegExp(\n `${HAND_LETTERED.source}|\\\\b(?:chalk\\\\w*|whiteboards?|blackboards?|sticky[- ]notes?|post[- ]?its?|notepads?|notebooks?|napkins?)\\\\b`,\n \"i\",\n);\n\n/**\n * In-scene copy that is set as type instead: written by hand (its surface, its look or where it\n * sits says so), or lettered on no surface the spec names, so not on real packaging or signage.\n */\nconst handwritten = (line: AdCopyLine): boolean =>\n !line.on?.trim() || HANDWRITING.test(`${line.on} ${line.look ?? \"\"} ${line.position ?? \"\"}`);\n\n/**\n * The spec as it is rendered: handwritten copy (post-its, whiteboards, chalkboards,\n * hand-lettering) is set as designed type over the surface it was written on. The user rated\n * in-scene handwriting \"so bad, none of them work\" (E6 gallery, BENCH.md § \"E1 v2\") and again\n * \"very odd\" on post-its and boards (§ \"Fix round\"). A line the spec letters on a named surface of\n * real packaging, signage or a screen stays in the scene, whatever its length: set as type, a product's label\n * came back redrawn (§ \"Final\"). Pure.\n */\nexport function withRenderedLettering(spec: AdSpec): AdSpec {\n const designed = (line: AdCopyLine) => line.lettering === \"in_scene\" && handwritten(line);\n if (!spec.copy.some(designed)) return spec;\n const copy = spec.copy.map((line): AdCopyLine => {\n if (!designed(line)) return line;\n const { on, look: _look, position, ...rest } = line;\n const over = position ?? (on ? `over ${on}` : undefined);\n return { ...rest, lettering: \"designed\", ...(over ? { position: over } : {}) };\n });\n return { ...spec, copy };\n}\n\n/** What kind of picture the concept is, as the model router reads it (`image-model-router.ts`). */\nexport function conceptClassOf(spec: Pick<AdSpec, \"scene\" | \"references\">): ConceptClass {\n if (spec.scene.look === \"photo\") {\n return spec.references.some((r) => r.id.startsWith(\"PERSON_\")) ? \"photo_people\" : \"photo\";\n }\n return spec.scene.look;\n}\n\n// --- Plain text: what survives from a free-text field into a brief. ---\n\n/** Numbers a model cannot obey and a prompt should not pretend it can: percentages, lens and exposure figures. */\nconst PSEUDO_PRECISE =\n /\\d\\s*%|\\b\\d+(?:\\.\\d+)?\\s?mm\\b|\\bf\\/\\s?\\d|\\bISO\\s?\\d|\\b1\\/\\d+\\s?s\\b|\\bfocal length\\b|\\baperture\\b/i;\nconst HEX_TOKEN = /\\s*#[0-9a-fA-F]{6}\\b|\\s*#[0-9a-fA-F]{3}\\b/g;\n\n/**\n * A free-text field as a brief may carry it: hex codes out (a colour reaches the model only\n * tied to an object and from the brand palette), every clause carrying a percentage or a\n * lens/exposure figure dropped (a model has no coordinates and no lens, and a percentage\n * would fight the one safe-area owner), and the named advertisers removed.\n */\nexport function plainText(text: unknown, scrub: readonly string[] = []): string {\n if (typeof text !== \"string\") return \"\";\n let out = text.replace(HEX_TOKEN, \"\");\n for (const name of scrub) {\n const bare = name.replace(/[®™©]/g, \"\").trim();\n if (bare.length < 2) continue;\n out = out.replace(new RegExp(`\\\\b${bare.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b\\\\s*[®™©]?`, \"gi\"), \"\");\n }\n const clauses = out\n .split(/(?<=[,;])|\\(|\\)/)\n .map((c) => c.trim())\n .filter((c) => c && !PSEUDO_PRECISE.test(c));\n return clauses\n .join(\" \")\n .replace(/\\s+([,;.])/g, \"$1\")\n .replace(/[,;]\\s*$/, \"\")\n .replace(/\\s{2,}/g, \" \")\n .trim();\n}\n\n// --- The adapter: an older blueprint read as an ad spec. ---\n\ntype Entry = Record<string, unknown>;\n\nconst str = (v: unknown): string | undefined => (typeof v === \"string\" && v.trim() ? v.trim() : undefined);\nconst entries = (v: unknown): Entry[] =>\n Array.isArray(v) ? v.filter((e): e is Entry => Boolean(e) && typeof e === \"object\") : [];\nconst hexOrUndefined = (v: unknown): string | undefined => {\n const parsed = Hex.safeParse(v);\n return parsed.success ? parsed.data : undefined;\n};\n\n/** What an in-scene line is written on, read off where the blueprint places it. */\nconst SURFACES = [\n \"whiteboard\",\n \"sticky note\",\n \"notepad\",\n \"receipt\",\n \"packaging\",\n \"bottle\",\n \"jar\",\n \"sign\",\n \"screenshot\",\n \"phone\",\n \"screen\",\n] as const;\n\nfunction surfaceOf(placement: string | undefined): string | undefined {\n const lower = placement?.toLowerCase() ?? \"\";\n return SURFACES.find((s) => lower.includes(s));\n}\n\nconst TREATMENT_WORDS: Record<string, string> = {\n underline: \"underlined\",\n arrow: \"followed by an arrow\",\n button: \"as a button\",\n sticker: \"as a sticker\",\n highlight: \"highlighted\",\n};\n\nfunction treatmentOf(entry: Entry): string | undefined {\n const t = str(entry.treatment)?.toLowerCase();\n if (!t || t === \"plain\") return undefined;\n const key = Object.keys(TREATMENT_WORDS).find((k) => t.includes(k));\n return key ? TREATMENT_WORDS[key] : undefined;\n}\n\n/** Keep `value` under `key` only when it is non-empty. */\nconst field = <K extends string>(key: K, value: string | undefined): Partial<Record<K, string>> =>\n (value ? { [key]: value } : {}) as Partial<Record<K, string>>;\n\nfunction copyLineOf(entry: Entry, text: string, scrub: readonly string[]): AdCopyLine {\n const placement = str(entry.placement);\n const common = { text, ...field(\"role\", str(entry.role)), ...field(\"position\", plainText(placement, scrub)) };\n const treatment = treatmentOf(entry);\n if (!isInScene(entry)) return { ...common, lettering: \"designed\", ...field(\"look\", treatment) };\n const lettering = [plainText(entry.font_characteristics, scrub), treatment].filter(Boolean).join(\", \");\n const surface = surfaceOf(placement);\n return {\n ...common,\n lettering: \"in_scene\",\n ...field(\"on\", surface && `the ${surface}`),\n ...field(\"look\", lettering),\n };\n}\n\nfunction copyFromBlueprint(bp: Entry, scrub: readonly string[]): AdCopyLine[] {\n const seen = new Set<string>();\n return entries(bp.text_content).flatMap((entry) => {\n const text = str(entry.text);\n if (!text || str(entry.role)?.toLowerCase() === \"logo\" || seen.has(text)) return [];\n seen.add(text);\n return [copyLineOf(entry, text, scrub)];\n });\n}\n\nconst PRODUCT_CATEGORY = /product|package|packaging|bottle|box|can\\b|jar|tin|pack\\b/i;\nconst LIVING_CATEGORY = /animal|pet|dog|cat|character|mascot|creature/i;\n\n/** A blueprint entry's role, by what the rewritten blueprint says it is (never by the source's label). */\nfunction referenceRoleOf(collection: string, entry: Entry): { role: AdReferenceRole; prefix: string } {\n if (collection === \"people\") return { role: \"identity\", prefix: \"PERSON\" };\n if (collection === \"brands_logos\") return { role: \"prop\", prefix: \"BADGE\" };\n const category = `${str(entry.category) ?? \"\"} ${str(entry.treatment) ?? \"\"}`;\n if (LIVING_CATEGORY.test(category) || str(entry.expression)) return { role: \"identity\", prefix: \"ANIMAL\" };\n if (PRODUCT_CATEGORY.test(str(entry.category) ?? \"\")) return { role: \"product\", prefix: \"PRODUCT\" };\n return { role: \"prop\", prefix: \"PROP\" };\n}\n\n/**\n * The pictures an older blueprint grounds on: every entry the scaffold stamped with a\n * `reference_image` label. Relabelled by role (the stamped label is the source ad's, e.g. a\n * competitor's bag), with the canvas slot its old label names kept for wiring.\n */\nfunction referencesFromBlueprint(bp: Entry, scrub: readonly string[]): AdReference[] {\n const labelled = ([\"people\", \"subjects\", \"brands_logos\"] as const)\n .flatMap((collection) => entries(bp[collection]).map((entry) => ({ collection, entry })))\n .filter(\n ({ collection, entry }) =>\n str(entry.reference_image) &&\n !(collection === \"brands_logos\" && str(entry.function_in_image) === \"advertiser_brand\"),\n );\n const counters = new Map<string, number>();\n const pictured = labelled.map(({ collection, entry }): AdReference => {\n const { role, prefix } = referenceRoleOf(collection, entry);\n const n = (counters.get(prefix) ?? 0) + 1;\n counters.set(prefix, n);\n const described = entry.description ?? entry.apparent_identity_or_role ?? entry.what_it_represents;\n const what = plainText(described, scrub) || `the ${role === \"identity\" ? \"subject\" : role} in this picture`;\n return { id: `${prefix}_${n}`, role, slot: sanitizeId(`el_${str(entry.reference_image)}`, \"el\"), what };\n });\n // A product the blueprint describes with no picture stamped: a reference with no slot, so the\n // brief states it on its own and a trimmed scene cannot lose it (BENCH.md § \"Final\", the serum).\n const described = entries(bp.subjects).flatMap((entry): AdReference[] => {\n const what = plainText(entry.description, scrub);\n if (str(entry.reference_image) || !what || referenceRoleOf(\"subjects\", entry).role !== \"product\") return [];\n const n = (counters.get(\"PRODUCT\") ?? 0) + 1;\n counters.set(\"PRODUCT\", n);\n return [{ id: `PRODUCT_${n}`, role: \"product\", what }];\n });\n return [...pictured, ...described];\n}\n\n/** Where the advertiser's own logo sits in the blueprint, in words (percentages dropped). */\nfunction logoPositionOf(bp: Entry, scrub: readonly string[]): string | undefined {\n const own = entries(bp.brands_logos).find((e) => str(e.function_in_image) === \"advertiser_brand\");\n return plainText(own?.placement, scrub) || undefined;\n}\n\n/** The describe pass's roles for a brand the ad is about rather than one it merely shows (constants.ts `brands_logos`). */\nconst THIRD_PARTY_FUNCTIONS = new Set([\"partner\", \"sponsor\"]);\n\n/**\n * The partner and sponsor brands a blueprint shows, as third-party brands, each with the\n * reference that holds its logo picture when one is stamped. A brand `scrub` names (the source\n * ad's advertiser and, for a new ad, every brand the source showed) is never carried.\n */\nfunction thirdPartyFromBlueprint(bp: Entry, scrub: readonly string[], references: readonly AdReference[]) {\n const scrubbed = new Set(scrub.map((n) => n.trim().toLowerCase()));\n return entries(bp.brands_logos).flatMap((entry) => {\n const name = str(entry.brand_name);\n const role = str(entry.function_in_image)?.toLowerCase();\n if (!name || !role || !THIRD_PARTY_FUNCTIONS.has(role) || scrubbed.has(name.toLowerCase())) return [];\n const label = str(entry.reference_image);\n const slot = label ? sanitizeId(`el_${label}`, \"el\") : undefined;\n const logo = slot ? references.find((r) => r.slot === slot)?.id : undefined;\n return [{ name, ...field(\"what\", plainText(entry.what_it_represents, scrub)), ...(logo ? { logo } : {}) }];\n });\n}\n\nconst sub = (bp: Entry, key: string): Entry => (bp[key] && typeof bp[key] === \"object\" ? (bp[key] as Entry) : {});\n\nfunction lookOf(bp: Entry): AdScene[\"look\"] {\n const medium = str(sub(bp, \"meta\").medium)?.toLowerCase() ?? \"\";\n if (medium.includes(\"photo\")) return \"photo\";\n return /illustration|painting|3d/.test(medium) ? \"illustration\" : \"designed\";\n}\n\nfunction sceneDescriptionOf(bp: Entry, scrub: readonly string[]): string {\n const environment = sub(bp, \"environment\");\n const framing = plainText(sub(bp, \"composition\").framing, scrub);\n const setting = plainText(environment.setting, scrub);\n const things = [\n ...entries(bp.subjects).map((s) => plainText(s.description, scrub)),\n plainText(environment.foreground_elements, scrub),\n ].filter(Boolean);\n const where = [framing, setting].filter(Boolean).join(\", in \");\n const inFrame = things.length > 0 ? `In the frame: ${things.join(\"; \")}` : \"\";\n return [where, inFrame].filter(Boolean).join(\". \") || \"The ad's words on the brand's colours\";\n}\n\n/** How a photo was taken, in words: the camera, its height and angle, the light, the grain. */\nfunction captureOf(bp: Entry, scrub: readonly string[]): string {\n const camera = sub(bp, \"camera\");\n const lighting = sub(bp, \"lighting\");\n const source = entries(lighting.sources)[0];\n const direction = source ? str(source.direction) : undefined;\n const light = source\n ? plainText(\n [str(source.quality), str(source.type), \"light\", direction && `from the ${direction}`]\n .filter(Boolean)\n .join(\" \"),\n scrub,\n )\n : \"\";\n const grain = str(sub(bp, \"post_processing\").grain);\n return [\n plainText(camera.capture, scrub),\n plainText(camera.viewpoint, scrub),\n plainText(sub(bp, \"composition\").camera_height, scrub),\n plainText(camera.perspective ?? camera.perspective_distortion, scrub),\n light,\n plainText(lighting.specular_highlights, scrub),\n grain && grain !== \"none\" ? `${plainText(grain)} grain` : \"\",\n ]\n .filter(Boolean)\n .join(\", \");\n}\n\nfunction sceneFromBlueprint(bp: Entry, scrub: readonly string[]): AdScene {\n const look = lookOf(bp);\n const keywords = sub(bp, \"style\").mood_keywords;\n const mood = Array.isArray(keywords) ? keywords.filter((m): m is string => typeof m === \"string\").join(\", \") : \"\";\n return {\n description: sceneDescriptionOf(bp, scrub),\n look,\n ...field(\"capture\", look === \"photo\" ? captureOf(bp, scrub) : undefined),\n ...field(\"mood\", mood),\n ...field(\"layout\", plainText(sub(bp, \"composition\").visual_flow, scrub)),\n };\n}\n\n/** The colours the describe pass tied to what carries them (`color.palette[].where`); the compiler keeps the brand's only. */\nfunction colorsFromBlueprint(bp: Entry, scrub: readonly string[]): AdColorUse[] {\n const palette = entries((bp.color as Entry | undefined)?.palette);\n return palette.flatMap((c) => {\n const object = plainText(c.where, scrub);\n const name = plainText(c.name, scrub);\n const hex = hexOrUndefined(c.hex);\n return object && name ? [{ object, name, ...(hex ? { hex } : {}) }] : [];\n });\n}\n\n/**\n * The designed type's face in words, from the describe pass's `fonts` (the headline's entry\n * first): what the brief names when no specimen of the brand's face is wired, instead of a\n * generic sans that contradicts a serif or script brand.\n */\nfunction typefaceFromFonts(bp: Entry): string | undefined {\n const fonts = entries(bp.fonts);\n const font = fonts.find((f) => /headline/i.test(str(f.used_for) ?? \"\")) ?? fonts[0];\n const kind = plainText(font?.classification);\n const family = plainText(font?.family_guess);\n if (!kind && !family) return undefined;\n if (!kind) return family;\n return family ? `a ${kind} face like ${family}` : `a ${kind} face`;\n}\n\nfunction brandFromBlueprint(bp: Entry): AdBrand | undefined {\n const b = bp.brand as Entry | undefined;\n const typeface = str(b?.typeface) ?? typefaceFromFonts(bp);\n if (!b || typeof b !== \"object\") return typeface ? { typeface, palette: [] } : undefined;\n const palette = Array.isArray(b.palette) ? b.palette.map(hexOrUndefined).filter((h): h is string => Boolean(h)) : [];\n const roles = Object.fromEntries(\n ([\"ground\", \"ink\", \"accent\", \"cta\"] as const).flatMap((k) => {\n const hex = hexOrUndefined(b[k]);\n return hex ? [[k, hex]] : [];\n }),\n );\n const name = str(b.name);\n return { ...(name ? { name } : {}), ...(typeface ? { typeface } : {}), ...roles, palette };\n}\n\n/**\n * An older blueprint (the describe pass's read of the source ad, rewritten by the agent) as\n * an ad spec. Pure. Keeps what a render acts on and drops what it cannot (the source's\n * provenance, ownership metadata, lens figures, percentages, sampled hexes); `scrub` names\n * advertisers whose names must not survive into any compiled text.\n */\nexport function adSpecFromBlueprint(raw: unknown, opts: { scrub?: readonly string[] } = {}): AdSpec {\n const bp = (raw && typeof raw === \"object\" ? raw : {}) as Entry;\n const scrub = opts.scrub ?? [];\n const intent = (bp.ad_intent ?? {}) as Entry;\n const brand = brandFromBlueprint(bp);\n const message = str(intent.viewer_takeaway);\n const viewer = Viewer.safeParse(bp.viewer);\n const references = referencesFromBlueprint(bp, scrub);\n return {\n schema: AD_SPEC_SCHEMA,\n ...(viewer.success ? { viewer: viewer.data } : {}),\n ...(message ? { message: plainText(message, scrub) } : {}),\n copy: copyFromBlueprint(bp, scrub),\n scene: sceneFromBlueprint(bp, scrub),\n ...(brand ? { brand } : {}),\n colors: colorsFromBlueprint(bp, scrub),\n references,\n logo: conceptHasLogo(bp),\n third_party_brands: thirdPartyFromBlueprint(bp, scrub, references),\n scene_text: [],\n ...field(\"logo_position\", logoPositionOf(bp, scrub)),\n mechanism: winningMechanisms(bp).flatMap((m) => {\n const device = plainText(m.device, scrub);\n const why = m.why_it_works ? plainText(m.why_it_works, scrub) : \"\";\n return device ? [{ device, ...field(\"why_it_works\", why) }] : [];\n }),\n ...(bp.reference !== undefined ? { reference: bp.reference } : {}),\n };\n}\n\n/** The ad spec as JSON Schema, for a writer that needs the exact shape (input form: defaults optional). */\nexport function adSpecJsonSchema(): unknown {\n return z.toJSONSchema(AdSpecSchema, { io: \"input\", unrepresentable: \"any\" });\n}\n","import { REFERENCE_SHEET_MODEL } from \"@baker/canvas-contract\";\n\n/**\n * The model every scaffold fuses a recurring element's multi-view reference sheet on:\n * the registry's `referenceSheet` role, which a test keeps registered for\n * `image_reference_sheet` (a sheet node on any other model is `unknown_model`).\n */\nexport const ACTOR_SHEET_MODEL = REFERENCE_SHEET_MODEL;\n\n/** lower_snake_case id that satisfies the canvas NodeIdRe; falls back to a stable default. */\nexport function sanitizeId(raw: string, fallback: string): string {\n const id = raw\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, \"\") || fallback;\n}\n\n/**\n * What the video and static scaffolds both know about a selected identity element: the\n * AI selection pass names its type, a label and how it looks; a video element may also be\n * a second look (`same_as`) of another.\n */\nexport type SelectedElement = {\n type: string;\n label?: string;\n description?: string;\n expression?: string | null;\n same_as?: string | null;\n};\n\n/** Which scaffold is asking: the two write different instructions for the same element. */\nexport type ScaffoldMedium = \"video\" | \"static\";\n\n/**\n * Assign each element its UPPER_SNAKE reference label, deduped in order (HERO, HERO_2, ...)\n * — one source of truth shared by every consumer: the slot id, the prompt legend and the\n * annotation in prompt.json all read the same label.\n *\n * A video names every logo BRAND_LOGO: a logo's detected label carries the SOURCE brand\n * (\"LEMONADE_LOGO\"), which leaked the reference brand into the canvas ids and legends, and a\n * reproduction's brand swap must leave no trace. A static ad goes further and labels every\n * element by its role (PRODUCT_1, PERSON_1, PROP_1, LOGO): the selector labels what it sees\n * on the SOURCE ad, and a competitor's \"DOORDASH_DELIVERY_BAG\" reached every render prompt\n * as \"the showcased product\" of an ad that had none (prompt-audit.md §1a.3).\n */\nexport function assignElementLabels<T extends SelectedElement>(\n elements: readonly T[],\n medium: ScaffoldMedium,\n): Array<{ el: T; label: string }> {\n if (medium === \"static\") return staticElementLabels(elements);\n const used = new Set<string>();\n return elements.map((el) => {\n const raw =\n medium === \"video\" && el.type?.toLowerCase() === \"logo\" ? \"BRAND_LOGO\" : (el.label ?? el.type ?? \"ELEMENT\");\n const base =\n raw\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\") || \"ELEMENT\";\n let label = base;\n let n = 2;\n while (used.has(label)) label = `${base}_${n++}`;\n used.add(label);\n return { el, label };\n });\n}\n\nconst STATIC_LABEL_PREFIX: Record<string, string> = {\n person: \"PERSON\",\n animal: \"ANIMAL\",\n product: \"PRODUCT\",\n badge: \"BADGE\",\n};\n\n/** A static ad's role-based labels, numbered per role in order: PRODUCT_1, PRODUCT_2, PROP_1, LOGO, LOGO_2. */\nfunction staticElementLabels<T extends SelectedElement>(elements: readonly T[]): Array<{ el: T; label: string }> {\n const counts = new Map<string, number>();\n return elements.map((el) => {\n const type = el.type?.toLowerCase() ?? \"\";\n const prefix = type === \"logo\" ? \"LOGO\" : (STATIC_LABEL_PREFIX[type] ?? \"PROP\");\n const n = (counts.get(prefix) ?? 0) + 1;\n counts.set(prefix, n);\n const label = prefix === \"LOGO\" ? (n === 1 ? \"LOGO\" : `LOGO_${n}`) : `${prefix}_${n}`;\n return { el, label };\n });\n}\n\n// What the generator should do with each reference, by element type. A video's roles stress\n// consistency ACROSS frames; a static ad renders one frame, and its person is always a cast\n// Avatar's sheet, so its roles say what to take from the reference instead.\nconst REFERENCE_ROLES: Record<ScaffoldMedium, { byType: Record<string, string>; fallback: string }> = {\n video: {\n byType: {\n logo: \"the brand wordmark/logo; reproduce it exactly, do not redraw or restyle it.\",\n badge: \"a trust/rating badge; reproduce it exactly, do not invent ratings or seals.\",\n product:\n \"the showcased product; keep this exact product identity consistent across every frame. Ignore any caption text printed on this reference.\",\n person:\n \"a recurring cast member; render the SAME individual as this reference image and keep them consistent across EVERY frame — their appearance comes from this reference, never from prose. Ignore any caption text printed on this reference.\",\n animal:\n \"a recurring cast member; render the SAME individual as this reference image and keep them consistent across EVERY frame — their appearance comes from this reference, never from prose. Ignore any caption text printed on this reference.\",\n location:\n \"the fixed set/location; keep the room, background, and layout identical to this reference across EVERY frame — do not re-invent the environment. Ignore any caption text printed on this reference.\",\n },\n fallback:\n \"a recurring identity element; reproduce it faithfully and keep it consistent across every frame. Ignore any caption text printed on it.\",\n },\n static: {\n byType: {\n logo: \"the brand wordmark/logo; reproduce it as closely as possible, do not redraw or restyle it.\",\n badge: \"a trust/rating badge; reproduce it exactly, do not invent ratings or seals.\",\n product:\n \"the showcased product; keep this exact product identity. Ignore any caption text printed on this reference.\",\n person:\n \"the emotional hero — the brand's cast person: a company Avatar's identity sheet, a multi-view turnaround of ONE person the brand may show. Keep this exact face, hair and build, and render them once in the pose, gaze, wardrobe and expression the blueprint specifies — never as a grid. Ignore the cell labels printed on the sheet.\",\n animal:\n \"the emotional hero — the face/muzzle/eyes the viewer locks onto and exactly where AI artifacts (malformed mouths, teeth, foam, extra fingers) betray a fake. It MUST be grounded in this real reference, never invented from prose. Keep this identity AND the expression the blueprint specifies. Ignore any caption text printed on this reference.\",\n },\n fallback:\n \"an identity-bearing element; reproduce it faithfully from this reference. Ignore any caption text printed on it.\",\n },\n};\n\n/** The reference legend's instruction for one element type. */\nexport function referenceRole(type: string, medium: ScaffoldMedium): string {\n const roles = REFERENCE_ROLES[medium];\n return roles.byType[type.toLowerCase()] ?? roles.fallback;\n}\n\n/**\n * The `[TODO]` an element's empty slot carries: what to drop there, and what never to.\n *\n * A person is cast from a company Avatar, never grounded on a photo of somebody real: the\n * image model copies the face of whoever is in the reference, whatever the prompt says\n * (measured on gpt-image-2 and Gemini 3 Pro Image, BAKER-646/652). Everything else is one\n * real image, as a path RELATIVE to the canvas file: an absolute one resolves only in the\n * workspace that wrote it and fails the publish build (validate-creatives:\n * absolute-reference).\n */\nexport function elementTodo(el: SelectedElement, label: string, medium: ScaffoldMedium): string {\n const desc = el.description ? ` — ${el.description}` : \"\";\n const expr = el.expression ? `, with a ${el.expression} expression` : \"\";\n const t = el.type.toLowerCase();\n return medium === \"video\"\n ? videoElementTodo(el, t, label, `${desc}${expr}`)\n : staticElementTodo(el, t, label, `${desc}${expr}`);\n}\n\nfunction videoElementTodo(el: SelectedElement, t: string, label: string, brief: string): string {\n // Animals/sets are RE-SHOT fresh — never the source ad's own.\n const fresh =\n t === \"animal\" || t === \"location\"\n ? \" [SOURCE FRESH — a DIFFERENT animal/set than the original ad; do not reuse the source's]\"\n : \"\";\n // A same_as element is the same recast person in another outfit — source the\n // SAME individual you cast for the linked slot, only in this wardrobe.\n const same = el.same_as\n ? ` [SAME INDIVIDUAL as ${el.same_as} — a different wardrobe/look of the same person; reuse that cast person, change only the outfit]`\n : \"\";\n if (t === \"person\") {\n return `[TODO: cast a company Avatar as ${label} (person)${brief}${same} — re-run scaffold-video with \\`--avatar <handle>\\` (\\`baker avatars list\\`; \\`baker avatars create\\` invents a new face). NEVER a photo of a real person found on the web or taken from the source ad: the image model reproduces that face]`;\n }\n return `[TODO: drop one real source image for ${label} (${el.type})${brief} — reused across every frame it appears in${fresh}${same}; use a path relative to this canvas file, never an absolute path]`;\n}\n\nfunction staticElementTodo(el: SelectedElement, t: string, label: string, brief: string): string {\n // The emotional hero must stay grounded — do NOT delete this slot and let the generator\n // dream the subject up; a free-generated face/muzzle reads as AI and grows artifacts.\n // A person's face has to be one the brand may show: the render keeps whatever face it is\n // given (a \"use the look, not the face\" instruction does not stop gpt-image-2 or Gemini\n // from copying it), so it is a company Avatar, synthetic or likeness-confirmed.\n if (t === \"person\") {\n return `[TODO: cast a company Avatar as ${label} (person)${brief} — REQUIRED: this is the emotional hero and the render keeps this face, so re-run scaffold-static-ad with \\`--avatar <handle>\\` (\\`baker avatars list\\`; \\`baker avatars create\\` invents a new face); never a found photo of a real person (web search, the source ad), who never agreed to appear in this ad]`;\n }\n const lock =\n t === \"animal\"\n ? \" — REQUIRED: this is the emotional hero; ground it in a real reference (image-library / video-library / Pinterest), do not delete this slot and free-generate\"\n : \"\";\n return `[TODO: drop a real image for ${label} (${el.type})${brief}${lock} — replace with a path relative to this canvas file, never an absolute path]`;\n}\n","/**\n * A native surface: the ad is a screen people already know (a post on X, a WhatsApp chat, a wall of\n * Trustpilot reviews, a note, a Slack message), drawn exactly, from HTML, rather than painted.\n *\n * Tier 1, Oct 2026: asked to \"replicate a tweet\", \"a WhatsApp chat\", \"a Trustpilot wall\", the image\n * model drew a generic card with our logo on top, an invented handle and a button; the person\n * rating it: \"this doesn't look at all like WhatsApp\", \"we are not replicating any official\n * platform\". A disguise only works when it is the real thing at a glance. So the screen is built\n * here, in the platform's own layout, type sizes and colours, with every word exact; the image\n * model is asked only for the photos the screen holds (the post's picture, a customer's portrait),\n * each a plain photo with no words, which is the one thing it does well. One designer: the HTML\n * lays out the ad, the photos are content, as in a real screenshot.\n *\n * Every size is the same screen fitted inside the area that size blocks on (`nativeLayoutInsets`),\n * so a 9:16 never puts a word where Stories covers it, by construction.\n *\n * Pure: the spec's `native` block, the brand and the pictures in, an HTML page out.\n */\nimport { z } from \"zod\";\nimport { figureForms } from \"./review-platforms.ts\";\n\nexport const NATIVE_SURFACES = [\n \"x_post\",\n \"whatsapp\",\n \"imessage\",\n \"trustpilot\",\n \"google_reviews\",\n \"notes\",\n \"slack\",\n] as const;\nexport type NativeSurfaceKind = (typeof NATIVE_SURFACES)[number];\n\nconst Hex = z\n .string()\n .regex(/^#[0-9a-fA-F]{6}$/, \"a colour is a 6-digit hex like #123551\")\n .transform((h) => h.toLowerCase());\n\n/** A photo the screen holds, by id: PHOTO_1. Rendered by the image model with no words, or ours (`image`). */\nconst PhotoId = z.string().regex(/^PHOTO_\\d+$/, \"a photo id is PHOTO_1, PHOTO_2…\");\n\n/**\n * Who a message, post or review is from, as the screen shows them. `avatar` is the brand's logo\n * (`logo`, the brand speaking), a photo of the screen (`PHOTO_1`), or their initials on a colour,\n * as most review sites and chats show a person without a picture.\n */\nconst Author = z\n .object({\n name: z.string().trim().min(1),\n /** Their handle with the @, as the platform prints it: a real one of ours, never an invented one. */\n handle: z.string().trim().min(2).optional(),\n avatar: z.union([z.literal(\"logo\"), z.literal(\"initials\"), PhotoId]).default(\"initials\"),\n verified: z.boolean().optional(),\n /** A line under the name: a review site's \"3 reviews · ES\", a chat's \"online\". */\n subtitle: z.string().trim().min(1).optional(),\n })\n .strict();\nexport type NativeAuthor = z.infer<typeof Author>;\n\nconst Photo = z\n .object({\n id: PhotoId,\n /** What the photo shows, in plain words: who, doing what, where. */\n what: z.string().trim().min(1),\n /** Its shape on the screen. */\n ratio: z.enum([\"1:1\", \"4:3\", \"3:4\", \"16:9\", \"4:5\"]).default(\"4:3\"),\n look: z.enum([\"photo\", \"illustration\"]).default(\"photo\"),\n /**\n * A picture of ours (a path from the creative's folder, or a URL): used as it is, nothing\n * rendered. There is no `avatar`: nothing grounds a rendered photo on an avatar's sheet, so a\n * photo naming one would still render a made-up face (the strict schema refuses the key).\n */\n image: z.string().trim().min(1).optional(),\n })\n .strict();\nexport type NativePhoto = z.infer<typeof Photo>;\n\nconst XPost = z\n .object({\n author: Author,\n text: z.string().trim().min(1),\n photo: PhotoId.optional(),\n /** The handle this post answers, printed above it as X does (\"Replying to @alexbuilds\"). */\n reply_to: z.string().trim().min(2).optional(),\n /** How long ago, as X prints it: 2h, 14m, Sep 3. */\n time: z.string().trim().min(1).optional(),\n stats: z\n .object({\n replies: z.string().optional(),\n reposts: z.string().optional(),\n likes: z.string().optional(),\n views: z.string().optional(),\n })\n .strict()\n .optional(),\n })\n .strict();\n\nconst ChatCard = z\n .object({\n photo: PhotoId.optional(),\n title: z.string().trim().min(1),\n subtitle: z.string().trim().min(1).optional(),\n /** The card's button, as the chat prints it (\"Pagar $899 MXN\"). */\n button: z.string().trim().min(1).optional(),\n })\n .strict();\n\nconst ChatMessage = z\n .object({\n /** `them` is the other side of the chat (the contact in the header), `me` the phone's owner. */\n from: z.enum([\"them\", \"me\"]),\n text: z.string().trim().min(1).optional(),\n time: z.string().trim().min(1).optional(),\n card: ChatCard.optional(),\n })\n .strict()\n .refine((m) => m.text || m.card, \"a message has text or a card\");\n\nconst Review = z\n .object({\n author: Author,\n stars: z.number().int().min(1).max(5).default(5),\n title: z.string().trim().min(1).optional(),\n text: z.string().trim().min(1),\n date: z.string().trim().min(1).optional(),\n })\n .strict();\n\nconst NotesItem = z.object({ text: z.string().trim().min(1), checked: z.boolean().default(false) }).strict();\n\nconst SlackMessage = z\n .object({\n author: Author,\n text: z.string().trim().min(1),\n time: z.string().trim().min(1).optional(),\n reactions: z\n .array(z.object({ emoji: z.string().trim().min(1), count: z.number().int().min(1) }).strict())\n .default([]),\n /** \"3 replies\" under the message. */\n replies: z.string().trim().min(1).optional(),\n })\n .strict();\n\n/**\n * What sits around the screen: a headline in the brand's face above it, a caption under it, the\n * brand's logo, the colour behind it. A native ad usually has none of them: the platform prints the\n * page's name and button around every ad.\n */\nconst Frame = z\n .object({\n headline: z.string().trim().min(1).optional(),\n caption: z.string().trim().min(1).optional(),\n logo: z.boolean().default(false),\n /** Where the frame's logo goes: under the screen, or over it as a badge (a review wall's anchor). */\n logo_position: z.enum([\"under\", \"over\"]).default(\"under\"),\n background: Hex.optional(),\n })\n .strict()\n .default({ logo: false, logo_position: \"under\" });\n\nconst Base = { photos: z.array(Photo).default([]), frame: Frame };\n\nexport const NativeSurfaceSchema = z.discriminatedUnion(\"surface\", [\n z\n .object({\n surface: z.literal(\"x_post\"),\n theme: z.enum([\"light\", \"dark\"]).default(\"light\"),\n /** One post, or a thread: each after the first a reply under it, its line joining them. */\n posts: z.array(XPost).min(1).max(4),\n ...Base,\n })\n .strict(),\n z\n .object({\n surface: z.enum([\"whatsapp\", \"imessage\"]),\n /** Who the chat is with: the header's name and picture. */\n contact: Author,\n messages: z.array(ChatMessage).min(1).max(8),\n /** The day pill above the messages (\"Hoy\", \"Today\"). */\n day: z.string().trim().min(1).optional(),\n /** Drawn inside a phone; else the screen alone. Default true. */\n device: z.boolean().default(true),\n ...Base,\n })\n .strict(),\n z\n .object({\n surface: z.enum([\"trustpilot\", \"google_reviews\"]),\n /** The rating the platform shows for the company: a real one, in `src/content`. */\n summary: z\n .object({ score: z.string().trim().min(1), count: z.string().trim().min(1), label: z.string().optional() })\n .strict()\n .optional(),\n /**\n * Every real review the wall shows, the strongest first: the first two are read as the ad's\n * copy, whole in the safe area; the rest are the wall they lie on, as large and as sharp, across\n * the whole frame and cut by its edges. A wall wins on how many there are (8 to 16), each one a\n * real review in the company's own words.\n */\n reviews: z.array(Review).min(1).max(16),\n ...Base,\n })\n .strict(),\n z\n .object({\n surface: z.literal(\"notes\"),\n title: z.string().trim().min(1),\n items: z.array(NotesItem).default([]),\n body: z.string().trim().min(1).optional(),\n ...Base,\n })\n .strict(),\n z\n .object({\n surface: z.literal(\"slack\"),\n channel: z.string().trim().min(1),\n messages: z.array(SlackMessage).min(1).max(6),\n ...Base,\n })\n .strict(),\n]);\nexport type NativeSurface = z.infer<typeof NativeSurfaceSchema>;\n\nconst SURFACE_NAMES: Record<NativeSurfaceKind, string> = {\n x_post: \"a post on X\",\n whatsapp: \"a WhatsApp chat\",\n imessage: \"an iMessage chat\",\n trustpilot: \"a wall of Trustpilot reviews\",\n google_reviews: \"a wall of Google reviews\",\n notes: \"a note in the Notes app\",\n slack: \"a Slack message\",\n};\n\n/** The screen as a viewer would name it (\"a post on X\"). */\nexport function nativeSurfaceName(surface: Pick<NativeSurface, \"surface\">): string {\n return SURFACE_NAMES[surface.surface];\n}\n\n// --- What the screen says ------------------------------------------------------------\n\n/** Every word of ours the screen prints, in reading order: what the review reads back as copy. */\nexport function nativeCopy(surface: NativeSurface): string[] {\n const frame = [surface.frame.headline, surface.frame.caption];\n const body = ((): Array<string | undefined> => {\n switch (surface.surface) {\n case \"x_post\":\n return surface.posts.map((p) => p.text);\n case \"whatsapp\":\n case \"imessage\":\n return surface.messages.flatMap((m) => [m.text, m.card?.title, m.card?.subtitle, m.card?.button]);\n case \"trustpilot\":\n case \"google_reviews\":\n return surface.reviews.slice(0, FEATURED_REVIEWS).flatMap((r) => [r.title, shownReview(r.text)]);\n case \"notes\":\n return [surface.title, ...surface.items.map((i) => i.text), surface.body];\n case \"slack\":\n return surface.messages.map((m) => m.text);\n default:\n return [];\n }\n })();\n return [frame[0], ...body, frame[1]].filter((t): t is string => Boolean(t?.trim()));\n}\n\n/**\n * How many reviews of a wall are read as its copy, set whole inside the safe area: the rest are the\n * wall behind them, cut by the frame's edges as the reference walls are. Two, as the walls that win\n * show (Tier 1, Oct 2026: three in a column were small and the blurred pile under them read as a mess).\n */\nexport const FEATURED_REVIEWS = 2;\n\n/** The most of a review's text a card shows: past it, the platform's own lists cut it at a word. */\nconst REVIEW_SHOWN = 160;\n\n/** A review's text as its card shows it: whole, or cut at the last word that fits, with an ellipsis. */\nfunction shownReview(text: string): string {\n if (text.length <= REVIEW_SHOWN) return text;\n const cut = text.slice(0, REVIEW_SHOWN - 1);\n const atWord = cut.slice(0, Math.max(cut.lastIndexOf(\" \"), REVIEW_SHOWN / 2));\n return `${atWord.replace(/[\\s,.;:!?¡¿-]+$/u, \"\")}…`;\n}\n\n/** The authors the screen names. */\nfunction authorsOf(surface: NativeSurface): NativeAuthor[] {\n switch (surface.surface) {\n case \"x_post\":\n return surface.posts.map((p) => p.author);\n case \"whatsapp\":\n case \"imessage\":\n return [surface.contact];\n case \"trustpilot\":\n case \"google_reviews\":\n return surface.reviews.map((r) => r.author);\n case \"slack\":\n return surface.messages.map((m) => m.author);\n default:\n return [];\n }\n}\n\n/**\n * The words the platform itself prints (names, handles, times, counts, its labels), in the\n * viewer's language: never copy, and the review never calls them extra words.\n */\nexport function nativeChrome(surface: NativeSurface, language?: string): string[] {\n const t = chromeWords(language);\n // A reader splits a handle at its underscores and dots: every part is the platform's too.\n const handleParts = (h?: string) => (h ? [h, h.replace(/^@/, \"\"), ...h.replace(/^@/, \"\").split(/[_.]/)] : []);\n const authors = authorsOf(surface).flatMap((a) => [a.name, ...handleParts(a.handle), a.subtitle, initials(a.name)]);\n const own = ((): Array<string | undefined> => {\n switch (surface.surface) {\n case \"x_post\":\n return surface.posts.flatMap((p) => [\n p.time,\n ...Object.values(p.stats ?? {}),\n ...(p.reply_to ? [t.replying, ...handleParts(p.reply_to)] : []),\n ]);\n case \"whatsapp\":\n case \"imessage\":\n return [surface.day ?? t.today, ...surface.messages.map((m) => m.time), t.online, t.business, t.message];\n case \"trustpilot\":\n case \"google_reviews\":\n return [\n ...surface.reviews.slice(FEATURED_REVIEWS).flatMap((r) => [r.title, shownReview(r.text)]),\n ...surface.reviews.map((r) => r.date),\n surface.summary?.score,\n surface.summary?.count,\n surface.summary?.label ?? t.excellent,\n t.verified,\n surface.surface === \"trustpilot\" ? \"Trustpilot\" : \"Google\",\n t.reviewsWord,\n ];\n case \"notes\":\n return [t.notes];\n case \"slack\":\n return [\n `#${surface.channel.replace(/^#/, \"\")}`,\n ...surface.messages.flatMap((m) => [m.time, m.replies, ...m.reactions.map((r) => String(r.count))]),\n ];\n default:\n return [];\n }\n })();\n // A 9:16 draws the app full screen, with its reply and message prompts.\n const prompts = [t.post, t.replyPrompt, t.messagePrompt];\n return [...new Set([...authors, ...own, ...prompts].filter((w): w is string => Boolean(w?.trim())))];\n}\n\n/** Every photo id the screen names, each once: an avatar, a post's picture, a chat card's. */\nfunction photoIdsNamed(surface: NativeSurface): string[] {\n const avatars = authorsOf(surface).flatMap((a) => (a.avatar.startsWith(\"PHOTO_\") ? [a.avatar] : []));\n const pictures =\n surface.surface === \"x_post\"\n ? surface.posts.map((p) => p.photo)\n : surface.surface === \"whatsapp\" || surface.surface === \"imessage\"\n ? surface.messages.map((m) => m.card?.photo)\n : [];\n return [...new Set([...avatars, ...pictures.filter((id): id is string => Boolean(id))])];\n}\n\n/** The photos the screen shows, each once, in the order they are named. */\nexport function nativePhotosShown(surface: NativeSurface): NativePhoto[] {\n const named = new Set(photoIdsNamed(surface));\n return surface.photos.filter((p) => named.has(p.id));\n}\n\n/** The photos the screen shows as somebody's avatar (a square portrait), not as a picture. */\nexport function avatarPhotoIds(surface: NativeSurface): Set<string> {\n return new Set(authorsOf(surface).flatMap((a) => (a.avatar.startsWith(\"PHOTO_\") ? [a.avatar] : [])));\n}\n\n/** A photo named on the screen that the spec does not describe. */\nexport function nativeProblems(surface: NativeSurface): string[] {\n const described = new Set(surface.photos.map((p) => p.id));\n return photoIdsNamed(surface)\n .filter((id) => !described.has(id))\n .map(\n (id) => `\\`native\\` shows ${id} but \\`native.photos\\` does not say what it is: add {\"id\":\"${id}\",\"what\":\"…\"}.`,\n );\n}\n\n/**\n * What stops a native screen from rendering, in the agent's words: a photo it never describes, a\n * handle of ours the company's records never state (Tier 1, Oct 2026: the brand posted as an\n * invented \"@kampe.pro\"), and a rating the records do not hold. `records` is COMPANY.md and\n * src/content, as the proof check reads them. Pure.\n */\nexport function nativeGateProblems(surface: NativeSurface, records: string, brandName?: string): string[] {\n const problems = nativeProblems(surface);\n const haystack = records.toLowerCase();\n const ours = (a: NativeAuthor) =>\n a.avatar === \"logo\" || (brandName !== undefined && a.name.trim().toLowerCase() === brandName.trim().toLowerCase());\n for (const author of authorsOf(surface)) {\n if (!author.handle || !ours(author)) continue;\n const bare = author.handle.replace(/^@/, \"\").toLowerCase();\n if (!haystack.includes(bare)) {\n problems.push(\n `The screen shows the brand as ${author.handle}, a handle COMPANY.md and src/content never state: use the brand's real handle, or drop \\`handle\\` (never an invented one).`,\n );\n }\n }\n if ((surface.surface === \"trustpilot\" || surface.surface === \"google_reviews\") && surface.summary) {\n if (!figureForms(surface.summary.score.toLowerCase()).some((form) => haystack.includes(form))) {\n problems.push(\n `The screen shows a ${surface.summary.score} rating the company's records (COMPANY.md, src/content) do not hold: add where it comes from, or drop \\`summary\\`.`,\n );\n }\n }\n return problems;\n}\n\n// --- The page ------------------------------------------------------------------------\n\ntype Words = {\n today: string;\n online: string;\n business: string;\n message: string;\n verified: string;\n excellent: string;\n reviewsWord: string;\n notes: string;\n experience: string;\n replying: string;\n /** The title over a thread on X. */\n post: string;\n /** The reply bar under a thread on X. */\n replyPrompt: string;\n /** Slack's message bar, before the channel. */\n messagePrompt: string;\n};\n\nconst WORDS: Record<string, Words> = {\n es: {\n today: \"Hoy\",\n online: \"en línea\",\n business: \"Cuenta de empresa\",\n message: \"Mensaje\",\n verified: \"Verificada\",\n excellent: \"Excelente\",\n reviewsWord: \"opiniones\",\n notes: \"Notas\",\n experience: \"Fecha de la experiencia\",\n replying: \"En respuesta a\",\n post: \"Post\",\n replyPrompt: \"Postea tu respuesta\",\n messagePrompt: \"Enviar mensaje a\",\n },\n pt: {\n today: \"Hoje\",\n online: \"online\",\n business: \"Conta comercial\",\n message: \"Mensagem\",\n verified: \"Verificada\",\n excellent: \"Excelente\",\n reviewsWord: \"avaliações\",\n notes: \"Notas\",\n experience: \"Data da experiência\",\n replying: \"Em resposta a\",\n post: \"Post\",\n replyPrompt: \"Poste sua resposta\",\n messagePrompt: \"Enviar mensagem para\",\n },\n en: {\n today: \"Today\",\n online: \"online\",\n business: \"Business account\",\n message: \"Message\",\n verified: \"Verified\",\n excellent: \"Excellent\",\n reviewsWord: \"reviews\",\n notes: \"Notes\",\n experience: \"Date of experience\",\n replying: \"Replying to\",\n post: \"Post\",\n replyPrompt: \"Post your reply\",\n messagePrompt: \"Message\",\n },\n};\n\nfunction chromeWords(language?: string): Words {\n const lang = (language ?? \"en\").slice(0, 2).toLowerCase();\n return WORDS[lang] ?? (WORDS.en as Words);\n}\n\n/** The pictures the page is built with, each a URL the page can load (a data URL in practice). */\nexport type NativeAssets = {\n /** The logo that reads on a light card (a review site's summary, an avatar). */\n logo?: string;\n /** The logo that reads on the colour behind the screen, for the frame; else `logo`. */\n frameLogo?: string;\n /** The logo's square mark (an icon or isologo file), for an avatar; else the logo itself, contained. */\n mark?: string;\n photos: Record<string, string>;\n /** The brand's face for the frame's headline (a font URL). */\n font?: string;\n /** The logos above that are light (white) ink: on a light ground they are inked dark, the brand having no dark one. */\n lightLogos?: string[];\n};\n\n/** ` inked` when a light logo sits on a light ground, so it reads; else nothing. */\nfunction inked(assets: NativeAssets, src: string | undefined, groundIsDark: boolean): string {\n return src && !groundIsDark && assets.lightLogos?.includes(src) ? \" inked\" : \"\";\n}\n\nexport type NativeFrameOpts = {\n /** The delivered size in pixels; the page lays out at half (2x device scale). */\n width: number;\n height: number;\n /** The fractions of each edge an interface covers or a feed trims (`staticSafeArea`). */\n safe: { top: number; bottom: number; left: number; right: number };\n brand: { name?: string; ground?: string; ink?: string; accent?: string; cta?: string };\n language?: string;\n};\n\n/** A time or figure kept on one line: \"11:47 p. m.\", \"4-6 semanas\", \"100 %\". */\nfunction keepTogether(html: string): string {\n return html\n .replace(/\\b([ap])\\. m\\./g, \"$1.&nbsp;m.\")\n .replace(/(\\d) (?=[ap]\\.|%|€|\\$|(?:semanas|weeks|días|days|min)\\b)/g, \"$1&nbsp;\");\n}\n\nconst esc = (text: string): string =>\n text.replace(/[&<>\"]/g, (ch) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" })[ch] as string);\n\n/** A post's text with its hashtags, mentions and links in the platform's link colour. */\nfunction linked(text: string): string {\n return esc(text).replace(/(^|[\\s(])([#@][\\p{L}\\p{N}_]+|https?:\\/\\/\\S+)/gu, '$1<span class=\"link\">$2</span>');\n}\n\n/** One or two letters for an avatar without a picture. */\nexport function initials(name: string): string {\n const words = name\n .replace(/[^\\p{L}\\s]/gu, \" \")\n .split(/\\s+/)\n .filter(Boolean);\n const first = words[0]?.[0] ?? \"\";\n const second = words.length > 1 ? (words[1]?.[0] ?? \"\") : \"\";\n return `${first}${second}`.toUpperCase();\n}\n\n/** The soft colours review sites put behind initials, picked by name so a person keeps theirs. */\nconst INITIALS_TINTS = [\"#f4d7c4\", \"#d6e4f5\", \"#dcecd7\", \"#f2dcef\", \"#f6e7b8\", \"#d9d6f2\"];\nfunction tintOf(name: string): string {\n let sum = 0;\n for (const ch of name) sum += ch.codePointAt(0) ?? 0;\n return INITIALS_TINTS[sum % INITIALS_TINTS.length] as string;\n}\n\nfunction avatarHtml(author: NativeAuthor, assets: NativeAssets, cls: string): string {\n if (author.avatar === \"logo\") {\n const src = assets.mark ?? assets.logo;\n if (src) return `<div class=\"${cls} logo-av\"><img src=\"${src}\" alt=\"\"></div>`;\n } else if (author.avatar !== \"initials\") {\n const src = assets.photos[author.avatar];\n if (src) return `<div class=\"${cls}\"><img src=\"${src}\" alt=\"\"></div>`;\n }\n return `<div class=\"${cls} initials\" style=\"background:${tintOf(author.name)}\">${esc(initials(author.name))}</div>`;\n}\n\nconst ICON = {\n verifiedX:\n '<svg class=\"badge\" viewBox=\"0 0 22 22\"><path fill=\"#1d9bf0\" d=\"M20.4 11c0-1.3-.8-2.4-1.9-2.9.4-1.2.1-2.6-.8-3.5s-2.3-1.2-3.5-.8C13.7 2.6 12.4 1.8 11 1.8s-2.6.8-3.1 2c-1.2-.4-2.6-.1-3.5.8s-1.2 2.3-.8 3.5C2.4 8.6 1.6 9.7 1.6 11s.8 2.4 1.9 2.9c-.4 1.2-.1 2.6.8 3.5s2.3 1.2 3.5.8c.5 1.2 1.8 2 3.2 2s2.6-.8 3.1-2c1.2.4 2.6.1 3.5-.8s1.2-2.3.8-3.5c1.2-.5 2-1.6 2-2.9z\"/><path fill=\"#fff\" d=\"M9.6 14.6 6.4 11.4l1.3-1.3 1.9 1.9 4.6-4.6 1.3 1.3z\"/></svg>',\n verifiedWa:\n '<svg class=\"badge\" viewBox=\"0 0 22 22\"><path fill=\"#25d366\" d=\"M20.4 11c0-1.3-.8-2.4-1.9-2.9.4-1.2.1-2.6-.8-3.5s-2.3-1.2-3.5-.8C13.7 2.6 12.4 1.8 11 1.8s-2.6.8-3.1 2c-1.2-.4-2.6-.1-3.5.8s-1.2 2.3-.8 3.5C2.4 8.6 1.6 9.7 1.6 11s.8 2.4 1.9 2.9c-.4 1.2-.1 2.6.8 3.5s2.3 1.2 3.5.8c.5 1.2 1.8 2 3.2 2s2.6-.8 3.1-2c1.2.4 2.6.1 3.5-.8s1.2-2.3.8-3.5c1.2-.5 2-1.6 2-2.9z\"/><path fill=\"#fff\" d=\"M9.6 14.6 6.4 11.4l1.3-1.3 1.9 1.9 4.6-4.6 1.3 1.3z\"/></svg>',\n reply:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M1.8 10c0-4.4 3.6-8 8-8h4.4c4.5 0 8.1 3.6 8.1 8.1 0 3-1.6 5.6-4.2 7.1L10 21.7v-3.6H9.9C5.4 18.1 1.8 14.5 1.8 10zm8-6c-3.3 0-6 2.7-6 6 0 3.4 2.7 6.1 6.1 6.1H12v2.3l5.1-2.8c1.9-1.1 3.1-3.1 3.1-5.4 0-3.4-2.7-6.1-6.1-6.1H9.8z\"/></svg>',\n repost:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M4.5 3.9 8.9 8l-1.4 1.5-2-1.9V16c0 1.1.9 2 2 2H13v2H7.5c-2.2 0-4-1.8-4-4V7.6l-2 1.9L.1 8l4.4-4.1zM16.5 6H11V4h5.5c2.2 0 4 1.8 4 4v8.4l2-1.9 1.4 1.5-4.4 4.1-4.4-4.1 1.4-1.5 2 1.9V8c0-1.1-.9-2-2-2z\"/></svg>',\n like: '<svg viewBox=\"0 0 24 24\"><path d=\"M16.7 5.5c-1.1-.1-2.6.4-3.9 2.3l-.8 1.2-.8-1.2C9.9 5.9 8.4 5.4 7.3 5.5c-1.2.1-2.2.8-2.8 1.9-1.1 2.2-.6 5.4 3.1 8.9 1.2 1.1 2.7 2.3 4.4 3.5 1.7-1.2 3.2-2.4 4.4-3.5 3.7-3.5 4.2-6.7 3.1-8.9-.6-1.1-1.6-1.8-2.8-1.9zM12 21.4c-1.9-1.3-3.7-2.7-5.1-4-4-3.8-5.1-7.6-3.6-10.8.9-1.8 2.6-2.9 4.5-3.1 1.6-.1 3.1.5 4.2 1.7 1.1-1.2 2.6-1.8 4.2-1.7 1.9.2 3.6 1.3 4.5 3.1 1.5 3.2.4 7-3.6 10.8-1.4 1.3-3.2 2.7-5.1 4z\"/></svg>',\n views:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M8.8 20H6.8V4h2v16zm5-14h-2v14h2V6zm5 4h-2v10h2V10zm-15 4h-2v6h2v-6z\"/></svg>',\n share:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M12 2.6 17.4 8 16 9.4l-3-3V16h-2V6.4l-3 3L6.6 8 12 2.6zM21 15l-.1 3.5c0 1.4-1.1 2.5-2.5 2.5H5.5C4.1 21 3 19.8 3 18.5V15h2v3.5c0 .3.2.5.5.5h12.9c.3 0 .5-.2.5-.5L19 15h2z\"/></svg>',\n ticks:\n '<svg class=\"ticks\" viewBox=\"0 0 18 11\"><path fill=\"#53bdeb\" d=\"M12.1.6 5.3 7.4 2.4 4.5 1.3 5.6l4 4 7.9-7.9zM16.6.6 9.8 7.4l-.9-.9-1.1 1.1 2 2 7.9-7.9z\"/></svg>',\n back: '<svg class=\"back\" viewBox=\"0 0 12 20\"><path d=\"M10 2 2 10l8 8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n video:\n '<svg viewBox=\"0 0 26 18\"><rect x=\"1\" y=\"2\" width=\"16\" height=\"14\" rx=\"3.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"/><path d=\"m18 7 6-4v12l-6-4z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linejoin=\"round\"/></svg>',\n phone:\n '<svg viewBox=\"0 0 22 22\"><path d=\"M6.6 2.5 8.9 6c.4.6.3 1.3-.2 1.8L7.3 9.1c1.1 2.4 3.1 4.4 5.6 5.6l1.3-1.4c.5-.5 1.2-.6 1.8-.2l3.5 2.3c.6.4.8 1.2.5 1.8-.8 1.7-2.5 2.6-4.3 2.3C9.8 18.6 3.4 12.2 2.5 6.3c-.3-1.8.6-3.5 2.3-4.3.6-.3 1.4-.1 1.8.5z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\"/></svg>',\n star: '<svg viewBox=\"0 0 24 24\"><path fill=\"#fff\" d=\"m12 3.6 2.5 5.9 6.3.5-4.8 4.1 1.5 6.2L12 17l-5.5 3.3L8 14.1 3.2 10l6.3-.5z\"/></svg>',\n check:\n '<svg viewBox=\"0 0 24 24\"><path d=\"m6 12.5 4 4 8-9\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n\n home: '<svg viewBox=\"0 0 24 24\"><path d=\"M3 10.5 12 3l9 7.5V21h-6v-6H9v6H3z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n search:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M10.5 3a7.5 7.5 0 1 1 0 15 7.5 7.5 0 0 1 0-15zM16 16l5 5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n bell: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 17V11a6 6 0 0 1 12 0v6l1.5 2h-15zM10 21h4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n mail: '<svg viewBox=\"0 0 24 24\"><path d=\"M3 6h18v12H3zM3 6l9 7 9-7\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n tick: '<svg viewBox=\"0 0 24 24\"><path d=\"m5 12.5 4.5 4.5L19 7.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n checklist:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2M4 12h2M4 18h2M9 6h11M9 12h11M9 18h11\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n camera:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M3 8h4l2-3h6l2 3h4v11H3zM12 10a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n pen: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 20l4-1L19 8l-3-3L5 16zM14 7l3 3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n compose:\n '<svg viewBox=\"0 0 24 24\"><path d=\"M4 4h9M4 4v16h16v-9M20 4l-9 9-2 .5.5-2z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.9\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>',\n};\n\n/** A screen in three parts: the app's bars at the top, what it shows, and its bars at the bottom. */\ntype ScreenParts = {\n /** The classes the app's own styles hang on (\"surface x dark\", \"screen whatsapp\"). */\n cls: string;\n top: string;\n body: string;\n bottom: string;\n /** A chat's messages sit on its input bar; a feed, a note, a channel start under the header. */\n align: \"top\" | \"bottom\";\n};\n\nconst STATUS_BAR =\n '<div class=\"status-bar\"><span>9:41</span><span class=\"bars\"><i></i><i></i><i></i><i></i></span></div>';\n\nfunction xPosts(surface: Extract<NativeSurface, { surface: \"x_post\" }>, assets: NativeAssets, t: Words): string {\n return surface.posts\n .map((post, i) => {\n const last = i === surface.posts.length - 1;\n const name = `<span class=\"name\">${esc(post.author.name)}</span>${post.author.verified ? ICON.verifiedX : \"\"}`;\n const meta = [post.author.handle, post.time].filter(Boolean).map((s) => esc(s as string));\n const photo = post.photo && assets.photos[post.photo];\n const ratio = surface.photos.find((p) => p.id === post.photo)?.ratio ?? \"4:3\";\n const stats = post.stats ?? {};\n const action = (icon: string, n?: string) => `<span class=\"act\">${icon}<span>${n ? esc(n) : \"\"}</span></span>`;\n return `<article class=\"post${last ? \"\" : \" threaded\"}\">\n <div class=\"rail\">${avatarHtml(post.author, assets, \"av\")}${last ? \"\" : '<div class=\"thread-line\"></div>'}</div>\n <div class=\"body\">\n <div class=\"head\">${name}<span class=\"meta\">${meta.join(\" · \")}</span><span class=\"more\">···</span></div>\n ${post.reply_to ? `<div class=\"replying\">${esc(t.replying)} <span class=\"link\">${esc(post.reply_to)}</span></div>` : \"\"}\n <div class=\"text\">${linked(post.text)}</div>\n ${photo ? `<div class=\"media\" style=\"aspect-ratio:${ratio.replace(\":\", \"/\")}\"><img src=\"${photo}\" alt=\"\"></div>` : \"\"}\n <div class=\"actions\">${action(ICON.reply, stats.replies)}${action(ICON.repost, stats.reposts)}${action(ICON.like, stats.likes)}${action(ICON.views, stats.views)}${action(ICON.share)}</div>\n </div>\n</article>`;\n })\n .join(\"\");\n}\n\nfunction xParts(surface: Extract<NativeSurface, { surface: \"x_post\" }>, assets: NativeAssets, t: Words): ScreenParts {\n const tabs = [ICON.home, ICON.search, ICON.bell, ICON.mail].map((icon) => `<span>${icon}</span>`).join(\"\");\n return {\n cls: `surface x ${surface.theme}`,\n top: `${STATUS_BAR}<div class=\"x-head\">${ICON.back}<span>${esc(t.post)}</span></div>`,\n body: xPosts(surface, assets, t),\n bottom: `<div class=\"x-reply\"><span class=\"x-reply-av\"></span><span>${esc(t.replyPrompt)}</span></div><div class=\"tabbar\">${tabs}</div><div class=\"home-bar\"></div>`,\n align: \"top\",\n };\n}\n\ntype ChatMessage = Extract<NativeSurface, { surface: \"whatsapp\" | \"imessage\" }>[\"messages\"][number];\n\n/** One message bubble: a card (its photo, title and price), the text with its time, the card's button. */\nfunction bubbleHtml(m: ChatMessage, assets: NativeAssets, wa: boolean): string {\n const photo = m.card?.photo ? assets.photos[m.card.photo] : undefined;\n const sub = m.card?.subtitle ? `<div class=\"card-sub\">${esc(m.card.subtitle)}</div>` : \"\";\n const card = m.card\n ? `<div class=\"card\">${photo ? `<img src=\"${photo}\" alt=\"\">` : \"\"}<div class=\"card-title\">${esc(m.card.title)}</div>${sub}</div>`\n : \"\";\n const ticks = wa && m.from === \"me\" ? ICON.ticks : \"\";\n const time = m.time ? `<span class=\"time\">${esc(m.time)}${ticks}</span>` : \"\";\n const text = m.text ? `<div class=\"msg-text\">${linked(m.text)}${time}</div>` : time;\n const button = m.card?.button ? `<div class=\"card-button\">${esc(m.card.button)}</div>` : \"\";\n const copy = [m.text, m.card?.title, m.card?.subtitle, m.card?.button].filter((t): t is string => Boolean(t?.trim()));\n return `<div class=\"bubble ${m.from}\" data-copy=\"${esc(JSON.stringify(copy))}\">${card}${text}${button}</div>`;\n}\n\n/** The chat's header: who it is with, as WhatsApp or iMessage shows them. */\nfunction chatHeader(contact: NativeAuthor, assets: NativeAssets, t: Words, wa: boolean, verified: boolean): string {\n if (!wa) {\n return `<div class=\"im-head\">${ICON.back}<div class=\"who\">${avatarHtml(contact, assets, \"av\")}<div class=\"name\">${esc(contact.name)}</div></div></div>`;\n }\n const status = contact.subtitle ?? (verified ? t.business : t.online);\n const badge = verified ? ICON.verifiedWa : \"\";\n return `<div class=\"wa-head\">${ICON.back}${avatarHtml(contact, assets, \"av\")}<div class=\"who\"><div class=\"name\">${esc(contact.name)}${badge}</div><div class=\"status\">${esc(status)}</div></div><div class=\"icons\">${ICON.video}${ICON.phone}</div></div>`;\n}\n\nfunction chatParts(\n surface: Extract<NativeSurface, { surface: \"whatsapp\" | \"imessage\" }>,\n assets: NativeAssets,\n t: Words,\n): ScreenParts {\n const wa = surface.surface === \"whatsapp\";\n const bubbles = surface.messages.map((m) => bubbleHtml(m, assets, wa)).join(\"\");\n const input = wa\n ? '<div class=\"wa-input\"><span class=\"plus\">+</span><span class=\"field\"></span><span class=\"mic\"></span></div>'\n : '<div class=\"im-input\"><span class=\"plus\">+</span><span class=\"field\"></span></div>';\n return {\n cls: `screen ${surface.surface}`,\n top: `${STATUS_BAR}${chatHeader(surface.contact, assets, t, wa, surface.contact.verified === true)}`,\n body: `<div class=\"messages\"><div class=\"day\">${esc(surface.day ?? t.today)}</div>${bubbles}</div>`,\n bottom: input,\n align: \"bottom\",\n };\n}\n\nfunction chatHtml(\n surface: Extract<NativeSurface, { surface: \"whatsapp\" | \"imessage\" }>,\n assets: NativeAssets,\n t: Words,\n): string {\n const parts = chatParts(surface, assets, t);\n const screen = `<div class=\"${parts.cls}\">${parts.top}${parts.body}${parts.bottom}</div>`;\n return `<div class=\"surface chat${surface.device ? \" device\" : \"\"}\">${screen}</div>`;\n}\n\nfunction starsHtml(kind: \"trustpilot\" | \"google_reviews\", stars: number, size: \"s\" | \"l\" = \"s\"): string {\n if (kind === \"google_reviews\") {\n return `<span class=\"g-stars ${size}\">${\"★\".repeat(stars)}<span class=\"off\">${\"★\".repeat(5 - stars)}</span></span>`;\n }\n const cells = Array.from(\n { length: 5 },\n (_, i) => `<span class=\"tp-star${i < stars ? \"\" : \" off\"}\">${ICON.star}</span>`,\n );\n return `<span class=\"tp-stars ${size}\">${cells.join(\"\")}</span>`;\n}\n\ntype Review = Extract<NativeSurface, { surface: \"trustpilot\" | \"google_reviews\" }>[\"reviews\"][number];\n\nfunction reviewCard(\n r: Review,\n kind: \"trustpilot\" | \"google_reviews\",\n assets: NativeAssets,\n t: Words,\n cls: string,\n style: string,\n): string {\n const sub = r.author.subtitle ? `<div class=\"r-sub\">${esc(r.author.subtitle)}</div>` : \"\";\n const verified = kind === \"trustpilot\" ? `<span class=\"r-verified\">${ICON.tick} ${esc(t.verified)}</span>` : \"\";\n return `<div class=\"${cls}\" style=\"${style}\"><div class=\"r-head\">${avatarHtml(r.author, assets, \"av\")}<div><div class=\"r-name\">${esc(r.author.name)}</div>${sub}</div></div><div class=\"r-stars\">${starsHtml(kind, r.stars)}${verified}${r.date ? `<span class=\"r-date\">${esc(r.date)}</span>` : \"\"}</div>${r.title ? `<div class=\"r-title\">${esc(r.title)}</div>` : \"\"}<div class=\"r-text\">${esc(shownReview(r.text))}</div></div>`;\n}\n\nfunction platformMark(kind: \"trustpilot\" | \"google_reviews\"): string {\n return kind === \"trustpilot\"\n ? `<span class=\"tp-mark\"><span class=\"tp-glyph\">${ICON.star}</span>Trustpilot</span>`\n : `<span class=\"g-mark\"><b style=\"color:#4285f4\">G</b><b style=\"color:#ea4335\">o</b><b style=\"color:#fbbc05\">o</b><b style=\"color:#4285f4\">g</b><b style=\"color:#34a853\">l</b><b style=\"color:#ea4335\">e</b></span>`;\n}\n\n/** The three reviews read as copy, the brand's badge between them, the platform under them. */\nfunction reviewsHtml(\n surface: Extract<NativeSurface, { surface: \"trustpilot\" | \"google_reviews\" }>,\n assets: NativeAssets,\n t: Words,\n brandName: string | undefined,\n): string {\n const kind = surface.surface;\n const platform = platformMark(kind);\n const summary = surface.summary\n ? `<div class=\"summary\">${assets.logo ? `<img class=\"sum-logo${inked(assets, assets.logo, false)}\" src=\"${assets.logo}\" alt=\"\">` : brandName ? `<div class=\"sum-name\">${esc(brandName)}</div>` : \"\"}<div class=\"sum-row\"><span class=\"sum-label\">${esc(surface.summary.label ?? t.excellent)}</span>${starsHtml(kind, 5, \"l\")}</div><div class=\"sum-count\">${esc(surface.summary.score)} · ${esc(surface.summary.count)} ${esc(t.reviewsWord)} ${platform}</div></div>`\n : \"\";\n // Scattered, as the reference walls are: each card turned and pushed off the column.\n const tilt = [-2.5, 2.2, -1.8, 2.8, -2.4, 1.6];\n const nudge = [-22, 22, -16, 12, -16, 10];\n const cards = surface.reviews\n .slice(0, FEATURED_REVIEWS)\n .map((r, i) =>\n reviewCard(\n r,\n kind,\n assets,\n t,\n \"review\",\n `transform:translateX(${nudge[i % nudge.length]}px) rotate(${tilt[i % tilt.length]}deg)`,\n ),\n );\n const footer = surface.summary ? \"\" : `<div class=\"platform-foot\">${platform}</div>`;\n // The brand's logo as the pile's anchor, in the gap between two cards so it covers no review.\n const anchor =\n surface.frame.logo && surface.frame.logo_position === \"over\" && assets.logo\n ? `<div class=\"wall-badge\"><img class=\"${inked(assets, assets.logo, false).trim()}\" src=\"${assets.logo}\" alt=\"\"></div>`\n : \"\";\n const at = Math.ceil(cards.length / 2);\n const pile = anchor ? [...cards.slice(0, at), anchor, ...cards.slice(at)].join(\"\") : cards.join(\"\");\n return `<div class=\"surface reviews ${kind}\">${summary}<div class=\"wall\">${pile}</div>${footer}</div>`;\n}\n\n/**\n * Every review after the featured ones, as the wall under them: cards as large and as sharp as those,\n * turned every way, across the whole frame and cut by its edges, as the walls that win are. Placed by\n * the page's script.\n */\nfunction reviewPile(\n surface: Extract<NativeSurface, { surface: \"trustpilot\" | \"google_reviews\" }>,\n assets: NativeAssets,\n t: Words,\n): string {\n return surface.reviews\n .slice(FEATURED_REVIEWS)\n .map((r) => reviewCard(r, surface.surface, assets, t, \"review back\", \"\"))\n .join(\"\");\n}\n\nfunction notesParts(surface: Extract<NativeSurface, { surface: \"notes\" }>, t: Words): ScreenParts {\n const items = surface.items\n .map(\n (i) =>\n `<div class=\"item${i.checked ? \" checked\" : \"\"}\"><span class=\"circle\">${i.checked ? ICON.check : \"\"}</span><span>${esc(i.text)}</span></div>`,\n )\n .join(\"\");\n const tools = [ICON.checklist, ICON.camera, ICON.pen, ICON.compose].map((icon) => `<span>${icon}</span>`).join(\"\");\n return {\n cls: \"surface notes\",\n top: `${STATUS_BAR}<div class=\"n-head\"><span class=\"n-back\">${ICON.back}${esc(t.notes)}</span><span class=\"n-icons\"><i></i><i></i></span></div>`,\n body: `<h1>${esc(surface.title)}</h1>${items}${surface.body ? `<p class=\"n-body\">${esc(surface.body)}</p>` : \"\"}`,\n bottom: `<div class=\"n-tools\">${tools}</div><div class=\"home-bar\"></div>`,\n align: \"top\",\n };\n}\n\nfunction notesHtml(surface: Extract<NativeSurface, { surface: \"notes\" }>, t: Words): string {\n const parts = notesParts(surface, t);\n return `<div class=\"${parts.cls}\">${parts.top.replace(STATUS_BAR, \"\")}${parts.body}</div>`;\n}\n\nfunction slackParts(\n surface: Extract<NativeSurface, { surface: \"slack\" }>,\n assets: NativeAssets,\n t: Words,\n): ScreenParts {\n const channel = surface.channel.replace(/^#/, \"\");\n const messages = surface.messages\n .map((m) => {\n const reactions = m.reactions.map((r) => `<span class=\"reaction\">${esc(r.emoji)} ${r.count}</span>`).join(\"\");\n return `<div class=\"s-msg\">${avatarHtml(m.author, assets, \"av\")}<div class=\"s-body\"><div class=\"s-head\"><span class=\"name\">${esc(m.author.name)}</span>${m.time ? `<span class=\"s-time\">${esc(m.time)}</span>` : \"\"}</div><div class=\"s-text\">${linked(m.text)}</div>${reactions ? `<div class=\"reactions\">${reactions}</div>` : \"\"}${m.replies ? `<div class=\"s-replies\">${esc(m.replies)}</div>` : \"\"}</div></div>`;\n })\n .join(\"\");\n return {\n cls: \"surface slack\",\n top: `${STATUS_BAR}<div class=\"s-channel\"># ${esc(channel)}</div>`,\n body: messages,\n bottom: `<div class=\"s-compose\"><span>${esc(t.messagePrompt)} #${esc(channel)}</span></div><div class=\"home-bar\"></div>`,\n align: \"top\",\n };\n}\n\nfunction slackHtml(surface: Extract<NativeSurface, { surface: \"slack\" }>, assets: NativeAssets, t: Words): string {\n const parts = slackParts(surface, assets, t);\n return `<div class=\"${parts.cls}\">${parts.top.replace(STATUS_BAR, \"\")}${parts.body}</div>`;\n}\n\n/** The colour behind the screen: the platform's own for a screenshot, the brand's ground around a phone or a wall. */\nexport function backdrop(surface: NativeSurface, brand: NativeFrameOpts[\"brand\"]): string {\n if (surface.frame.background) return surface.frame.background;\n switch (surface.surface) {\n case \"x_post\":\n return surface.theme === \"dark\" ? \"#000000\" : \"#ffffff\";\n case \"notes\":\n case \"slack\":\n return \"#ffffff\";\n case \"trustpilot\":\n case \"google_reviews\":\n return brand.ground ?? \"#f4f3ee\";\n default:\n return brand.ground ?? \"#1f2a44\";\n }\n}\n\n/** Whether a colour is dark enough to need light type on it. */\nfunction isDark(hex: string): boolean {\n const n = Number.parseInt(hex.slice(1), 16);\n const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n return 0.2126 * r + 0.7152 * g + 0.0722 * b < 140;\n}\n\nconst STYLE = `\n*{box-sizing:border-box;margin:0;padding:0}\nhtml,body{width:100%;height:100%;overflow:hidden}\nbody{font-family:Inter,\"Noto Sans\",\"Liberation Sans\",Arial,sans-serif,\"Noto Color Emoji\";-webkit-font-smoothing:antialiased}\n#box{position:absolute;display:flex;align-items:center;justify-content:center}\n#stack{display:flex;flex-direction:column;align-items:center;gap:18px;transform-origin:center center;width:390px}\n.headline{font-family:BrandFont,Inter,sans-serif;font-weight:800;font-size:30px;line-height:1.08;letter-spacing:-.02em;text-align:center;width:100%}\n.caption{font-family:Inter,sans-serif;font-weight:600;font-size:17px;line-height:1.3;text-align:center;width:100%}\n.frame-logo{height:30px;max-width:180px;object-fit:contain}\n.inked{filter:brightness(0)}\n.over{position:relative}\n.logo-badge{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%) rotate(-4deg);background:#fff;border-radius:18px;padding:16px 26px;box-shadow:0 14px 40px rgba(0,0,0,.35);z-index:5}\n.logo-badge img{height:54px;max-width:240px;object-fit:contain}\nimg{display:block}\n.av{width:40px;height:40px;border-radius:50%;overflow:hidden;flex:none;background:#ddd}\n.av img{width:100%;height:100%;object-fit:cover}\n.av.logo-av{background:#fff;border:1px solid rgba(0,0,0,.08);display:flex;align-items:center;justify-content:center}\n.av.logo-av img{width:78%;height:78%;object-fit:contain}\n.av.initials{display:flex;align-items:center;justify-content:center;font-weight:600;font-size:15px;color:#2b2b2b}\n.badge{width:18px;height:18px;flex:none;margin-left:2px}\n.link{color:#1d9bf0}\n/* X */\n.surface.x{width:390px;background:#fff;color:#0f1419;padding:4px 0}\n.x.dark{background:#000;color:#e7e9ea}\n.post{display:flex;gap:10px;padding:12px 16px 4px}\n.post .rail{display:flex;flex-direction:column;align-items:center}\n.thread-line{width:2px;flex:1;background:#cfd9de;margin-top:4px}\n.x.dark .thread-line{background:#333639}\n.post .body{flex:1;min-width:0}\n.post .head{display:flex;align-items:center;gap:3px;font-size:15px;line-height:20px;white-space:nowrap}\n.post .name{font-weight:700;flex:none}\n.post .meta{color:#536471;margin-left:3px;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto}\n.post .replying{color:#536471;font-size:15px;line-height:20px}.x.dark .replying{color:#71767b}\n.x.dark .meta,.x.dark .actions{color:#71767b}\n.post .more{margin-left:auto;color:#536471;letter-spacing:1px}\n.post .text{font-size:15px;line-height:20px;white-space:pre-wrap;overflow-wrap:break-word;margin-top:2px}\n.post .media{margin-top:12px;border-radius:16px;overflow:hidden;border:1px solid #cfd9de}\n.x.dark .media{border-color:#2f3336}\n.post .media img{width:100%;height:100%;object-fit:cover}\n.actions{display:flex;justify-content:space-between;color:#536471;font-size:13px;margin:10px 0 8px;max-width:340px}\n.act{display:flex;align-items:center;gap:4px}\n.act svg{width:18px;height:18px;fill:currentColor}\n/* Chats */\n.surface.chat{width:390px}\n.chat.device{padding:11px;border-radius:52px;background:#111;box-shadow:0 18px 50px rgba(0,0,0,.35)}\n.screen{border-radius:42px;overflow:hidden;display:flex;flex-direction:column;background:#efeae2}\n.chat:not(.device) .screen{border-radius:18px}\n.status-bar{display:flex;justify-content:space-between;align-items:center;padding:12px 28px 6px;font-weight:600;font-size:15px}\n.bars{display:flex;gap:2px;align-items:flex-end}.bars i{display:block;width:3px;background:currentColor;border-radius:1px}\n.bars i:nth-child(1){height:4px}.bars i:nth-child(2){height:6px}.bars i:nth-child(3){height:8px}.bars i:nth-child(4){height:10px}\n.whatsapp .status-bar,.whatsapp .wa-head{background:#f6f5f3;color:#111b21}\n.wa-head{display:flex;align-items:center;gap:8px;padding:6px 12px 10px;border-bottom:1px solid #e0ddd8}\n.wa-head .back{width:11px;height:19px;color:#007aff;margin-right:2px}\n.wa-head .av{width:36px;height:36px}\n.wa-head .who{flex:1;min-width:0}\n.wa-head .name{display:flex;align-items:center;gap:3px;font-weight:600;font-size:16px}\n.wa-head .status{font-size:12.5px;color:#667781}\n.wa-head .icons{display:flex;gap:20px;color:#007aff}.wa-head .icons svg{width:24px;height:20px}\n.whatsapp .messages{background-color:#efeae2;background-image:radial-gradient(rgba(0,0,0,.035) 1.2px,transparent 1.3px),radial-gradient(rgba(0,0,0,.025) 1px,transparent 1.1px);background-size:22px 22px,13px 13px;background-position:0 0,7px 9px}\n.messages{display:flex;flex-direction:column;gap:6px;padding:10px 12px 14px}\n.day{align-self:center;background:#fff;color:#54656f;font-size:12.5px;padding:4px 12px;border-radius:8px;margin:2px 0 6px;box-shadow:0 1px .5px rgba(11,20,26,.13)}\n.bubble{max-width:80%;border-radius:10px;padding:6px 8px 6px 9px;font-size:16px;line-height:21px;color:#111b21;box-shadow:0 1px .5px rgba(11,20,26,.13);overflow:hidden}\n.bubble.them{align-self:flex-start;background:#fff;border-top-left-radius:2px}\n.bubble.me{align-self:flex-end;background:#d9fdd3;border-top-right-radius:2px}\n.msg-text{white-space:pre-wrap;overflow-wrap:break-word}\n.time{float:right;font-size:11.5px;color:#667781;margin:8px 0 -4px 10px;display:inline-flex;align-items:center;gap:3px}\n.ticks{width:17px;height:11px}\n.card{margin:-3px -5px 6px -6px;border-radius:7px;overflow:hidden;background:rgba(0,0,0,.035)}\n.card img{width:100%;aspect-ratio:16/10;object-fit:cover;background:#fff}\n.card-title{font-weight:600;padding:7px 9px 0}\n.card-sub{color:#111b21;font-weight:600;font-size:16px;padding:2px 9px 8px}\n.card-button{margin:6px -8px -6px -9px;border-top:1px solid rgba(0,0,0,.08);text-align:center;color:#027eb5;font-weight:500;padding:10px 0 9px;font-size:15.5px}\n.wa-input{display:flex;align-items:center;gap:10px;background:#f6f5f3;padding:8px 12px 26px;color:#007aff;font-size:28px;font-weight:300}\n.wa-input .field,.im-input .field{flex:1;height:34px;border-radius:17px;background:#fff;border:1px solid #e0ddd8}\n.wa-input .mic{width:16px;height:24px;border:2.5px solid #007aff;border-radius:9px}\n.imessage .screen,.screen.imessage{background:#fff}\n.imessage .status-bar{color:#000}\n.im-head{display:flex;align-items:flex-start;padding:2px 14px 10px;background:#f7f7f7;border-bottom:1px solid #e5e5ea;color:#007aff}\n.im-head .back{width:11px;height:19px;margin-top:14px}\n.im-head .who{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;margin-right:11px}\n.im-head .av{width:50px;height:50px}\n.im-head .name{color:#000;font-size:12px}\n.screen.imessage .messages{background:#fff}\n.screen.imessage .day{background:none;box-shadow:none;color:#8e8e93;font-size:12px}\n.screen.imessage .bubble{box-shadow:none;border-radius:19px;padding:8px 13px}\n.screen.imessage .bubble.them{background:#e9e9eb;color:#000}\n.screen.imessage .bubble.me{background:#0b84fe;color:#fff}\n.screen.imessage .time{display:none}\n.screen.imessage .card-button{color:inherit}\n.im-input{display:flex;align-items:center;gap:10px;padding:8px 12px 26px;color:#8e8e93;font-size:26px}\n/* Reviews */\n.surface.reviews{width:100%;display:flex;flex-direction:column;gap:14px}\n.summary{background:#fff;border-radius:14px;padding:14px 16px;display:flex;flex-direction:column;align-items:center;gap:6px;box-shadow:0 6px 18px rgba(0,0,0,.12)}\n.sum-logo{height:34px;max-width:200px;object-fit:contain}\n.sum-name{font-weight:800;font-size:22px}\n.sum-row{display:flex;align-items:center;gap:10px}\n.sum-label{font-weight:700;font-size:20px;color:#191919}\n.sum-count{font-size:13px;color:#555;display:flex;align-items:center;gap:6px}\n.wall{display:flex;flex-direction:column;gap:0}.wall .review+.review{margin-top:-4px}\n.wall-badge{align-self:center;position:relative;z-index:5;margin:2px 0;background:#fff;border-radius:22px;padding:16px 30px;box-shadow:0 16px 44px rgba(0,0,0,.38);transform:rotate(-3deg)}\n.wall-badge img{height:64px;max-width:280px;object-fit:contain}\n.wide #stack{width:620px}.wide .surface.reviews{width:620px}.wide .wall{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px;align-items:start}.wide .wall .review+.review{margin-top:0}.wide .wall-badge{grid-column:1/-1;justify-self:center;margin:-22px 0}\n.review{position:relative;background:#fff;border-radius:10px;padding:14px 16px 16px;box-shadow:0 6px 18px rgba(0,0,0,.14);color:#191919}\n.r-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}\n.r-name{font-weight:600;font-size:15px}\n.r-sub{font-size:12.5px;color:#6c6c85}\n.r-stars{display:flex;align-items:center;gap:10px;margin-bottom:8px}\n.r-verified{font-size:12px;color:#6c6c85}\n.r-date{margin-left:auto;font-size:12px;color:#6c6c85}\n.r-title{font-weight:700;font-size:16px;margin-bottom:4px}\n.r-text{font-size:14.5px;line-height:1.42}\n.tp-stars{display:inline-flex;gap:2px}\n.tp-star{width:20px;height:20px;background:#00b67a;display:inline-flex;align-items:center;justify-content:center}\n.tp-stars.l .tp-star{width:28px;height:28px}\n.tp-star svg{width:82%;height:82%}\n.tp-star.off{background:#dcdce6}\n.tp-mark{font-weight:700;color:#191919;display:inline-flex;align-items:center;gap:2px}\n.g-stars{color:#fbbc04;font-size:17px;letter-spacing:1px}.g-stars.l{font-size:26px}.g-stars .off{color:#dadce0}\n.g-mark{font-weight:700;font-size:15px;letter-spacing:-.5px}\n.platform-foot{align-self:center;background:#fff;border-radius:999px;padding:6px 14px;font-size:15px}\n/* Notes */\n.surface.notes{width:390px;background:#fff;color:#000;padding:8px 20px 22px}\n.n-head{display:flex;justify-content:space-between;align-items:center;color:#e0a100;font-size:17px;padding:6px 0 14px}\n.n-back{display:flex;align-items:center;gap:6px}.n-back svg{width:11px;height:19px}\n.n-icons{display:flex;gap:16px}.n-icons i{display:block;width:24px;height:24px;border:2px solid #e0a100;border-radius:50%}\n.notes h1{font-size:28px;line-height:1.15;font-weight:700;margin-bottom:14px}\n.item{display:flex;gap:12px;align-items:flex-start;font-size:17px;line-height:1.35;margin-bottom:12px}\n.circle{width:24px;height:24px;border-radius:50%;border:1.6px solid #c7c7cc;flex:none;display:flex;align-items:center;justify-content:center;margin-top:-1px}\n.item.checked .circle{background:#e0a100;border-color:#e0a100}\n.circle svg{width:16px;height:16px}\n.n-body{font-size:17px;line-height:1.4;margin-top:6px}\n/* Slack */\n.surface.slack{width:390px;background:#fff;color:#1d1c1d;padding-bottom:10px}\n.s-channel{font-weight:800;font-size:17px;padding:12px 16px;border-bottom:1px solid #e8e8e8}\n.s-msg{display:flex;gap:10px;padding:10px 16px 4px}\n.slack .av{border-radius:8px;width:38px;height:38px}\n.s-body{flex:1;min-width:0}\n.s-head{display:flex;align-items:baseline;gap:8px}\n.s-head .name{font-weight:800;font-size:15px}\n.s-time{font-size:12px;color:#616061}\n.s-text{font-size:15px;line-height:1.46;white-space:pre-wrap}\n.slack .link{color:#1264a3;background:rgba(29,155,209,.1);border-radius:3px;padding:0 2px}\n.reactions{display:flex;gap:6px;margin-top:6px}\n.reaction{border:1px solid #1d9bd1;background:#e8f5fa;border-radius:12px;padding:1px 8px;font-size:13px;color:#1264a3}\n.s-replies{color:#1264a3;font-weight:700;font-size:13px;margin-top:6px}\n/* A 9:16: the app full screen */\n.fs-app{position:absolute;inset:0;overflow:hidden}\n.fs-app.surface{width:auto;padding:0}\n.fs-app.screen{border-radius:0;display:block}\n.fs-app.surface.notes,.fs-app.surface.slack{background:#fff}\n#top{position:absolute;left:0;right:0;top:0;z-index:3}\n#bottom{position:absolute;left:0;right:0;bottom:0;z-index:3}\n#top>div,#bottom>div{width:390px}\n#banner{position:absolute;left:0;right:0;z-index:4}\n#banner>div{width:390px}\n.fs-card{margin:0 14px;border-radius:16px;padding:11px 14px;display:flex;flex-direction:column;align-items:center;gap:6px;box-shadow:0 10px 28px rgba(0,0,0,.22)}\n.fs-card .headline{font-size:18px}.fs-card .caption{font-size:11.5px}.fs-card .frame-logo{height:17px}\n.fs #stack{position:absolute;left:0;top:0;transform-origin:top left;display:block;gap:0}\n.fs .surface.notes h1,.fs .item,.fs .n-body{margin-left:20px;margin-right:20px}\n.fs-app.notes #stack{padding-top:6px}\n.fs-app.screen.whatsapp{background-color:#efeae2;background-image:radial-gradient(rgba(0,0,0,.035) 1.2px,transparent 1.3px),radial-gradient(rgba(0,0,0,.025) 1px,transparent 1.1px);background-size:30px 30px,18px 18px;background-position:0 0,9px 12px}\n.fs-app.whatsapp .messages{background:none}\n.fs-app .messages{padding:10px 12px 12px}\n.fs-app .bubble{max-width:min(80%,320px)}.fs-app .bubble:has(.card){max-width:min(66%,240px)}.fs-app .card img{aspect-ratio:3/2}\n.fs .post .media{aspect-ratio:16/9!important}\n.fs .status-bar{padding:14px 30px 8px}\n.x .status-bar{color:inherit}\n.x-head{display:flex;align-items:center;gap:26px;padding:8px 16px 12px;font-weight:700;font-size:19px;border-bottom:1px solid #eff3f4}\n.x.dark .x-head{border-color:#2f3336}\n.x-head .back{width:12px;height:20px}\n.x-reply{display:flex;align-items:center;gap:12px;padding:10px 16px;border-top:1px solid #eff3f4;color:#536471;font-size:16px}\n.x.dark .x-reply{border-color:#2f3336;color:#71767b}\n.x-reply-av{width:30px;height:30px;border-radius:50%;background:#cfd9de}.x.dark .x-reply-av{background:#333639}\n.x.light #top,.x.light #bottom{background:#fff}.x.dark #top,.x.dark #bottom{background:#000}\n.tabbar{display:flex;justify-content:space-around;padding:10px 8px 4px;border-top:1px solid #eff3f4}\n.x.dark .tabbar{border-color:#2f3336}\n.tabbar svg{width:27px;height:27px}\n.home-bar{width:134px;height:5px;border-radius:3px;background:currentColor;opacity:.85;margin:10px auto 8px}\n.screen.whatsapp .wa-input{padding-bottom:8px}\n.screen.imessage .im-input{padding-bottom:8px;background:#fff}\n#bottom .wa-input,#bottom .im-input{border-top:1px solid #e0ddd8}\n.n-tools{display:flex;justify-content:space-between;padding:10px 26px 0;color:#e0a100;background:#fff}.n-tools svg{width:25px;height:25px}\n.fs-app.notes #top,.fs-app.notes #bottom{background:#fff;color:#000}\n.fs-app.notes .n-head{padding:6px 20px 10px}\n.s-compose{margin:8px 12px 0;border:1px solid #c9c9c9;border-radius:10px;padding:12px 14px;color:#868686;font-size:15px;background:#fff}\n.fs-app.slack #top,.fs-app.slack #bottom{background:#fff;color:#1d1c1d}\n/* A wall's pile */\n#backdrop{position:absolute;inset:0;overflow:hidden}\n.review.back{position:absolute;width:390px;box-shadow:0 4px 14px rgba(0,0,0,.12)}\n#backdrop .wash{position:absolute;inset:0;background:var(--ground);opacity:.35}\n.wall-card{width:100%;border-radius:16px;padding:12px 16px;box-shadow:0 12px 32px rgba(0,0,0,.3);position:relative;z-index:6}\n#box{z-index:2}\n.tp-glyph{display:inline-flex;width:18px;height:18px;background:#00b67a;align-items:center;justify-content:center;margin-right:3px}.tp-glyph svg{width:80%;height:80%}\n.r-verified{display:inline-flex;align-items:center;gap:3px}.r-verified svg{width:12px;height:12px}\n`;\n\n/** A CSS length to a tenth of a pixel. */\nconst px = (n: number): number => Math.round(n * 10) / 10;\n\n/** The width a phone lays an app out at, in CSS pixels: a 9:16 draws its bars at the frame's width over it. */\nconst PHONE_WIDTH = 390;\n\n/** The script that waits for the fonts and pictures, so every page measures what it draws. */\nconst READY = `\n await Promise.all([...document.images].map((i) => (i.complete ? null : i.decode().catch(() => null))));\n if (document.fonts) await document.fonts.ready;\n // A square mark set where a wordmark goes reads as a speck at a wordmark's height.\n for (const img of document.querySelectorAll(\".frame-logo\")) {\n if (img.naturalWidth > 0 && img.naturalWidth < img.naturalHeight * 1.6) img.style.height = img.offsetHeight * 1.8 + \"px\";\n }\n`;\n\n/** The script that fits the stack in its box once the fonts and pictures are in, then marks the page ready. */\nconst FIT = `\n(async () => {${READY}\n const box = document.getElementById(\"box\");\n const stack = document.getElementById(\"stack\");\n const pile = document.getElementById(\"backdrop\");\n const fit = () => Math.min(box.clientWidth / stack.offsetWidth, box.clientHeight / stack.offsetHeight);\n let scale = fit();\n // A wall in a box squarer than its column is laid out wider, up to a third, so its cards run\n // shorter and read larger.\n if (pile && !document.body.classList.contains(\"wide\")) {\n const base = stack.offsetWidth;\n let best = { width: base, scale };\n for (const grow of [1.1, 1.2, 1.33]) {\n stack.style.width = base * grow + \"px\";\n const s = fit();\n if (s > best.scale) best = { width: base * grow, scale: s };\n }\n stack.style.width = best.width + \"px\";\n scale = fit();\n }\n stack.style.transform = \"scale(\" + scale + \")\";\n if (pile) {\n // The wall under the featured: the other reviews once each, whole and upright, in tidy columns\n // a little smaller than the featured ones, set back under a wash of the ground. Tier 1, Oct\n // 2026: a pile of tilted cards larger than the featured, repeated to fill the frame, read as\n // \"too many testimonials in the background, it doesn't make much sense\".\n const W = innerWidth, H = innerHeight;\n const cards = [...pile.children];\n const cw = Math.min(scale * 0.8, (W * 0.46) / 390);\n const cols = Math.max(2, Math.min(3, Math.round(W / (390 * cw * 1.05))));\n const gap = (W - cols * 390 * cw) / (cols + 1);\n const ys = Array.from({ length: cols }, (_, c) => H * 0.03 - (c % 2) * 60 * cw);\n for (const card of cards) {\n const c = ys.indexOf(Math.min(...ys));\n card.style.zoom = String(cw);\n card.style.left = (gap + c * (390 * cw + gap)) / cw + \"px\";\n card.style.top = ys[c] / cw + \"px\";\n ys[c] += card.offsetHeight * cw + gap * 0.6;\n }\n const wash = document.createElement(\"div\");\n wash.className = \"wash\";\n pile.appendChild(wash);\n }\n document.body.dataset.ready = \"1\";\n})();\n`;\n\n/**\n * The script that lays a 9:16 out as the app full screen: its bars pinned at the phone's scale, the\n * frame's card under the header, and the posts or messages as large as the band between them holds,\n * laid out wider rather than shrunk narrower, so they always run the screen's width.\n */\nconst FIT_FULLSCREEN = `\n(async () => {${READY}\n const cfg = JSON.parse(document.body.dataset.fit);\n const W = innerWidth, H = innerHeight;\n const top = document.getElementById(\"top\"), bottom = document.getElementById(\"bottom\");\n const banner = document.getElementById(\"banner\"), stack = document.getElementById(\"stack\");\n let y0 = Math.max(top.getBoundingClientRect().height, cfg.bandTop);\n const y1 = Math.min(H - bottom.getBoundingClientRect().height, cfg.bandBottom);\n if (banner) {\n banner.style.top = y0 + 8 + \"px\";\n y0 += banner.getBoundingClientRect().height + 16;\n }\n const room = y1 - y0;\n // Laid out wider as it shrinks, so it runs the screen's width, but never past a fifth wider than\n // the phone: a post's photo grows with the width, and the words would only get smaller.\n // A chat runs the screen's whole width at any scale: its bubbles keep to the phone's widths, so\n // a wider column only sets them against the edges, as the app does, never in a narrow strip.\n // Clear of the buttons down the right, where the placement draws them (\\`rail\\`).\n const across = W - cfg.rail;\n const fits = (s) => { stack.style.width = (cfg.scroll ? across / s : Math.min(across / s, cfg.phone * 1.2)) + \"px\"; return stack.offsetHeight * s <= room; };\n // A chat never shrinks past what reads on a phone: its earliest messages scroll up under the\n // header instead, as the app does, and the page says which ones went (the review expects them\n // nowhere on this size).\n const floor = cfg.chrome * (cfg.scroll ? 0.5 : 0.2);\n // A short chat is set a little larger than the phone, so it reads and fills the screen.\n let lo = floor, hi = cfg.chrome * (cfg.scroll ? 1.3 : 1), best = lo;\n if (fits(hi)) best = hi;\n else for (let i = 0; i < 16; i++) { const m = (lo + hi) / 2; if (fits(m)) { best = m; lo = m; } else hi = m; }\n fits(best);\n stack.style.transform = \"scale(\" + best + \")\";\n stack.style.left = (across - stack.offsetWidth * best) / 2 + \"px\";\n const h = stack.offsetHeight * best;\n stack.style.top = (cfg.align === \"bottom\" ? y1 - h : y0) + \"px\";\n const hidden = [];\n if (cfg.scroll && h > room + 1) {\n const veil = document.createElement(\"div\");\n const wall = getComputedStyle(stack.querySelector(\".messages\") ?? stack).backgroundColor;\n veil.style.cssText = \"position:absolute;left:0;right:0;top:0;z-index:2;background:\" + wall + \";height:\" + y0 + \"px\";\n document.body.appendChild(veil);\n for (const bubble of stack.querySelectorAll(\"[data-copy]\")) {\n if (bubble.getBoundingClientRect().top < y0) hidden.push(...JSON.parse(bubble.dataset.copy));\n }\n }\n document.body.dataset.hidden = JSON.stringify(hidden);\n document.body.dataset.ready = \"1\";\n})();\n`;\n\n/** A size tall enough to be a phone's screen (a 9:16): the app is drawn full screen on it. */\nfunction isFullScreen(opts: { width: number; height: number }): boolean {\n return opts.height / opts.width >= 1.7;\n}\n\nconst FULL_SCREEN_APPS: ReadonlySet<NativeSurface[\"surface\"]> = new Set([\n \"x_post\",\n \"whatsapp\",\n \"imessage\",\n \"notes\",\n \"slack\",\n]);\n\n/**\n * The colour the frame's headline and logo sit on at one size: on a 9:16 that shows the app full\n * screen, the card in the frame's or brand's colour; on any other size, the colour behind the screen\n * (`backdrop`). Pure.\n */\nexport function frameGround(\n surface: NativeSurface,\n brand: NativeFrameOpts[\"brand\"],\n size: { width: number; height: number },\n): string {\n if (isFullScreen(size) && FULL_SCREEN_APPS.has(surface.surface)) {\n return surface.frame.background ?? brand.ground ?? \"#ffffff\";\n }\n return backdrop(surface, brand);\n}\n\n/**\n * The page that draws the ad at one size. A feed size: the screen and its frame, scaled as one to the\n * largest that fits the size's safe area, centred in it, on the screen's own colour or the brand's. A\n * 9:16: the app full screen (`FIT_FULLSCREEN`), the way a story shows a phone. A wall of reviews: three\n * in the safe area over every other one piled across the frame. Pure.\n */\nexport function nativeSurfaceHtml(surface: NativeSurface, assets: NativeAssets, opts: NativeFrameOpts): string {\n const w = opts.width / 2;\n const h = opts.height / 2;\n const t = chromeWords(opts.language);\n const bg = backdrop(surface, opts.brand);\n const ink = isDark(bg) ? \"#ffffff\" : (opts.brand.ink ?? \"#111111\");\n const font = assets.font ? `@font-face{font-family:BrandFont;src:url(${assets.font});font-display:block}` : \"\";\n const head = `<!doctype html><html><head><meta charset=\"utf-8\">\n<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Noto+Color+Emoji&display=block\">\n<style>${font}${STYLE}`;\n const parts = isFullScreen(opts) ? appParts(surface, assets, t) : null;\n if (parts) return fullScreenHtml(surface, parts, assets, opts, head);\n const wall = surface.surface === \"trustpilot\" || surface.surface === \"google_reviews\";\n const stack = frameStack(surface, screenHtml(surface, assets, t, opts.brand.name), assets, ink, bg);\n const box = layoutBox(opts.safe, w, h);\n const pile = wall ? `<div id=\"backdrop\" style=\"--ground:${bg}\">${reviewPile(surface, assets, t)}</div>` : \"\";\n // A landscape box holds a wall's featured reviews side by side rather than in a thin strip.\n const wide = wall && Math.min(surface.reviews.length, FEATURED_REVIEWS) >= 2 && box.width / box.height > 1.25;\n return `${head}\nbody{background:${bg};width:${w}px;height:${h}px;position:relative}\n#box{top:${px(box.top)}px;left:${px(box.left)}px;width:${px(box.width)}px;height:${px(box.height)}px}\n</style></head><body${wide ? ' class=\"wide\"' : \"\"}>${pile}<div id=\"box\"><div id=\"stack\">${stack}</div></div><script>${FIT}</script></body></html>`;\n}\n\n/** The app's three parts, for a screen an app draws full screen; null for a wall of reviews, which is no app. */\nfunction appParts(surface: NativeSurface, assets: NativeAssets, t: Words): ScreenParts | null {\n switch (surface.surface) {\n case \"x_post\":\n return xParts(surface, assets, t);\n case \"whatsapp\":\n case \"imessage\":\n return chatParts(surface, assets, t);\n case \"notes\":\n return notesParts(surface, t);\n case \"slack\":\n return slackParts(surface, assets, t);\n default:\n return null;\n }\n}\n\n/** A 9:16 as the app full screen, with the frame's headline, caption and logo as a card in the brand's colours. */\nfunction fullScreenHtml(\n surface: NativeSurface,\n parts: ScreenParts,\n assets: NativeAssets,\n opts: NativeFrameOpts,\n head: string,\n): string {\n const w = opts.width / 2;\n const h = opts.height / 2;\n const chrome = Math.round((w / PHONE_WIDTH) * 1000) / 1000;\n const fit = {\n bandTop: px(opts.safe.top * h),\n bandBottom: px((1 - opts.safe.bottom) * h),\n // Buttons down the right (Reels, TikTok, Shorts): an inset wider on the right than the left.\n rail: opts.safe.right > opts.safe.left ? px(opts.safe.right * w) : 0,\n chrome,\n phone: PHONE_WIDTH,\n align: parts.align,\n scroll: surface.surface === \"whatsapp\" || surface.surface === \"imessage\",\n };\n const { frame } = surface;\n const ground = frameGround(surface, opts.brand, opts);\n const ink = isDark(ground) ? \"#ffffff\" : (opts.brand.ink ?? \"#111111\");\n const logo = frame.logo\n ? isDark(ground)\n ? (assets.frameLogo ?? assets.logo)\n : (assets.logo ?? assets.frameLogo)\n : undefined;\n const card = [\n frame.headline ? `<div class=\"headline\" style=\"color:${ink}\">${keepTogether(esc(frame.headline))}</div>` : \"\",\n frame.caption ? `<div class=\"caption\" style=\"color:${ink}\">${keepTogether(esc(frame.caption))}</div>` : \"\",\n logo ? `<img class=\"frame-logo${inked(assets, logo, isDark(ground))}\" src=\"${logo}\" alt=\"\">` : \"\",\n ].join(\"\");\n const banner = card\n ? `<div id=\"banner\"><div style=\"zoom:${chrome}\"><div class=\"fs-card\" style=\"background:${ground}\">${card}</div></div></div>`\n : \"\";\n return `${head}\nbody{width:${w}px;height:${h}px;position:relative}\n</style></head><body class=\"fs\" data-fit='${JSON.stringify(fit)}'><div class=\"fs-app ${parts.cls}\"><div id=\"top\"><div style=\"zoom:${chrome}\">${parts.top}</div></div>${banner}<div id=\"stack\">${parts.body}</div><div id=\"bottom\"><div style=\"zoom:${chrome}\">${parts.bottom}</div></div></div><script>${FIT_FULLSCREEN}</script></body></html>`;\n}\n\n/** The screen itself, in its platform's layout. */\nfunction screenHtml(surface: NativeSurface, assets: NativeAssets, t: Words, brandName: string | undefined): string {\n switch (surface.surface) {\n case \"x_post\":\n return `<div class=\"surface x ${surface.theme}\">${xPosts(surface, assets, t)}</div>`;\n case \"whatsapp\":\n case \"imessage\":\n return chatHtml(surface, assets, t);\n case \"trustpilot\":\n case \"google_reviews\":\n return reviewsHtml(surface, assets, t, brandName);\n case \"notes\":\n return notesHtml(surface, t);\n case \"slack\":\n return slackHtml(surface, assets, t);\n default:\n return \"\";\n }\n}\n\n/** The screen with what the frame puts around it: the headline above, the caption and the logo under. */\nfunction frameStack(surface: NativeSurface, screen: string, assets: NativeAssets, ink: string, ground: string): string {\n const { frame } = surface;\n const logo = frame.logo ? (assets.frameLogo ?? assets.logo) : undefined;\n // A wall carries its badge in the pile itself (`reviewsHtml`); any other screen under it.\n const wall = surface.surface === \"trustpilot\" || surface.surface === \"google_reviews\";\n const badge =\n logo && frame.logo_position === \"over\" && !wall\n ? `<div class=\"logo-badge\"><img class=\"${inked(assets, assets.logo ?? logo, false).trim()}\" src=\"${assets.logo ?? logo}\" alt=\"\"></div>`\n : \"\";\n const placed = logo && frame.logo_position === \"over\";\n // Over a wall's pile the frame's lines sit on a card of the ground they were set for, or the pile hides them.\n const piled = wall && surface.reviews.length > FEATURED_REVIEWS;\n const line = (cls: string, text: string) => {\n const html = `<div class=\"${cls}\" style=\"color:${ink}\">${keepTogether(esc(text))}</div>`;\n return piled ? `<div class=\"wall-card\" style=\"background:${ground}\">${html}</div>` : html;\n };\n return [\n frame.headline ? line(\"headline\", frame.headline) : \"\",\n badge ? `<div class=\"over\">${screen}${badge}</div>` : screen,\n frame.caption ? line(\"caption\", frame.caption) : \"\",\n logo && !placed ? `<img class=\"frame-logo${inked(assets, logo, isDark(ground))}\" src=\"${logo}\" alt=\"\">` : \"\",\n ].join(\"\");\n}\n\n/** The box the stack is fitted in: the page less the safe area on each side (a margin at least), in CSS pixels. */\nfunction layoutBox(safe: NativeFrameOpts[\"safe\"], w: number, h: number) {\n const margin = 0.035;\n const top = Math.max(safe.top, margin) * h;\n const bottom = Math.max(safe.bottom, margin) * h;\n const left = Math.max(safe.left, margin) * w;\n const right = Math.max(safe.right, margin) * w;\n return { top, left, width: w - left - right, height: h - top - bottom };\n}\n","/**\n * Review platforms and ratings: a line that names a platform or shows a rating is a claim, and a\n * claim needs its source on the ad and its figure in the company's own records (D3, audited Sept\n * 2026: \"Reseña verificada en Trustpilot\" over generic gold stars with no Trustpilot logo; five-star\n * reviews with no source at all). One table, read by the review of the finished ad, the proof\n * check before a render and the scaffold that wires each platform's real logo.\n *\n * Pure.\n */\n\nexport type ReviewPlatform = {\n name: string;\n /** Where its real logo is fetched from (`baker images logo <domain>`). */\n domain: string;\n pattern: RegExp;\n /** The name alone is a review claim (Trustpilot); otherwise only beside rating words (Google). */\n reviewOnly: boolean;\n};\n\nexport const REVIEW_PLATFORMS: readonly ReviewPlatform[] = [\n { name: \"Trustpilot\", domain: \"trustpilot.com\", pattern: /\\btrust ?pilot\\b/i, reviewOnly: true },\n { name: \"G2\", domain: \"g2.com\", pattern: /\\bG2\\b/, reviewOnly: true },\n { name: \"Capterra\", domain: \"capterra.com\", pattern: /\\bcapterra\\b/i, reviewOnly: true },\n { name: \"Tripadvisor\", domain: \"tripadvisor.com\", pattern: /\\btrip ?advisor\\b/i, reviewOnly: true },\n { name: \"Yelp\", domain: \"yelp.com\", pattern: /\\byelp\\b/i, reviewOnly: true },\n { name: \"Feefo\", domain: \"feefo.com\", pattern: /\\bfeefo\\b/i, reviewOnly: true },\n // Before Google: the first match names the platform.\n { name: \"Google Play\", domain: \"play.google.com\", pattern: /\\bgoogle play\\b/i, reviewOnly: false },\n { name: \"Google\", domain: \"google.com\", pattern: /\\bgoogle\\b/i, reviewOnly: false },\n { name: \"the App Store\", domain: \"apple.com\", pattern: /\\bapp ?store\\b/i, reviewOnly: false },\n];\n\n/** Words that make a platform's name a rating claim. */\nexport const RATING_WORDS =\n /★|\\b(?:reviews?|reseñas?|rese[ñn]as?|ratings?|rated|valoraci[oó]n(?:es)?|opiniones|estrellas|stars?|avis|bewertung(?:en)?|verificad[ao]s?|verified|recensioni|avalia[cç][oõ]es)\\b|\\d[.,]\\d\\s*(?:\\/\\s*5|★)/i;\n\n/** The platform a text names, the most specific first (a \"Google Play\" logo is Google Play's, not Google's). */\nexport function namedPlatform(text: string): ReviewPlatform | null {\n return REVIEW_PLATFORMS.find((p) => p.pattern.test(text)) ?? null;\n}\n\n/** The review platform a line cites as the source of a rating or reviews, if any. */\nexport function claimedPlatform(text: string): ReviewPlatform | null {\n return REVIEW_PLATFORMS.find((p) => p.pattern.test(text) && (p.reviewOnly || RATING_WORDS.test(text))) ?? null;\n}\n\n/**\n * The rating figures a line states (\"4,8/5\", \"4.7 ★\", \"+2.000 reseñas\"), as written: each must\n * be in the company's records before an ad says it.\n */\nexport function ratingFigures(text: string): string[] {\n const out = new Set<string>();\n for (const m of text.matchAll(/(\\d[.,]\\d)\\s*(?:\\/\\s*5|★|\\s?(?:stars?|estrellas|étoiles|sterne))/gi))\n if (m[1]) out.add(m[1]);\n for (const m of text.matchAll(/★\\s*(\\d[.,]\\d)/g)) if (m[1]) out.add(m[1]);\n for (const m of text.matchAll(\n /\\+?\\s?(\\d{1,3}(?:[.,]\\d{3})+|\\d{2,})\\s+(?:reviews|reseñas|opiniones|valoraciones|avis|bewertungen|avaliações)/gi,\n )) {\n if (m[1]) out.add(m[1]);\n }\n return [...out];\n}\n\n/**\n * A figure as the records may write it: \"4,8\" and \"4.8\" alike, and \"2.000\", \"2,000\" and \"2000\"\n * alike. Only a count in thousands is also read bare: \"4,8\" as \"48\" would match any 48.\n */\nexport function figureForms(figure: string): string[] {\n const forms = [figure, figure.replace(/,/g, \".\"), figure.replace(/\\./g, \",\")];\n if (/^\\d{1,3}(?:[.,]\\d{3})+$/.test(figure)) forms.push(figure.replace(/[.,]/g, \"\"));\n return [...new Set(forms)];\n}\n","/**\n * What a static ad's blueprint says, read the one way every reader agrees on.\n *\n * The copy is edited in `prompt.json` after the scaffold writes the canvas, so anything\n * derived from it has to be derived again when the canvas runs. Frozen at scaffold time,\n * the render was told \"render these exact strings: <the source ad's copy>\" while the\n * blueprint below it carried ours, and the model drew the competitor's words.\n *\n * Pure: blueprint in, strings out. The run-time sync (`static-copy-projection.ts`), the\n * scaffold and the review all go through here, so no two can disagree about what the\n * copy, the logo or the mechanisms are.\n */\nimport { z } from \"zod\";\n\n/** The header the render prompt's copy block starts with. The block ends at the next blank line. */\nexport const COPY_DIRECTIVE_HEADER = \"TEXT — RENDER THE COPY EXACTLY:\";\n\ntype TextEntry = { text?: unknown; role?: unknown; surface?: unknown; placement?: unknown };\n\nconst normalize = (s: unknown): string => (typeof s === \"string\" ? s.trim().toLowerCase() : \"\");\n\nfunction textEntries(blueprint: unknown): TextEntry[] {\n const entries = (blueprint as { text_content?: unknown } | null)?.text_content;\n return Array.isArray(entries) ? (entries as TextEntry[]) : [];\n}\n\n/**\n * The visible copy, in blueprint order, without the logo.\n *\n * A wordmark is transcribed into `text_content` with role `logo` (the describe pass is told\n * to), but it is an image, not copy: quoting it as a string to render is how an ad came back\n * with the brand name drawn twice, once as the mark and once as a line of type.\n */\nexport function copyStrings(blueprint: unknown): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const entry of textEntries(blueprint)) {\n if (normalize(entry?.role) === \"logo\") continue;\n const text = typeof entry?.text === \"string\" ? entry.text.trim() : \"\";\n if (!text || seen.has(text)) continue;\n seen.add(text);\n out.push(text);\n }\n return out;\n}\n\nconst IN_SCENE_ROLES = new Set([\"in_scene\", \"scene\", \"ui\", \"screen\", \"screenshot\", \"packaging\", \"signage\", \"sign\"]);\n/** Placements that put a line on an object in the picture rather than over it. */\nconst IN_SCENE_PLACEMENT =\n /\\b(phone|screen|screenshot|app|packaging|package|bottle|jar|signage|signboard|handwritten|receipt|whiteboard|sticky note|notepad)\\b/;\n\n/**\n * Text that belongs to something in the scene (a phone note, a screenshot, a sign,\n * packaging): the describe pass marks it `surface: in_scene`, and an older blueprint\n * says so through its role or placement.\n */\nexport function isInScene(entry: TextEntry): boolean {\n const surface = normalize(entry.surface);\n if (surface === \"in_scene\") return true;\n if (surface === \"overlay\") return false;\n return IN_SCENE_ROLES.has(normalize(entry.role)) || IN_SCENE_PLACEMENT.test(normalize(entry.placement));\n}\n\n/** The lines written on something in the scene, verbatim, with where the concept places them. */\nexport function inSceneText(blueprint: unknown): Array<{ text: string; placement: string }> {\n const seen = new Set<string>();\n return textEntries(blueprint).flatMap((entry) => {\n const text = typeof entry?.text === \"string\" ? entry.text.trim() : \"\";\n if (!text || normalize(entry.role) === \"logo\" || !isInScene(entry) || seen.has(text)) return [];\n seen.add(text);\n return [{ text, placement: typeof entry.placement === \"string\" ? entry.placement.trim() : \"\" }];\n });\n}\n\n/**\n * Whether the concept carries a logo: the ad shows the advertiser's own mark. Deleting the\n * advertiser entry from `brands_logos` in prompt.json is how an ad drops its logo.\n */\nexport function conceptHasLogo(blueprint: unknown): boolean {\n const logos = (blueprint as { brands_logos?: unknown } | null)?.brands_logos;\n return (\n Array.isArray(logos) &&\n logos.some(\n (l) => normalize((l as { function_in_image?: unknown } | null)?.function_in_image) === \"advertiser_brand\",\n )\n );\n}\n\n/** A named device that makes the source ad work, as the describe pass records it. */\nconst WinningMechanismSchema = z\n .object({ kind: z.string(), device: z.string(), why_it_works: z.string().optional() })\n .loose();\nexport type WinningMechanism = z.infer<typeof WinningMechanismSchema>;\n\n/** The mechanisms the blueprint names, skipping any entry too malformed to act on. */\nexport function winningMechanisms(blueprint: unknown): WinningMechanism[] {\n const raw = (blueprint as { winning_mechanisms?: unknown } | null)?.winning_mechanisms;\n if (!Array.isArray(raw)) return [];\n return raw.flatMap((entry) => {\n const parsed = WinningMechanismSchema.safeParse(entry);\n return parsed.success ? [parsed.data] : [];\n });\n}\n\n/** The type specimen's text: the ad's own copy, so the specimen shows the letters the ad needs. */\nexport function specimenText(strings: string[]): string {\n return strings.length > 0 ? strings.join(\"\\n\") : \"Brand Headline\";\n}\n\n/**\n * The typography-fidelity lines for a render that sets its own text. Garbled, duplicated\n * or dropped copy is the #1 static-ad defect, so every displayed string is quoted, asked\n * for exactly once, and nothing else is allowed.\n */\nexport function textFidelityDirective(strings: string[], hasFont: boolean): string {\n const specimen = hasFont ? \" in the specimen's typeface,\" : \"\";\n const exact =\n strings.length > 0\n ? `Render these exact strings${specimen} each once, verbatim, with no extra, missing, or altered characters: ${strings.map((s) => `\"${s}\"`).join(\" / \")}.`\n : \"This ad has no copy: render no words at all.\";\n return [\n exact,\n \"Render NO other words anywhere in the image — no invented labels, captions, sub-headers, or filler text.\",\n \"For any brand name or uncommon word, lock its letters exactly as written (spell it out letter-by-letter if that helps the characters render correctly).\",\n \"Keep all type crisp, legible, and large enough to read cleanly; no garbled, warped, or duplicated characters, and no unreadable microtext.\",\n ].join(\"\\n\");\n}\n\n/**\n * Swap the copy block of a render prompt for `directive`. The block is the header line plus\n * everything up to the next blank line. A prompt without the header is returned unchanged:\n * a canvas somebody wrote by hand.\n */\nexport function replaceCopyDirective(prompt: string, directive: string): string {\n const start = prompt.indexOf(COPY_DIRECTIVE_HEADER);\n if (start === -1) return prompt;\n const bodyStart = start + COPY_DIRECTIVE_HEADER.length;\n const end = prompt.indexOf(\"\\n\\n\", bodyStart);\n const tail = end === -1 ? \"\" : prompt.slice(end);\n return `${prompt.slice(0, bodyStart)}\\n${directive}${tail}`;\n}\n","/**\n * The static ad brief: the ad spec (`static-ad-spec.ts`) compiled into the prompt one image\n * model is sent, following that model's profile (`prompt-profiles.ts`).\n *\n * The agent never writes a model prompt. It writes what the ad is; this writes it the way\n * each model reads best: short labelled sections for gpt-image, sentences that give the\n * reason for Gemini; every line of copy quoted once, lettered its own way, and allow-listed;\n * every picture numbered with one role, what must stay exact first; colours named, tied to\n * an object and taken from the brand; realism as capture cues; the safe area from its one\n * owner; the constraints last. `lintBrief` finds the contradictions an older prompt carried\n * (prompt-audit.md §1a), so the bench can gate on none.\n *\n * Pure: spec in, `{ prompt, references, params }` out.\n */\nimport { marketCountryName } from \"../../viewer-profile.ts\";\nimport type { BriefImageRole, ImageModelProfile } from \"../lib/prompt-profiles.ts\";\nimport { blockingArea, type SafeArea, staticSafeArea } from \"./platform-safe-area.ts\";\nimport {\n type AdBrand,\n type AdCopyLine,\n type AdReference,\n type AdSpec,\n type AdThirdPartyBrand,\n type AdViewer,\n conceptSummary,\n HAND_LETTERED,\n HANDWRITING,\n plainText,\n withRenderedLettering,\n} from \"./static-ad-spec.ts\";\n\n/** What the render does: a first render of the ad, a re-lay of a finished one to another shape, or a correction. */\nexport type BriefJob = \"generate\" | \"relay\" | \"fix\";\n\n/**\n * The pictures wired for this render, as the refs the canvas passes them by (or URLs on the\n * bench). `refs` maps a spec reference id (PRODUCT_1) to its picture; a spec reference with\n * no picture is described in the scene only. Omitted: every spec reference is wired under\n * its own id, and there is no logo, type specimen or source ad.\n */\nexport type BriefImages = {\n /** The ad to re-lay or correct. */\n base?: string;\n logo?: string;\n /** The specimen of the brand's typeface. */\n type?: string;\n /** The source ad. Sent only when `sourceLayout` asks for it. */\n layout?: string;\n refs?: Readonly<Record<string, string>>;\n};\n\n/**\n * The master's planned content band, and whether other sizes are cut from the master (the\n * reason the band is firm). `margin`: the render is the whole ad, cut to its format after it\n * renders (a 3:4 render filled to 4:5), so the band is its edge margin mapped onto the\n * rendered frame (`adaptPlan`), stated as edges rather than as a region.\n */\nexport type PlannedBand = { area: SafeArea; cut: boolean; margin?: boolean };\n\nexport type BriefOptions = {\n job?: BriefJob;\n /** Which of the three directions a first render takes (0 as written, 1 another picture, 2 another hook). */\n variant?: number;\n /** A correction's change, in words. */\n instruction?: string;\n images?: BriefImages;\n /** The ad set's placements (`staticSafeArea`); none = every placement the format runs in. */\n placements?: readonly string[];\n /**\n * The master's planned content band (`masterContentBand` in static-sizes.ts): stated as the\n * one region every word, the logo and the button sit in. Omitted: the format's own safe area.\n */\n band?: PlannedBand;\n /** The caller states the frame itself (the re-lays' zones, `adaptBrief`): the Layout states no safe area of its own. */\n zoned?: boolean;\n /**\n * Send the source ad as a layout-only reference, last. Off by default: with it on, the judge\n * called 35-37% of ads near-clones of the source vs 21% off, shippable fell 21% -> 16% and the\n * off arm won 60% head to head against it numbered last (63 ads, BENCH.md#E2).\n */\n sourceLayout?: boolean;\n};\n\n/** One picture the brief numbers: its place in the request, its one role, and the ref it travels as. */\nexport type BriefReference = { index: number; role: BriefImageRole; label: string; ref: string };\n\nexport type CompiledBrief = {\n prompt: string;\n /** The pictures in request order; `references[i].index === i + 1`. */\n references: BriefReference[];\n /** What the request asks for, before the registry snaps it to the pinned model. */\n params: { n: number; quality?: string; image_size?: string };\n words: number;\n};\n\n// --- Small text helpers. ---\n\nconst wordCount = (s: string): number => s.split(/\\s+/).filter(Boolean).length;\nconst sentence = (s: string): string => {\n const t = s.trim();\n return t && !/[.!?]$/.test(t) ? `${t}.` : t;\n};\nconst capitalize = (s: string): string => (s ? s[0]?.toUpperCase() + s.slice(1) : s);\n\n/**\n * Free text joined from several fields (\"…CTA bottom.\" + \". In the frame\") collapsed to one\n * mark per break: a run of marks keeps a leading ! or ?, else its last mark. An ellipsis\n * becomes \"…\" first so it survives.\n */\nfunction tidy(s: string): string {\n return s\n .replace(/\\.{3,}/g, \"…\")\n .replace(/([.!?,;:])(?:\\s*[.,;:])+/g, (run, first: string) =>\n /[!?]/.test(first) ? first : (run.trim().at(-1) ?? \"\"),\n );\n}\n\n/** A value the describe pass writes for \"nothing here\", never worth a clause. */\nconst EMPTY_CLAUSE = /^(none|null|nil|n a|na|not applicable)$/;\n\n/**\n * The clauses of a free text (split after , ; . ! ? and a space, so \"1.91:1\" and \"S.L.\" hold)\n * without the empty ones, the ones said already, and the ones `drop` rejects. A dropped clause\n * that ended a sentence hands its full stop to the clause before it.\n */\nfunction keepClauses(text: string, drop: (clause: string) => boolean = () => false): string {\n const kept: string[] = [];\n const seen = new Set<string>();\n for (const part of text.split(/(?<=[,;.!?])\\s+/)) {\n const body = normalized(part);\n if (body && !EMPTY_CLAUSE.test(body) && !seen.has(body) && !drop(part)) {\n seen.add(body);\n kept.push(part.trim());\n continue;\n }\n const end = part.trim().match(/[.!?]$/)?.[0];\n const last = kept.length - 1;\n if (end && last >= 0)\n kept[last] = (kept[last] ?? \"\").replace(/[,;:]?$/, (m) => (/[.!?]$/.test(kept[last] ?? \"\") ? m : end));\n }\n return kept.join(\" \");\n}\n\n/** What free text is cleaned against: the quoted copy, whether a logo picture is wired, whether the profile phrases everything positively. */\ntype Clean = { copy: readonly string[]; logo: boolean; positive: boolean };\n\n/** Copy too short to tell apart from an ordinary word or figure (\"1\", \"3x\") is never rewritten or counted. */\nconst MIN_COPY_LETTERS = 3;\nconst escapeRegExp = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\nconst wordEdge = (ch: string | undefined, look: \"behind\" | \"ahead\"): string =>\n ch && /[\\p{L}\\p{N}]/u.test(ch) ? (look === \"behind\" ? \"(?<![\\\\p{L}\\\\p{N}])\" : \"(?![\\\\p{L}\\\\p{N}])\") : \"\";\n/** `text` as a whole phrase, in any case. */\nconst phrase = (text: string, wrap: (core: string) => string = (core) => core): RegExp =>\n new RegExp(`${wordEdge(text[0], \"behind\")}${wrap(escapeRegExp(text))}${wordEdge(text.at(-1), \"ahead\")}`, \"giu\");\nconst longEnough = (copy: string): boolean => (copy.match(/\\p{L}/gu) ?? []).length >= MIN_COPY_LETTERS;\n\n/** Each copy line named in free text becomes \"line N\": the words are quoted once, in the Text section (openai §2). */\nfunction copyAsLineRefs(text: string, copy: readonly string[]): string {\n const byLength = copy.map((t, i) => ({ t, n: i + 1 })).sort((a, b) => b.t.length - a.t.length);\n return byLength.reduce(\n (out, { t, n }) =>\n longEnough(t)\n ? out.replace(\n phrase(t, (core) => `['\"“‘]?${core}['\"”’]?`),\n `line ${n}`,\n )\n : out,\n text,\n );\n}\n\n/**\n * Free text a render must never read: a logo forbidden while one is wired, the source ad or a\n * competitor named, hand-lettering (every line is set as type, `withRenderedLettering`), and (a\n * positive profile) any negation.\n */\nfunction unwanted(clause: string, k: Clean): boolean {\n if (k.logo && /\\bno (logos?|wordmarks?|branding)\\b/i.test(clause)) return true;\n if (HAND_LETTERED.test(clause)) return true;\n if (/\\bsource (ad|composition)\\b|\\bORIGINAL\\b|\\bcompetitor|\\breference ad\\b/.test(clause)) return true;\n return k.positive && NEGATION.test(clause.replace(/\"[^\"\\n]*\"/g, \"\"));\n}\n\nconst NEGATION = /\\b(no|not|never|don't|do not|avoid|without)\\b/i;\n\n/**\n * A free-text spec field as the brief carries it: `plainText`, enum underscores as spaces,\n * copy named by its line, the clauses a render must not read dropped, marks tidied.\n */\nfunction plain(text: unknown, k?: Clean): string {\n let out = plainText(text).replace(/(?<=\\p{L})_(?=\\p{L})/gu, \" \");\n if (k) out = copyAsLineRefs(out, k.copy).replace(/\\bfaithful(ly)?\\b/gi, (_m, ly?: string) => `exact${ly ?? \"\"}`);\n return tidy(keepClauses(out, k ? (clause) => unwanted(clause, k) : undefined)).replace(/\\s*[,;:]+$/, \"\");\n}\n\n/** A text's words, lower-cased with the punctuation gone, to tell a restatement from new detail. */\nconst normalized = (s: string): string =>\n s\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, \" \")\n .trim();\n\n/** The sentences of `text` that `said` does not already contain. */\nfunction unsaid(text: string, said: string): string {\n const known = ` ${normalized(said)} `;\n return (text.match(/[^.!?]+[.!?]*/g) ?? [])\n .map((s) => s.trim())\n .filter((s) => normalized(s) && !known.includes(` ${normalized(s)} `))\n .join(\" \");\n}\n\n/** The first `max` words of a text, cut at a sentence end when one fits, else at the last clause mark. */\nfunction clip(text: string, max: number): string {\n if (wordCount(text) <= max) return text;\n const sentences = text.match(/[^.!?]+[.!?]*/g) ?? [text];\n let out = \"\";\n for (const s of sentences) {\n if (wordCount(out + s) > max) break;\n out += s;\n }\n if (out.trim()) return sentence(out.trim().replace(/[\\s,;:]+$/, \"\"));\n const head = text.split(/\\s+/).slice(0, max).join(\" \");\n // A cut mid-phrase (\"left half dull with.\") reads as a different scene: end on the last clause.\n const clause = head.match(/^(.*?)[,;:—–](?:\\s[^,;:—–]*)?$/)?.[1];\n return sentence((clause?.trim() || head).replace(/[\\s,;:]+$/, \"\"));\n}\n\n/** A colour's lightness name when it carries too little hue to be named by it. */\nfunction neutralName(l: number): string {\n if (l > 0.95) return \"white\";\n if (l > 0.8) return \"off-white\";\n if (l > 0.6) return \"light grey\";\n if (l > 0.35) return \"grey\";\n return l > 0.12 ? \"dark grey\" : \"black\";\n}\n\n/** Hue bands, by their upper edge in degrees. */\nconst HUES: ReadonlyArray<[number, string]> = [\n [15, \"red\"],\n [40, \"orange\"],\n [65, \"yellow\"],\n [165, \"green\"],\n [185, \"teal\"],\n [250, \"blue\"],\n [290, \"purple\"],\n [340, \"pink\"],\n [360, \"red\"],\n];\n\nfunction hueOf(r: number, g: number, b: number, max: number, d: number): number {\n const raw = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4;\n return (raw * 60 + 360) % 360;\n}\n\n/**\n * A plain colour name for a brand hex, so a colour reaches the model as words tied to an\n * object with the hex beside it, never as a bare code (BFL: \"tie each hex to an object\",\n * research §1; OpenAI names colours in words, prompting-openai-gpt-image.md §7).\n */\nexport function colourName(hex: string): string {\n const n = Number.parseInt(hex.slice(1), 16);\n const r = ((n >> 16) & 255) / 255;\n const g = ((n >> 8) & 255) / 255;\n const b = (n & 255) / 255;\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n const s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));\n if (s < 0.15 || l > 0.95 || l < 0.07) return neutralName(l);\n const h = hueOf(r, g, b, max, d);\n const band = HUES.find(([edge]) => h < edge)?.[1] ?? \"red\";\n const hue = band === \"orange\" && l < 0.35 ? \"brown\" : band;\n const shade = l < 0.3 ? \"dark \" : l > 0.8 ? \"pale \" : \"\";\n return `${shade}${hue}`;\n}\n\n// --- What the ad is shown on, and the space it keeps clear. ---\n\nconst VERTICAL_NOUN: Record<string, string> = {\n reels: \"full-screen vertical ad for Stories and Reels\",\n stories: \"full-screen vertical Stories ad\",\n feed: \"full-screen vertical ad\",\n};\n\nfunction deliverableNoun(format: string, placements: readonly string[] | undefined): string {\n if (format === \"9:16\") {\n const safe = staticSafeArea(format, placements);\n return safe.placement === \"vertical\"\n ? `full-screen vertical ${safe.surface} ad`\n : (VERTICAL_NOUN[safe.placement] ?? \"full-screen vertical ad\");\n }\n const [w, h] = format.split(\":\").map(Number);\n if (!w || !h) return \"ad\";\n return w < h ? \"vertical feed ad\" : w === h ? \"square feed ad\" : \"landscape ad\";\n}\n\n/** The things the safe area keeps inside it, in words. */\nfunction keptInside(logo: boolean, button: boolean): string {\n const parts = [\"every word\", ...(logo ? [\"the logo\"] : []), ...(button ? [\"the button\"] : [])];\n return parts.length === 1 ? (parts[0] ?? \"\") : `${parts.slice(0, -1).join(\", \")} and ${parts.at(-1)}`;\n}\n\ntype Structure = ImageModelProfile[\"brief\"][\"structure\"];\n\n/**\n * The safe area, stated by what fills each band and with the numbers of its one owner\n * (`platform-safe-area.ts`), never restated here. Gemini gets the reason (gemini §9a).\n */\nfunction safeAreaText(\n format: string,\n placements: readonly string[] | undefined,\n items: string,\n structure: Structure,\n): string {\n const safe = blockingArea(format, placements);\n const pct = (f: number) => `${Math.round(f * 100)}%`;\n const side = pct(Math.max(safe.left, safe.right));\n const sits = items.includes(\" and \") ? \"sit\" : \"sits\";\n if (safe.placement !== \"feed\") {\n const surface = safe.placement === \"reels\" ? \"Stories and Reels\" : safe.surface;\n return structure === \"labelled_sections\"\n ? `Keep ${items} between the top ${pct(safe.top)} and the bottom ${pct(safe.bottom)} of the frame (the app's interface covers them; scene only there), at least ${side} in from each side.`\n : `In ${surface} the app's interface covers the top ${pct(safe.top)} and the bottom ${pct(safe.bottom)} of the screen, so those bands hold only the scene; ${items} ${sits} between them, at least ${side} in from each side.`;\n }\n return edgeText(safe, items, structure);\n}\n\n/** A margin at the frame's edges, stated as edges. */\nfunction edgeText(area: SafeArea, items: string, structure: Structure): string {\n const pct = (f: number) => `${Math.round(f * 100)}%`;\n const side = pct(Math.max(area.left, area.right));\n const sits = items.includes(\" and \") ? \"sit\" : \"sits\";\n const edges =\n pct(area.top) === side && pct(area.bottom) === side\n ? `at least ${side} in from every edge`\n : `at least ${pct(Math.max(area.top, area.bottom))} in from the top and bottom edges and ${side} from the sides`;\n return structure === \"labelled_sections\"\n ? `Keep ${items} ${edges}.`\n : `${capitalize(items)} ${sits} ${edges}, clear of the frame.`;\n}\n\n/**\n * What a whole-ad render's brief asks beyond the edge margin its checks hold it to. Bench E5\n * (.context/static-review/BENCH.md § E5 band master): asked for the 1:1's feed margin, the\n * renders set their words 2-5% from the edge, and the 1:1 passed its margin in 20% of ads.\n */\nconst MARGIN_HEADROOM = 0.03;\n\n/**\n * A whole-ad render's layout: a complete ad at the frame's own shape, its edge margin (plus\n * headroom) kept clear, and a scene that runs to the edges, because the fill to the delivered\n * pixels trims them.\n */\nfunction marginText(planned: PlannedBand, items: string, structure: Structure): string {\n const scene =\n structure === \"labelled_sections\"\n ? \"The scene runs to every edge of the frame, with no border or frame around the ad.\"\n : \"The scene runs to every edge of the frame and continues beyond it.\";\n const { top, bottom, left, right } = planned.area;\n const asked = {\n top: top + MARGIN_HEADROOM,\n bottom: bottom + MARGIN_HEADROOM,\n left: left + MARGIN_HEADROOM,\n right: right + MARGIN_HEADROOM,\n };\n return `${edgeText(asked, items, structure)} ${scene}`;\n}\n\n/**\n * The master's planned content band as one region (`masterContentBand`: the placement's safe\n * area narrowed to what every size cut from the master keeps), stated firmly: the cuts only\n * hold what sits inside it. Gemini gets the reason and positive phrasing (gemini §2, §9a).\n */\nfunction bandText(planned: PlannedBand, items: string, flowing: boolean, appChrome: boolean): string {\n const band = planned.area;\n const pct = (f: number) => `${Math.round(f * 100)}%`;\n const region = `from ${pct(band.top)} to ${pct(1 - band.bottom)} of the height and from ${pct(band.left)} to ${pct(1 - band.right)} of the width`;\n const sits = items.includes(\" and \") ? \"sit\" : \"sits\";\n const reasons = [\n ...(appChrome\n ? [\n flowing\n ? \"the app draws its interface over the top and bottom of the screen\"\n : \"the app's interface covers the top and bottom\",\n ]\n : []),\n ...(planned.cut ? [\"the other sizes are cut from this picture\"] : []),\n ];\n if (reasons.length === 0)\n reasons.push(flowing ? \"the edges of a feed crop tightly\" : \"a feed crops tightly at the edges\");\n if (flowing) {\n return `${capitalize(items)} ${sits} inside one region, ${region}, because ${reasons.join(\" and \")}; the rest of the frame carries the scene only.`;\n }\n return `${items} ${sits} inside one region: ${region}. Outside it the frame carries the scene only: ${reasons.join(\", and \")}.`;\n}\n\n// --- The pictures. ---\n\n/** An identity picture of an animal: by its label, or by what it shows when the label names neither. */\nconst isAnimal = (r: AdReference): boolean =>\n r.id.startsWith(\"ANIMAL_\") || (!r.id.startsWith(\"PERSON_\") && ANIMALS.test(r.what));\nconst personOrAnimal = (r: AdReference): \"person\" | \"animal\" => (isAnimal(r) ? \"animal\" : \"person\");\n\ntype Picture = BriefReference & { spec?: AdReference };\n\ntype PictureContext = {\n job: BriefJob;\n logoPosition?: string;\n designedLines: number[];\n /** Every copy line is designed, so the type picture need not list them. */\n allDesigned: boolean;\n thirdParty: readonly AdThirdPartyBrand[];\n /** The brand colours a logo plate may take, darkest first, tied to their names. */\n plates: readonly ColourUse[];\n flowing: boolean;\n clean: Clean;\n /** The brief is at its last trim level: the logo's instruction takes its short form. */\n short: boolean;\n /** The concept is a native disguise (`brand_presence: native`): the logo is the sender's mark. */\n native: boolean;\n};\n\n/** \"the SEAT logo, on the car the loan is for\": another brand's mark and where it shows. */\nfunction thirdPartyLogo(brand: AdThirdPartyBrand, k: Clean): string {\n const where = brand.what ? plain(brand.what, k) : \"\";\n return `the ${plain(brand.name)} logo${where ? `, on ${where.replace(/^on /i, \"\")}` : \"\"}`;\n}\n\n/** The other brands' logos, as the constraints name them (\"the SEAT logo\"). */\nconst thirdPartyNames = (spec: AdSpec): string =>\n spec.third_party_brands.map((b) => `the ${plain(b.name)} logo`).join(\" and \");\n\n/**\n * The other brands the offer is about, one line: their logos may appear, as those brands draw\n * them. The user asked to keep a car brand's logo on a car-loan ad (BENCH.md § \"E1 v2\").\n */\nfunction otherBrandsText(c: Ctx): string {\n const brands = c.spec.third_party_brands;\n if (brands.length === 0 || c.job !== \"generate\") return \"\";\n const marks = brands.map((b) => thirdPartyLogo(b, c.clean)).join(\"; \");\n return c.flowing\n ? ` ${sentence(capitalize(marks))} Each appears exactly as that brand draws it.`\n : `\\nOther brands: ${marks}, exactly as ${brands.length === 1 ? \"that brand draws it\" : \"each brand draws its own\"}.`;\n}\n\n/** Size words a logo position must not carry: the logo's size is the brief's (`logoInstruction`). */\nconst LOGO_SIZE_WORDS = /\\b(small|smaller|tiny|little|subtle|discreet|faint|minimal)\\b/i;\n\n/**\n * The logo, stated as the user asked (E1 gallery, BENCH.md § \"E1 v2\": \"using real logos is a\n * must\", legible, in a colour that stands out, letters not tiny). It is never recoloured, so\n * its contrast comes from where it sits or from a small plate in a brand colour. E6 found a\n * composited wordmark at 15% of the width unreadable; a fifth is the floor stated here.\n */\nfunction logoInstruction(p: Picture, ctx: PictureContext): string {\n // A native disguise carries the brand as its sender (C6): a big logo would break the disguise.\n if (ctx.native) {\n return ctx.flowing\n ? `the brand's real logo, as the sender's picture or signature where the disguise puts it, small as such a mark is. Reproduce it once, exactly as drawn in Image ${p.index}.`\n : \"the brand's real logo, as the sender's picture or signature where the disguise puts it, small as such a mark is. Reproduce it once, exactly as drawn.\";\n }\n const position = ctx.logoPosition\n ? keepClauses(plain(ctx.logoPosition, ctx.clean), (clause) => LOGO_SIZE_WORDS.test(clause))\n : \"\";\n const at = position ? `, ${position.replace(/[\\s,;:.!?]+$/, \"\")}` : \"\";\n const plate = ctx.plates.length > 0 ? ctx.plates.map((u) => `${u.name} (${u.hex})`).join(\" or \") : \"white or black\";\n const where = `at least a fifth of the frame wide, on an area it contrasts with or a small ${plate} plate`;\n if (ctx.short)\n return \"the brand's real logo. Reproduce it once, exactly as drawn, at least a fifth of the frame wide.\";\n // Once, as its own mark: the Tidal ad printed it on the bottle as well as in its header (BENCH.md § \"Final\").\n return ctx.flowing\n ? `the brand's real logo. Reproduce it once, apart from the product${at}, exactly as drawn in Image ${p.index}, ${where}.`\n : `the brand's real logo. Reproduce it once, never on the product${at}, exactly as drawn, ${where}.`;\n}\n\nfunction identityInstruction(p: Picture, what: string, keep: string, flowing: boolean): string {\n if (p.spec && personOrAnimal(p.spec) === \"person\") {\n return flowing\n ? `${what} Keep their facial features, hair, skin tone and build exactly the same as in Image ${p.index}, and show them once; several views in that picture are all this one person.${keep}`\n : `${what} Keep this exact face, hair, skin tone and build, and show them once; several views in the picture are all this one person.${keep}`;\n }\n return flowing\n ? `${what} Keep its face, markings and colours exactly the same as in Image ${p.index}, and show it once; several views in that picture are all this one animal.${keep}`\n : `${what} Keep its exact face, markings and colours, and show it once; several views in the picture are all this one animal.${keep}`;\n}\n\n/** The type specimen: which lines it sets (gpt-image lists them; Gemini's Text section names the face). */\nfunction typeInstruction(ctx: PictureContext): string {\n if (ctx.flowing) return \"a sample of the brand typeface; its words are only a sample.\";\n if (ctx.allDesigned) return \"a sample of the brand typeface for every line; its words are only a sample.\";\n const lines = `line${ctx.designedLines.length === 1 ? \"\" : \"s\"} ${ctx.designedLines.join(\", \")}`;\n return `a sample of the brand typeface for ${lines}; its words are only a sample.`;\n}\n\n/** What the model does with one picture, by its role (openai §3; gemini §4 for the \"exactly the same as Image N\" lock). */\nfunction pictureInstruction(p: Picture, ctx: PictureContext): string {\n const what = p.spec ? sentence(plain(p.spec.what, ctx.clean)) : \"\";\n const keep = p.spec?.keep ? ` Keep ${plain(p.spec.keep, ctx.clean)}.` : \"\";\n switch (p.role) {\n case \"base\":\n return ctx.job === \"fix\"\n ? \"the current version of this ad, to edit.\"\n : \"the finished ad, to re-lay for this shape.\";\n case \"identity\":\n return identityInstruction(p, what, keep, ctx.flowing);\n // The user saw one label come back two ways (E4 gallery, BENCH.md § \"E1 v2\"): the label is the product's.\n case \"product\":\n return ctx.flowing\n ? `${what} Keep it and its label, text and design, exactly as in Image ${p.index}.${keep}`\n : `${what} Reproduce this exact product and its label, text and design unchanged.${keep}`;\n case \"logo\":\n return logoInstruction(p, ctx);\n case \"prop\": {\n const brand = ctx.thirdParty.find((b) => b.logo === p.label);\n if (brand) return `${thirdPartyLogo(brand, ctx.clean)}. Reproduce it exactly as drawn, readable.`;\n return `${what} Show it as it looks here.${keep}`;\n }\n case \"type\":\n return typeInstruction(ctx);\n case \"layout\":\n return \"the ad this one is built on, for its structure only: how it is built (its container, its layout, its pacing); never its words, brand, logo, people, products or colours, which belong to another advertiser.\";\n default:\n return `${what} Show it as it looks here.${keep}`;\n }\n}\n\n/** Every picture the render could be handed, before ordering: the edit base, the spec's references, logo, type, layout. */\nfunction wiredPictures(\n spec: AdSpec,\n opts: BriefOptions,\n job: BriefJob,\n designed: boolean,\n): Array<Omit<Picture, \"index\">> {\n const images = opts.images;\n const refs = spec.references.flatMap((r) => {\n const ref = images ? images.refs?.[r.id] : r.id;\n return ref ? [{ role: r.role, label: r.id, ref, spec: r }] : [];\n });\n const optional: Array<[boolean, BriefImageRole, string, string | undefined]> = [\n [job !== \"generate\", \"base\", \"AD\", images?.base],\n [spec.logo, \"logo\", \"LOGO\", images?.logo],\n [designed, \"type\", \"TYPE\", images?.type],\n // The reference, for its structure only, when the ad is built on its concept (C5: the image\n // model never saw the reference, 0/127 renders); `sourceLayout` decides when given.\n [job === \"generate\" && (opts.sourceLayout ?? keptConcept(spec) !== undefined), \"layout\", \"LAYOUT\", images?.layout],\n ];\n const extras = optional.flatMap(([wanted, role, label, ref]) => (wanted && ref ? [{ role, label, ref }] : []));\n return [...refs, ...extras];\n}\n\n/** The pictures of this render, in the profile's order, numbered, one role each (a ref wired twice keeps its first role). */\nfunction pictures(\n spec: AdSpec,\n opts: BriefOptions,\n job: BriefJob,\n profile: ImageModelProfile,\n designed: boolean,\n): Picture[] {\n const order = profile.brief.referenceOrder;\n const sorted = wiredPictures(spec, opts, job, designed)\n .map((p, i) => ({ p, i }))\n .sort((a, b) => order.indexOf(a.p.role) - order.indexOf(b.p.role) || a.i - b.i)\n .map(({ p }) => p);\n const seen = new Set<string>();\n const unique: Array<Omit<Picture, \"index\">> = [];\n for (const p of sorted) {\n if (seen.has(p.ref)) continue;\n seen.add(p.ref);\n unique.push(p);\n }\n return unique.map((p, i) => ({ ...p, index: i + 1 }));\n}\n\n// --- The copy. ---\n\nconst SYMBOL_NAMES: Record<string, string> = {\n \"®\": \"a small raised registered-trademark sign\",\n \"™\": \"a small raised trademark sign\",\n \"©\": \"a copyright sign\",\n \"€\": \"the euro sign\",\n \"£\": \"the pound sign\",\n};\n\n/** A copy line as the brief quotes it: on one line, its breaks said in words (a quoted newline would split the numbered list). */\nexport function quotedCopy(text: string): string {\n return text\n .split(/\\s*\\n\\s*/)\n .filter(Boolean)\n .join(\" \");\n}\n\nfunction lineBreaks(text: string): string {\n const lines = text.split(/\\s*\\n\\s*/).filter(Boolean);\n if (lines.length < 2) return \"\";\n const ends = lines.slice(0, -1).map((l) => `“${l.split(/\\s+/).at(-1)}”`);\n return `, on ${lines.length} lines, breaking after ${ends.join(\" and \")}`;\n}\n\n/** The first `max` words of a note, without a dangling mark; nothing at 0. */\nfunction clipNote(text: string, max: number): string {\n if (wordCount(text) <= max) return text;\n return text\n .split(/\\s+/)\n .slice(0, max)\n .join(\" \")\n .replace(/[,;:.]+$/, \"\");\n}\n\n/**\n * A line's role in words, so the model sets each line at its rank (the old prompt carried it on\n * 212/216; BENCH.md § E1). Body text, the default rank, and unknown roles are left unnamed.\n */\nconst ROLE_WORDS: Readonly<Record<string, string>> = {\n headline: \"the headline\",\n title: \"the headline\",\n subhead: \"the subhead\",\n subheadline: \"the subhead\",\n subtitle: \"the subhead\",\n price: \"the price\",\n stat: \"the key figure\",\n figure: \"the key figure\",\n number: \"the key figure\",\n discount: \"the discount\",\n cta: \"the button\",\n button: \"the button\",\n call_to_action: \"the button\",\n caption: \"a caption\",\n fine_print: \"the fine print\",\n legal: \"the fine print\",\n disclaimer: \"the fine print\",\n footnote: \"the fine print\",\n label: \"a label\",\n list_item: \"a list item\",\n bullet: \"a list item\",\n badge: \"a badge\",\n quote: \"the quote\",\n testimonial: \"the quote\",\n attribution: \"who said it\",\n rating: \"the rating\",\n tagline: \"the tagline\",\n};\n\nconst roleKey = (role: string | undefined): string =>\n (role ?? \"\")\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\");\nconst roleWords = (line: AdCopyLine): string => ROLE_WORDS[roleKey(line.role)] ?? \"\";\n\n/** `face` null: the face is named once, above the lines (`faceOnce`). */\nfunction copyLineText(line: AdCopyLine, n: number, face: string | null, k: Clean, level: number): string {\n const cap = NOTE_CAP[level] ?? 0;\n const look = line.look ? clipNote(plain(line.look, k), cap) : \"\";\n const note = line.position ? clipNote(plain(line.position, k), cap) : \"\";\n const position = note ? `; ${note}` : \"\";\n const text = quotedCopy(line.text);\n const detail = `${look ? (line.lettering === \"in_scene\" ? `: ${look}` : `, ${look}`) : \"\"}${lineBreaks(line.text)}${position}`;\n const lettered =\n line.lettering === \"in_scene\"\n ? line.on\n ? `lettered on ${plain(line.on, k)}`\n : \"lettered on its object in the scene\"\n : face\n ? `set in ${face}`\n : \"\";\n const lead = [roleWords(line), lettered].filter(Boolean).join(\", \") || \"designed\";\n return sentence(`${n}. \"${text}\" — ${lead}${detail}`);\n}\n\n/** Symbols named in words and accents listed, so both survive the render (openai §2). */\nfunction textNotes(copy: AdCopyLine[], profile: ImageModelProfile): string[] {\n const notes = Object.entries(SYMBOL_NAMES).flatMap(([symbol, name]) => {\n const lines = copy.flatMap((line, i) => (line.text.includes(symbol) ? [i + 1] : []));\n if (lines.length === 0) return [];\n const which =\n lines.length === 1 ? `line ${lines[0]}` : `lines ${lines.slice(0, -1).join(\", \")} and ${lines.at(-1)}`;\n return [`The ${symbol} in ${which} is ${name}.`];\n });\n const marks = [...new Set(copy.flatMap((l) => l.text.match(/[À-ÖØ-öø-ÿ¿¡]/g) ?? []))];\n if (profile.brief.text.keepAccents && marks.length > 0)\n notes.push(`Keep every accent and mark exactly: ${marks.join(\" \")}.`);\n return notes;\n}\n\n/** Roles whose line is the ad's button, read through `roleKey` as `ROLE_WORDS` reads them. */\nconst BUTTON_ROLES = new Set([\"cta\", \"button\", \"call_to_action\"]);\nconst isButton = (l: AdCopyLine) => BUTTON_ROLES.has(roleKey(l.role)) || /button/i.test(l.look ?? \"\");\n\n/** Roles whose line is the ad's key figure: it, not the headline, is the largest thing in the ad. */\nconst FIGURE_ROLES = new Set([\"price\", \"stat\", \"figure\", \"number\", \"discount\"]);\nconst HEADLINE_ROLES = new Set([\"headline\", \"title\"]);\nconst FINE_PRINT_ROLES = new Set([\"fine_print\", \"legal\", \"disclaimer\", \"footnote\"]);\n\ntype NumberedLine = { line: AdCopyLine; n: number };\n\n/**\n * The button's size, stated: the user found the call to action too small to see (E1 gallery,\n * BENCH.md § \"E1 v2\"). A third of the width is a thumb-sized button on a phone feed.\n */\nconst BUTTON_SIZE = \"a solid button at least a third of the frame wide, its words large and bold\";\n\n/** The designed lines, numbered as the brief quotes them. */\nconst designedLines = (copy: readonly AdCopyLine[]): NumberedLine[] =>\n copy.flatMap((line, i) => (line.lettering === \"designed\" ? [{ line, n: i + 1 }] : []));\n\n/**\n * The type scale, one sentence: the key figure (a price, a stat) or else the headline is the\n * largest element, at least half the frame wide, and reads first; every line reads at a glance\n * on a phone (the user asked for bigger text again and again); the fine print is smallest. The old prompt won the judge on\n * \"the key figure's size and colour\" and type hierarchy (48% of its wins; BENCH.md § E1). The\n * third version picks its own lead (`hookDirection`), so it keeps only the fine print's rank.\n */\ntype Ranked = ReturnType<typeof rankedLines>;\n\nconst finePrint = (ranked: Ranked): NumberedLine[] =>\n ranked.designed.filter(({ line }) => FINE_PRINT_ROLES.has(roleKey(line.role)));\n\n/** A native disguise's call to action: what the post, the note or the chat would carry, never a big button (C6). */\nconst NATIVE_BUTTON =\n \"as the native element would carry it (a link, a reply, a small button of its own), never a big ad button\";\n\nfunction fullHierarchy(ranked: Ranked, lead: NumberedLine | undefined, native: boolean): string[] {\n const { designed, figure, headline, button } = ranked;\n const size = lead === figure ? \"very large\" : \"at least half the frame wide\";\n return [\n ...(lead ? [`line ${lead.n} largest (${size}), read first`] : []),\n ...(lead && figure && headline ? [`then line ${headline.n}`] : []),\n ...(button ? [`line ${button.n} ${native ? NATIVE_BUTTON : BUTTON_SIZE}`] : []),\n ...(designed.length > 1 ? [\"every line readable at a glance\"] : []),\n ...finePrint(ranked).map(({ n }) => `line ${n} smallest, still legible`),\n ];\n}\n\n/** The hierarchy in the few words a brief at its word budget can spare. */\nfunction shortHierarchy(ranked: Ranked, lead: NumberedLine | undefined, native: boolean): string[] {\n return [\n ...(lead ? [`line ${lead.n} largest, read first`] : []),\n ...(ranked.button\n ? [\n native\n ? `line ${ranked.button.n} as the native element would carry it`\n : `line ${ranked.button.n} a solid button a third of the frame wide`,\n ]\n : []),\n ...finePrint(ranked).map(({ n }) => `line ${n} smallest`),\n ];\n}\n\n/** The designed lines by rank: the key figure, the headline, the line that reads first, the button. */\nfunction rankedLines(copy: readonly AdCopyLine[]) {\n const designed = designedLines(copy);\n const numbered = copy.map((line, i) => ({ line, n: i + 1 }));\n // The figure and the headline lead whether they are set as type or lettered in the scene (C5).\n const byRole = (roles: ReadonlySet<string>) => numbered.find(({ line }) => roles.has(roleKey(line.role)));\n const figure = byRole(FIGURE_ROLES);\n const headline = byRole(HEADLINE_ROLES);\n const lead =\n figure ?? headline ?? designed.find(({ line }) => !isButton(line) && !FINE_PRINT_ROLES.has(roleKey(line.role)));\n return { designed, figure, headline, lead, button: designed.find(({ line }) => isButton(line)) };\n}\n\nfunction hierarchyText(c: Ctx): string {\n const ranked = rankedLines(c.spec.copy);\n // The third version picks its own lead (`hookDirection`).\n const lead = c.opts.variant === 2 ? undefined : ranked.lead;\n const native = c.spec.brand_presence === \"native\";\n const parts =\n c.level >= SHORT_DIRECTION_LEVEL ? shortHierarchy(ranked, lead, native) : fullHierarchy(ranked, lead, native);\n if (parts.length === 0) return \"\";\n return c.flowing ? sentence(capitalize(parts.join(\"; \"))) : `Hierarchy: ${sentence(parts.join(\"; \"))}`;\n}\n\n// --- Colour. ---\n\ntype ColourUse = { object: string; name: string; hex?: string };\n\n/** The colours the brand's roles give the parts this ad has. */\nfunction brandRoleColours(spec: AdSpec, brand: AdBrand): ColourUse[] {\n const designed = designedLines(spec.copy);\n const figure = designed.find(({ line }) => FIGURE_ROLES.has(roleKey(line.role)));\n const panel = spec.scene.look === \"photo\" && !isNativeCapture(spec);\n // Brief v2: a photo ad's band and the emphasised figure lost the brand's colour (BENCH.md § E1,\n // \"What the compiled brief drops\"); a native phone photo keeps no designed panel.\n const roles: Array<[boolean, string, string | undefined]> = [\n [spec.scene.look === \"designed\", \"the background\", brand.ground],\n [panel && designed.length > 0, \"any band or panel behind the words\", brand.ground],\n [designed.length > 0, \"the designed type\", brand.ink],\n [spec.copy.some(isButton), \"the button\", brand.cta],\n [Boolean(figure), figure ? `line ${figure.n} (or the shape it sits on)` : \"\", brand.accent],\n [\n !figure && spec.copy.some((l) => /underline|highlight|arrow/i.test(l.look ?? \"\")),\n \"underlines and highlights\",\n brand.accent,\n ],\n ];\n return roles.flatMap(([applies, object, hex]) => (applies && hex ? [{ object, name: colourName(hex), hex }] : []));\n}\n\n/**\n * A photo a phone takes as a post (a selfie, UGC, a screenshot): only it gets the phone-photo\n * realism cues and no designed panel. The brief once gave those cues to 116/216 ads, studio and\n * hero shots included (BENCH.md § E1, \"What the compiled brief drops\").\n */\nconst NATIVE_CAPTURE =\n /\\b(selfie|phone[- ]camera|phone photo|shot on (?:a )?phone|taken on (?:a )?phone|smartphone photo|ugc|user[- ]generated|webcam|front[- ]camera|snapshot|native caption)\\b/i;\n\n/** In the capture field itself, any phone names the camera (in the scene it may be a prop). */\nconst PHONE_CAMERA = /\\b(phone|smartphone|iphone)\\b/i;\n\nfunction isNativeCapture(spec: AdSpec): boolean {\n if (spec.scene.look !== \"photo\") return false;\n const capture = spec.scene.capture ?? \"\";\n return NATIVE_CAPTURE.test(`${spec.scene.description} ${capture}`) || PHONE_CAMERA.test(capture);\n}\n\n/** Every colour the brief states: tied to an object, and from the brand palette when there is one. */\nfunction colourUses(spec: AdSpec, brand: AdBrand | undefined, k: Clean): ColourUse[] {\n const allowed = brand\n ? new Set(\n [...brand.palette, brand.ground, brand.ink, brand.accent, brand.cta].filter((h): h is string => Boolean(h)),\n )\n : null;\n const own = spec.colors.flatMap((c): ColourUse[] => {\n if (c.hex && allowed && !allowed.has(c.hex)) return [];\n const tied = { object: plain(c.object, k), name: plain(c.name, k) };\n return [c.hex && allowed ? { ...tied, hex: c.hex } : tied];\n });\n if (!brand) return own;\n // A role colour the spec already ties to an object is said once, by the spec's object.\n const said = new Set(own.flatMap((u) => (u.hex ? [u.hex] : [])));\n return [...own, ...brandRoleColours(spec, brand).filter((u) => !u.hex || !said.has(u.hex))];\n}\n\n/** Relative luminance of a hex colour, 0 (black) to 1 (white). */\nfunction luminance(hex: string): number {\n const n = Number.parseInt(hex.slice(1), 16);\n return (0.2126 * ((n >> 16) & 255) + 0.7152 * ((n >> 8) & 255) + 0.0722 * (n & 255)) / 255;\n}\n\n/**\n * The brand's darkest and lightest colours, a plate for a logo of either kind: named as the\n * spec names them when it ties them to something, else by `colourName`.\n */\nfunction plateColours(spec: AdSpec, brand: AdBrand | undefined, k: Clean): ColourUse[] {\n if (!brand) return [];\n const hexes = [...new Set([brand.ink, brand.ground, brand.accent, brand.cta, ...brand.palette])].filter(\n (h): h is string => Boolean(h),\n );\n if (hexes.length === 0) return [];\n const sorted = [...hexes].sort((a, b) => luminance(a) - luminance(b));\n const ends = [...new Set([sorted[0], sorted.at(-1)])].filter((h): h is string => Boolean(h));\n const named = (hex: string) => spec.colors.find((c) => c.hex === hex)?.name;\n return ends.map((hex) => ({ object: \"the logo plate\", name: plain(named(hex) ?? colourName(hex), k), hex }));\n}\n\n// --- Direction. ---\n\n/** What the ad shows, by its picture when one is wired (the product first), else in words. */\nfunction heroOf(pics: readonly Picture[]): string {\n const product = pics.find((p) => p.role === \"product\");\n const identity = pics.find((p) => p.role === \"identity\" && p.spec);\n if (product) return `the product in Image ${product.index}`;\n if (identity?.spec) return `the ${personOrAnimal(identity.spec)} in Image ${identity.index}`;\n return MAIN_SUBJECT;\n}\n\nconst MAIN_SUBJECT = \"the main subject of the scene\";\n\n/**\n * The three versions a first render is asked for, each on its own axis so the person gets\n * options rather than three rolls of one picture: 0 the spec's scene as written (no direction of\n * its own, `directionSection`); 1 a different picture of the same message; 2 a different hook in\n * the same words. This writes 1 and 2. Bench E3\n * (.context/static-review/BENCH.md#E3): with \"the hero larger\" and \"rearranged\" as the second\n * and third, two judge-distinct options came back in 39% of ads. From `SHORT_DIRECTION_LEVEL`\n * a brief over its word budget gets each direction's short form.\n */\nfunction variantDirection(c: Ctx): string {\n const [long, short] = c.opts.variant === 1 ? pictureDirection(c) : hookDirection(c);\n // The other versions vary only what the concept does not rest on (C5).\n const keep = mustKeep(c);\n const kept = keep.length > 0 ? ` Keep ${keep.join(\" and \")} as it is.` : \"\";\n return c.level >= SHORT_DIRECTION_LEVEL ? `${short} ${DEPARTS_SHORT}${kept}` : `${long} ${DEPARTS}${kept}`;\n}\n\n/** The scene and each line's notes describe the first version; the others depart from them on purpose. */\nconst DEPARTS = \"It leads where it differs from the scene and line notes above.\";\nconst DEPARTS_SHORT = \"It leads over the notes above.\";\n\n/** A direction in full, and in the few words a brief at its word budget can spare. */\ntype Direction = [long: string, short: string];\n\n/** Lines that close or qualify an ad rather than hook it: the button, the small print, who said it. */\nconst isSupport = (l: AdCopyLine) =>\n isButton(l) || FINE_PRINT_ROLES.has(roleKey(l.role)) || roleKey(l.role) === \"attribution\";\n\n/**\n * Variant 2: the same words with another hook. The next line leads; with one hook line, that\n * line at a scale the first version does not use. Lines are named by number, so no word changes.\n */\nfunction hookDirection(c: Ctx): Direction {\n const hooks = c.spec.copy.flatMap((l, i) =>\n l.lettering === \"designed\" && !isSupport(l) ? [{ n: i + 1, text: l.text }] : [],\n );\n const [first, ...rest] = hooks;\n // The next line, never one picked for carrying a figure: a price-led hook broke concepts that were not about price.\n const lead = rest[0];\n if (first && lead)\n return [\n `a type-led layout with another hook: line ${lead.n} leads, set as the largest words in the ad and read first; line ${first.n} follows it, smaller; the picture sits behind or beside the words as support, with the same words.`,\n `line ${lead.n} leads, largest; line ${first.n} smaller.`,\n ];\n if (first)\n return [\n `a type-led layout: line ${first.n} set very large, across most of the frame's width, at least three times the size of every other line; the picture sits small behind or beside it as support, with the same words.`,\n `line ${first.n} very large, every other line small.`,\n ];\n const lettered = c.spec.copy.findIndex((l) => l.lettering === \"in_scene\");\n if (lettered >= 0) {\n const on = c.spec.copy[lettered]?.on;\n const object = on ? plain(on, c.clean) : `the object carrying line ${lettered + 1}`;\n return [\n `a closer framing of the words: ${object} seen close and large in the frame, so its words are the first thing read, with the same words.`,\n `${object} close and large.`,\n ];\n }\n const hero = heroOf(c.pics);\n return [\n `another arrangement: ${hero} on the other side of the frame, seen from another angle, with the same words.`,\n `${hero} on the other side, from another angle.`,\n ];\n}\n\n/** A scene the spec already frames close, so its other picture is the wide one. */\nconst CLOSE_FRAMING =\n /\\b(?:close[- ]?up|macro|detail shot|close(?: [a-z-]+){0,2} shot|tight(?:ly)? (?:crop|cropped|framed|shot))\\b/i;\n\n/** Variant 1: the same message in another picture, the other way from how the scene frames it. */\nfunction pictureDirection(c: Ctx): Direction {\n const hero = heroOf(c.pics);\n if (c.spec.scene.look === \"designed\") {\n const shown = hero === MAIN_SUBJECT ? \"the thing the message is about\" : hero;\n return [\n `a picture-led layout: ${shown} shown as one large picture filling most of the frame, the words set compactly over a calm part of it, with the same words.`,\n `a picture-led layout: ${shown} large, the words compact.`,\n ];\n }\n if (c.spec.scene.look === \"illustration\")\n return [\n `a close, simple drawing: ${hero} drawn large, filling most of the frame, on one flat background colour, with the same words.`,\n `a close, simple drawing of ${hero} on one flat colour.`,\n ];\n const scene = `${c.spec.scene.description} ${c.spec.scene.capture ?? \"\"}`;\n if (CLOSE_FRAMING.test(scene))\n return [\n `a wide shot: ${hero} smaller in the frame, in the full place around it, so the setting tells the story, with the same words.`,\n `a wide shot of ${hero} in its full setting.`,\n ];\n return [\n `a close-up: ${hero} fills most of the frame, seen from close, and the setting shrinks to a soft hint of its colour and light, with the same words.`,\n `a close-up of ${hero}, filling the frame.`,\n ];\n}\n\n// --- The compiler. ---\n\n/** Everything one brief is written from, resolved once. `level` trims optional detail to fit the word budget. */\ntype Ctx = {\n spec: AdSpec;\n brand: AdBrand | undefined;\n viewer: AdViewer | undefined;\n format: string;\n profile: ImageModelProfile;\n opts: BriefOptions;\n job: BriefJob;\n flowing: boolean;\n pics: Picture[];\n designedLines: number[];\n level: number;\n clean: Clean;\n};\n\n/**\n * Compile an ad spec into the prompt, the numbered pictures and the request params of one\n * render on one model. `brand` and `viewer` override the spec's own when given (the bench\n * varies them); `format` is the delivered format (\"9:16\", \"4:5\", …).\n */\nexport function compileStaticBrief(\n written: AdSpec,\n brandOverride: AdBrand | undefined,\n format: string,\n viewerOverride: AdViewer | undefined,\n profile: ImageModelProfile,\n opts: BriefOptions = {},\n): CompiledBrief {\n const spec = withRenderedLettering(written);\n const job = opts.job ?? \"generate\";\n const designedLines = spec.copy.flatMap((l, i) => (l.lettering === \"designed\" ? [i + 1] : []));\n const pics = pictures(spec, opts, job, profile, designedLines.length > 0);\n const base: Ctx = {\n spec,\n brand: brandOverride ?? spec.brand,\n viewer: viewerOverride ?? spec.viewer,\n format,\n profile,\n opts,\n job,\n flowing: profile.brief.structure === \"flowing_sentences\",\n pics,\n designedLines,\n level: 0,\n clean: {\n copy: spec.copy.map((l) => quotedCopy(l.text)),\n logo: pics.some((p) => p.role === \"logo\"),\n positive: profile.brief.constraints === \"positive_last\",\n },\n };\n let prompt = assemble(base);\n for (let level = 1; level <= MAX_TRIM && wordCount(prompt) > profile.brief.wordBudget; level++) {\n prompt = assemble({ ...base, level });\n }\n const p = profile.brief.params;\n return {\n prompt,\n references: base.pics.map(({ index, role, label, ref }) => ({ index, role, label, ref })),\n params: {\n n: job === \"generate\" ? p.n : 1,\n ...(p.quality ? { quality: p.quality } : {}),\n ...(p.imageSize ? { image_size: p.imageSize } : {}),\n },\n words: wordCount(prompt),\n };\n}\n\n/**\n * Trim levels: 1 drops the mood and the idea's device, 2 the arrangement and direction note,\n * 3 the viewer and the message, 4-8 cut the scene, 9 drops the capture, 10-13 shorten each copy\n * line's lettering and position notes (and from 10 the second and third versions' directions\n * and the hierarchy take their short form), 14 shortens a picture's instruction when an earlier\n * picture of its role explained it, 15 drops the screens and fill sentences and shortens the\n * people cue, 16 shortens the logo's instruction and each unwired product's description and drops\n * the concept's idea (what it must keep stays). The copy itself is never cut.\n */\nconst MAX_TRIM = 16;\n/** From this trim level the logo's instruction and an unwired product's description take their short forms. */\nconst LAST_RESORT_LEVEL = 16;\n/** The words an unwired product's description keeps at `LAST_RESORT_LEVEL`. */\nconst PRODUCT_NOTE_WORDS = 10;\n/** From this trim level the brief v2 extras (screens, fill, people) take their short form or go. */\nconst EXTRAS_SHORT_LEVEL = 15;\n/** From this trim level a photo's capture cues go: after the scene, before the directions. */\nconst CAPTURE_LEVEL = 9;\n/** From this trim level the second and third versions' directions take their short form. */\nconst SHORT_DIRECTION_LEVEL = 10;\n/** From this trim level a picture whose role an earlier one explained gets a one-line instruction. */\nconst SHORT_PICTURES_LEVEL = 14;\nconst NOTE_CAP = [...Array<number>(10).fill(Number.POSITIVE_INFINITY), 12, 8, 4, 0];\nconst SCENE_CAP = [\n Number.POSITIVE_INFINITY,\n Number.POSITIVE_INFINITY,\n Number.POSITIVE_INFINITY,\n Number.POSITIVE_INFINITY,\n 80,\n 50,\n 35,\n 25,\n 15,\n];\n\nfunction audienceText(viewer: AdViewer | undefined, level: number, k: Clean): string {\n if (!viewer || level >= 3) return \"\";\n const warmth: Record<string, string> = {\n cold: \", who are new to the brand\",\n warm: \", who already know the brand\",\n hot: \", who are ready to act\",\n };\n const knows = viewer.knows\n ? ` (what they already know: ${plain(viewer.knows, k)})`\n : (warmth[viewer.temperature ?? \"\"] ?? \"\");\n return `, made for ${plain(viewer.who, k)}${knows}`;\n}\n\n/** The one idea, unless it repeats a line of copy (the model would draw it as another line). */\nfunction ideaText(spec: AdSpec, level: number, k: Clean): string {\n const message = spec.message;\n if (!message || level >= 3) return \"\";\n const m = message.toLowerCase();\n const repeats = spec.copy.some((l) => m.includes(l.text.toLowerCase()) || l.text.toLowerCase().includes(m));\n return repeats ? \"\" : ` Its one idea: ${sentence(plain(message, k))}`;\n}\n\n/** 1. The deliverable, and whose it is and for whom (the intended use sets the polish: openai §1). */\nfunction deliverableSection(c: Ctx): string {\n const noun = deliverableNoun(c.format, c.opts.placements);\n const forBrand = c.brand?.name ? ` for ${plain(c.brand.name)}` : \"\";\n if (c.job === \"fix\") {\n const change = sentence(c.opts.instruction?.trim() || \"correct what is wrong\");\n // Its own sentence after the picture's: read as an instruction, never as the tail of a clause.\n return c.flowing\n ? `Image 1 is the current version of this ad. ${capitalize(change)} Keep everything else in Image 1 exactly the same, preserving its picture, layout, colours, lettering and every word.`\n : `Edit: Image 1 is the current version of this ad. ${capitalize(change)} Keep everything else exactly as it is in Image 1: the picture, the layout, the colours, the lettering and every word.`;\n }\n if (c.job === \"relay\") {\n return c.flowing\n ? `Image 1 is a finished ad. Recompose this same ad as a ${noun}${forBrand}: the same picture, colours, lettering and every word, rearranged for the new shape, with more of the scene revealed at the edges.`\n : `Deliverable: the same ad as Image 1, re-laid as a finished ${noun}${forBrand}. Keep the same picture, colours, lettering and every word, rearranged for the new shape; show more of the scene rather than cropping it.`;\n }\n const lead = c.flowing ? \"Create a finished\" : \"Deliverable: a finished\";\n return `${lead} ${noun}${forBrand}${audienceText(c.viewer, c.level, c.clean)}.${marketText(c)}${recognitionText(c)}${ideaText(c.spec, c.level, c.clean)}${conceptText(c)}${deviceText(c)}`;\n}\n\n/**\n * Where the ad is set (D1, audited Sept 2026: a Mexican WhatsApp ad came back with Spain's\n * people and euros): a one-country ad's places, shops, money and people are that country's; a\n * version that runs in several countries shows none of any one. Never trimmed: it is one line.\n */\nfunction marketText(c: Ctx): string {\n const market = c.viewer?.market;\n if (!market) return \"\";\n if (market.country === \"global\") {\n return c.flowing\n ? \" It is set in no one country: no flags, landmarks, money or shop signs of any country, and people who could live in any of them.\"\n : \"\\nSet in no one country: no flags, landmarks, money or shop signs of any country.\";\n }\n const where = marketCountryName(market) ?? market.country;\n const money = market.currency ? ` (${market.currency})` : \"\";\n return c.flowing\n ? ` It is set in ${where}: its places, shops, money${money} and people are ${where}'s, in everyday details, never a flag or a landmark added to say where.`\n : `\\nSet in ${where}: its places, shops, money${money} and people are ${where}'s, in everyday details, never a flag or a landmark added to say where.`;\n}\n\n/** How a stranger knows in a second what the ad is (D4): one line, never trimmed. */\nfunction recognitionText(c: Ctx): string {\n const what = c.spec.recognition ? plain(c.spec.recognition, c.clean) : \"\";\n if (!what) return \"\";\n return c.flowing\n ? ` A stranger knows at a glance it is ${sentence(what)}`\n : ` A stranger knows at a glance it is ${sentence(what)}`;\n}\n\n/** The concept a spec adapts, when it is one to keep (`use` or `adapt`). */\nconst keptConcept = (spec: AdSpec) => (spec.concept && spec.concept.verdict !== \"reject\" ? spec.concept : undefined);\n\n/** The load-bearing elements, each with what of ours carries it: \"the gift (two accounts free forever)\". */\nfunction mustKeep(c: Ctx): string[] {\n if (!conceptSummary(c.spec)) return [];\n return (c.spec.concept?.load_bearing ?? []).map((l) => {\n const ours = l.ours ? plain(l.ours, c.clean) : \"\";\n return `${plain(l.element, c.clean)}${ours ? ` (${ours})` : \"\"}`;\n });\n}\n\n/**\n * Why the reference won and what must stay of it (C5, audited Sept 2026: 12 of 33 creatives lost\n * the concept, and the brief barely carried it): the idea in its layers (the disguise it wears,\n * the voice, the trigger, what stops the scroll, the logic), then the load-bearing elements with\n * what of ours carries each. The idea goes only at the last trim level, after every cut of the\n * scene and the copy notes; what must stay never does.\n */\nfunction conceptText(c: Ctx): string {\n const kept = conceptSummary(c.spec) ? keptConcept(c.spec) : undefined;\n if (!kept) return \"\";\n const w = kept.why_it_wins;\n // The brief carries every layer; the review checks only the trick (`conceptSummary`).\n const layers = [w.disguise, w.voice, w.trigger, w.attention, w.logic].flatMap((t) =>\n t?.trim() ? [plain(t, c.clean)] : [],\n );\n const idea = layers.length > 0 && c.level < LAST_RESORT_LEVEL ? sentence(layers.join(\"; \")) : \"\";\n const keep = mustKeep(c);\n const keepLine = keep.length > 0 ? sentence(keep.join(\"; \")) : \"\";\n if (c.flowing) {\n return `${idea ? ` The idea: ${idea}` : \"\"}${keepLine ? ` It must keep ${keepLine}` : \"\"}`;\n }\n return `${idea ? `\\nThe idea: ${idea}` : \"\"}${keepLine ? `\\nMust keep: ${keepLine}` : \"\"}`;\n}\n\n/**\n * The device the ad is built on (`mechanism[0]`), one line, dropped at the first trim level.\n * The old prompt rebuilt each winning mechanism (201/216 prompts) and the brief printed it on\n * 1/216; adding it back closed part of the judge's gap (BENCH.md § E1, the `brief-idea-m` probe).\n */\nfunction deviceText(c: Ctx): string {\n const device = c.spec.mechanism[0]?.device;\n if (!device || c.level >= 1) return \"\";\n const text = plain(device, c.clean);\n if (!text) return \"\";\n if (!c.flowing) return `\\nIdea: ${sentence(capitalize(text))}`;\n const lower = /^\\p{Lu}\\p{Ll}/u.test(text) ? `${text[0]?.toLowerCase()}${text.slice(1)}` : text;\n return ` The idea it is built on: ${sentence(lower)}`;\n}\n\n/** 2. The pictures, each numbered with one role (openai §3, gemini §4). */\n/** What a spec picture shows, as one noun. */\nfunction pictureNoun(p: Picture): string {\n if (p.role === \"identity\" && p.spec) return personOrAnimal(p.spec);\n return p.role === \"product\" ? \"product\" : \"object\";\n}\n\n/** An earlier picture whose instruction already covers `p` (same role and kind), for the shortest briefs. */\nfunction explainedBefore(p: Picture, pics: readonly Picture[]): Picture | undefined {\n if (!p.spec) return undefined;\n return pics.find((q) => q.index < p.index && q.spec && q.role === p.role && pictureNoun(q) === pictureNoun(p));\n}\n\nfunction picturesSection(c: Ctx): string[] {\n if (c.pics.length === 0) return [];\n const ctx: PictureContext = {\n clean: c.clean,\n job: c.job,\n designedLines: c.designedLines,\n allDesigned: c.designedLines.length === c.spec.copy.length,\n plates: plateColours(c.spec, c.brand, c.clean),\n thirdParty: c.spec.third_party_brands,\n flowing: c.flowing,\n short: c.level >= LAST_RESORT_LEVEL,\n native: c.spec.brand_presence === \"native\",\n ...(c.spec.logo_position ? { logoPosition: c.spec.logo_position } : {}),\n };\n const lines = c.pics.map((p) => {\n const label = p.label ? ` (${p.label})` : \"\";\n const same = c.level >= SHORT_PICTURES_LEVEL ? explainedBefore(p, c.pics) : undefined;\n const instruction = same\n ? `another ${pictureNoun(p)}${c.flowing ? \", under\" : \";\"} the same rules as Image ${same.index}.`\n : pictureInstruction(p, ctx);\n return c.flowing ? `Image ${p.index}${label} is ${instruction}` : `Image ${p.index}${label}: ${instruction}`;\n });\n return [c.flowing ? lines.join(\"\\n\") : `References:\\n${lines.join(\"\\n\")}`];\n}\n\nconst DESIGNED_LOOK = {\n flowing:\n \"It is a clean designed ad graphic with one dominant element and a clear reading order, and its type reads at a glance on a phone.\",\n labelled:\n \"Look: a clean designed ad graphic: one dominant element, a clear reading order, crisp type that reads on a phone.\",\n};\nconst ILLUSTRATION_LOOK = {\n flowing: \"It is an illustration with one dominant element and a clear reading order.\",\n labelled: \"Look: an illustration: one dominant element, a clear reading order.\",\n};\n\n/** A photo's look: the profile's realism vocabulary and the spec's capture cues, never lens figures (openai §5, gemini §6). */\nfunction photoLook(c: Ctx): string {\n const r = isNativeCapture(c.spec) ? c.profile.brief.realism.native : c.profile.brief.realism.produced;\n const capture = c.spec.scene.capture && c.level < CAPTURE_LEVEL ? plain(c.spec.scene.capture, c.clean) : \"\";\n if (!c.flowing) return `Look: ${r.photo}${capture ? ` — ${capture}` : \"\"}; ${r.texture}.`;\n const purpose = r.purpose ? `, ${r.purpose}` : \"\";\n return `It is ${r.photo}${capture ? `, ${capture}` : \"\"}, with ${r.texture}${purpose}.`;\n}\n\n/** The user found a dog \"very AI\" (BENCH.md § \"Fix round\"). Dropped with the other extras at the last trim. */\nfunction animalsText(c: Ctx): string {\n if (!showsAnimals(c.spec) || c.level >= EXTRAS_SHORT_LEVEL) return \"\";\n return c.flowing\n ? \" The animals look real, with real fur, natural light and a candid moment.\"\n : \" Animals look real: real fur, natural light, a candid moment.\";\n}\n\nfunction lookText(c: Ctx): string {\n const people = `${showsPeople(c.spec) ? peopleText(c) : \"\"}${animalsText(c)}`;\n if (c.spec.scene.look === \"photo\") return `${photoLook(c)}${people}`;\n const look = c.spec.scene.look === \"illustration\" ? ILLUSTRATION_LOOK : DESIGNED_LOOK;\n return `${c.flowing ? look.flowing : look.labelled}${people}`;\n}\n\n/** People named in a scene: the real-person cues apply to them (hands alone do not need faces). */\nexport const PEOPLE =\n /\\b(person|people|wom[ae]n|m[ae]n|girls?|boys?|child(?:ren)?|kids?|baby|parents?|mother|father|mum|mom|dad|family|couple|customers?|founder|presenter|technician|nurse|doctor|workers?|students?|friends|athlete|chef|faces?|selfie|portrait|plumbers?|electricians?|drivers?|owners?|managers?|employees?|engineers?|developers?|teachers?|mechanics?|carpenters?|tradesm[ae]n|advis[oe]rs?|agents?|riders?|couriers?)\\b/i;\n\n/**\n * Photoreal people in the ad, in any capture: a person picture is wired, or a photo's scene\n * names someone. The user found the people \"too AI\" and some faces too dark (BENCH.md § \"E1 v2\").\n */\nexport function showsPeople(spec: AdSpec): boolean {\n if (spec.scene.look === \"illustration\") return false;\n // By the role the picture plays (C5): a prop that shows a person (a chat on a phone) is no person.\n if (spec.references.some((r) => r.role === \"identity\" && !isAnimal(r))) return true;\n return (\n spec.scene.look === \"photo\" && PEOPLE.test(withoutAbsences(`${spec.scene.description} ${spec.scene.layout ?? \"\"}`))\n );\n}\n\n/** The rest of an absence's clause, up to the next thing the scene names (\"… and a woman cooking\"). */\nconst CLAUSE_REST = String.raw`(?:(?!\\b(?:and|but|while|with|as|where|whereas)\\b)[^.;,])*`;\nconst ABSENCE = new RegExp(\n String.raw`\\b(?:no|without|nor|never any|not a single)\\s+(?:\\w+\\s+){0,2}?(?:people|persons?|humans?|one|hands?|faces?|figures?)\\b${CLAUSE_REST}|\\b(?:nobody|no-one|no one)\\b${CLAUSE_REST}`,\n \"gi\",\n);\n\n/** A manager, an agent or a driver that is software: \"a password manager app\", \"AI agents\", \"USB drivers\". */\nconst SOFTWARE_ROLE =\n /\\b(?:password|file|task|project|package|window|device|usb|printer|graphics|gpu|ai|software|browser|download|display|audio|print)\\s+(?:managers?|agents?|drivers?)\\b|\\b(?:managers?|agents?|drivers?)\\s+(?:apps?|software|tools?|dashboards?|platforms?|programs?|updates?|panels?|screens?|extensions?|plugins?)\\b/gi;\n\n/**\n * A scene's words with what it says is absent taken out: \"no people appear anywhere and no\n * hands\", \"an office without people\", \"nobody at the desks\" name people that are not there, a\n * \"first-person headline\" names a voice, \"a developer's desk\" an owner, and \"a password manager\n * app\" software, not a person. An absence ends at the next thing the scene names.\n */\nfunction withoutAbsences(text: string): string {\n return (\n text\n .replace(/\\b(?:first|second|third)[- ]person\\b/gi, \" \")\n // \"a developer's desk\" says whose the desk is: nobody stands in the picture for it.\n .replace(/\\b\\w+['’]s\\b/g, \" \")\n .replace(SOFTWARE_ROLE, \" \")\n .replace(ABSENCE, \" \")\n );\n}\n\n/**\n * The real-person cue, and where the person's gaze or pose leads: to the button, else to the line\n * that reads first. The user: \"man looking to the CTA maximizes conversion\" (E4 gallery).\n */\nfunction peopleText(c: Ctx): string {\n const cue = c.profile.brief.realism.people;\n if (!cue) return \"\";\n const { button, lead } = rankedLines(c.spec.copy);\n const target = button ?? lead;\n const said = c.level >= EXTRAS_SHORT_LEVEL ? \"People look real, faces well lit.\" : sentence(capitalize(cue));\n // Tier 1, Oct 2026: a course for 45-60 year olds came back with young, good-looking, \"very AI\" faces.\n const who = c.viewer?.who ? \" Ordinary people the viewer's age: real faces with lines, everyday clothes.\" : \"\";\n return ` ${said}${who}${target ? ` Their gaze or pose leads toward line ${target.n}.` : \"\"}`;\n}\n\n/** Things a render tends to draw glossy and computer-made: people, animals, phone and app screens. */\nconst looksRendered = (spec: AdSpec): boolean => showsPeople(spec) || showsAnimals(spec) || showsScreen(spec);\n\n/** Animals named in a photo, or an animal picture wired. */\nconst ANIMALS = /\\b(dogs?|puppy|puppies|cats?|kittens?|pets?|horses?|birds?|rabbits?|animals?)\\b/i;\n\nfunction showsAnimals(spec: AdSpec): boolean {\n if (spec.scene.look === \"illustration\") return false;\n if (spec.references.some((r) => r.role === \"identity\" && isAnimal(r))) return true;\n return spec.scene.look === \"photo\" && ANIMALS.test(`${spec.scene.description} ${spec.scene.layout ?? \"\"}`);\n}\n\n/** A phone, app or web screen in the ad. */\nconst SCREEN =\n /\\b(apps?|screens?|lock[- ]screen|dashboard|interface|mockup|notification|laptop|tablet|browser|website|chat bubbles?|ui)\\b/i;\n\nfunction showsScreen(spec: AdSpec): boolean {\n const ons = spec.copy.map((l) => l.on ?? \"\").join(\" \");\n return SCREEN.test(`${spec.scene.description} ${spec.scene.layout ?? \"\"} ${ons}`);\n}\n\n/**\n * A device's screen drawn as a real one: the user found the old prompt's app mockups more\n * polished (E1 gallery, BENCH.md § \"E1 v2\"), then the brief's phone screenshot \"very AI\"\n * (§ \"Fix round\"): a real phone photographed or a real screenshot, never a glossy render. Its\n * words stay inside the allow-list.\n */\nconst SCREENS = {\n labelled:\n \"Screens: a real phone photographed, or a real screenshot: real app density, a real status bar, flat true-to-life colours; any words on them from the quoted lines.\",\n flowing:\n \"Every phone or app screen looks like a real phone photographed or a real screenshot, with real app density, a real status bar and flat true-to-life colours, any words on it from the quoted lines.\",\n};\n\n/** A picture of the company's own screen, wired: what every screen in the ad shows. */\nconst SCREENSHOT = /\\b(screenshots?|screen ?grabs?|screens?)\\b/i;\nconst screenshotOf = (pics: readonly Picture[]): Picture | undefined =>\n pics.find((p) => p.spec && p.role !== \"identity\" && SCREENSHOT.test(p.spec.what));\n\nfunction screensText(c: Ctx): string {\n const shot = screenshotOf(c.pics);\n if (shot) {\n return c.flowing\n ? ` Every screen shows Image ${shot.index} exactly as it is, on a real phone, photographed.`\n : `\\nScreens: every screen shows Image ${shot.index} exactly as it is, on a real phone, photographed.`;\n }\n if (!showsScreen(c.spec) || c.level >= EXTRAS_SHORT_LEVEL) return \"\";\n return c.flowing ? ` ${SCREENS.flowing}` : `\\n${SCREENS.labelled}`;\n}\n\n/**\n * A product the spec names but no picture is wired for, stated on its own so trimming the scene\n * never drops it: the serum bottle went with the scene from two of three versions, and the pick\n * had none (BENCH.md § \"Final\").\n */\nfunction unwiredProductsText(c: Ctx): string {\n const wired = new Set(c.pics.map((p) => p.label));\n return c.spec.references\n .filter((r) => r.role === \"product\" && !wired.has(r.id))\n .map((r) => {\n const what = plain(r.what, c.clean).replace(/[.!?]+$/, \"\");\n const told = c.level >= LAST_RESORT_LEVEL ? clipNote(what.split(/[,;]/)[0] ?? what, PRODUCT_NOTE_WORDS) : what;\n return ` The product, ${told}, is in the frame, whole and clearly visible.`;\n })\n .join(\"\");\n}\n\n/**\n * A person nobody cast (no Avatar, so no picture wired): stated in the scene so a trimmed\n * description cannot drop them, and stated as somebody real, because a person the model\n * invents unprompted comes back as a glossy model (AV1, scripts/creative-evals).\n */\nfunction unwiredPeopleText(c: Ctx): string {\n const wired = new Set(c.pics.map((p) => p.label));\n return c.spec.references\n .filter((r) => r.role === \"identity\" && r.id.startsWith(\"PERSON_\") && !wired.has(r.id))\n .map((r) => {\n const what = plain(r.what, c.clean).replace(/[.!?]+$/, \"\");\n const told = c.level >= LAST_RESORT_LEVEL ? clipNote(what.split(/[,;]/)[0] ?? what, PRODUCT_NOTE_WORDS) : what;\n return ` The person, ${told}, is an invented, ordinary real person — real skin, unposed, never a model.`;\n })\n .join(\"\");\n}\n\n/** An ad that shows a before and an after (a split, two days, two states), in any of the corpus's languages. */\nexport const BEFORE_AFTER =\n /\\bbefore\\b[^.]{0,40}\\bafter\\b|\\bantes\\b[^.]{0,30}\\bdespu[eé]s\\b|\\bavant\\b[^.]{0,30}\\bapr[eè]s\\b|\\bvorher\\b[^.]{0,30}\\bnachher\\b|\\bprima\\b[^.]{0,30}\\bdopo\\b/i;\n\nexport function isBeforeAfter(spec: AdSpec): boolean {\n const told = [spec.scene.description, spec.scene.layout, spec.direction, ...spec.mechanism.map((m) => m.device)];\n return BEFORE_AFTER.test(told.filter(Boolean).join(\" \\n \"));\n}\n\n/** The serum's two faces barely differed, in every size (BENCH.md § \"Final\"). */\nconst BEFORE_AFTER_TEXT =\n \" The two states it compares differ clearly at a glance, even as a thumbnail; only the result changes between them.\";\n\n/** 3. The scene and its look, for a first render only: a re-lay and a fix take them from Image 1. */\nfunction sceneSection(c: Ctx): string[] {\n if (c.job !== \"generate\") return [];\n const scene = c.spec.scene;\n const products = `${unwiredProductsText(c)}${unwiredPeopleText(c)}${isBeforeAfter(c.spec) ? BEFORE_AFTER_TEXT : \"\"}`;\n // The scene's own sentence about an unwired product goes: the product line says it whole.\n const told = normalized(products);\n const own = (sentence(plain(scene.description, c.clean)).match(/[^.!?]+[.!?]*/g) ?? [])\n .filter((s) => !products || !told.includes(normalized(s.replace(/^\\s*in the frame:?/i, \"\"))))\n .join(\"\")\n .trim();\n const description = capitalize(clip(own || sentence(plain(scene.description, c.clean)), SCENE_CAP[c.level] ?? 15));\n const mood = scene.mood && c.level < 1 ? ` The mood is ${sentence(plain(scene.mood, c.clean))}` : \"\";\n const out = [\n c.flowing\n ? `${description}${products}${mood} ${lookText(c)}${screensText(c)}${otherBrandsText(c)}`\n : `Scene: ${description}${products}${mood}\\n${lookText(c)}${screensText(c)}${otherBrandsText(c)}`,\n ];\n const layout =\n scene.layout && c.level < 2 ? unsaid(plain(scene.layout, c.clean), plain(scene.description, c.clean)) : \"\";\n if (layout) out.push(c.flowing ? sentence(capitalize(layout)) : `Arrangement: ${sentence(layout)}`);\n return out;\n}\n\n/** 4. The words: numbered, quoted, each once, lettered per line, allow-listed (openai §2, gemini §3). */\nfunction textSection(c: Ctx): string {\n const copy = c.spec.copy;\n if (copy.length === 0)\n return c.flowing ? \"The picture carries the whole ad on its own.\" : \"Text: none; the picture is the ad.\";\n const typePic = c.pics.find((p) => p.role === \"type\");\n const face = typePic\n ? `the brand typeface (Image ${typePic.index})`\n : c.brand?.typeface\n ? plain(c.brand.typeface, c.clean)\n : \"a clean, bold sans-serif\";\n // Lines that share one face name it once, above them (brief v2 spends the words on the logo\n // and the button); in-scene lines still say what they are lettered on. gpt-image's type picture\n // lists its lines itself; Gemini's names the face here.\n const faceOnce = c.designedLines.length > 1;\n const named = faceOnce && (!typePic || c.flowing);\n const label = productLabel(c);\n const header = c.flowing\n ? `The ad carries exactly these ${copy.length} lines of text, each once and spelled exactly as quoted${named ? `, the designed type set in ${face}` : \"\"}:`\n : `Text: exactly these ${copy.length} lines, each once, spelled exactly as quoted; they are the only words in the image${label}${named ? `; designed type in ${face}` : \"\"}:`;\n return [\n header,\n ...copy.map((l, i) => copyLineText(l, i + 1, faceOnce ? null : face, c.clean, c.level)),\n ...[hierarchyText(c)].filter(Boolean),\n ...textNotes(copy, c.profile),\n ].join(\"\\n\");\n}\n\n/** A wired product keeps its own label's words, a wired screenshot its screen's: the allow-list makes room for them. */\nfunction productLabel(c: Ctx): string {\n const shot = screenshotOf(c.pics);\n if (shot) return ` besides the words on Image ${shot.index}'s screen`;\n return c.pics.some((p) => p.role === \"product\") ? \" besides the product's own label\" : \"\";\n}\n\n/** 5. Colour, named and tied to objects, brand palette only (research §1 BFL; openai §7). */\nfunction colourSection(c: Ctx): string[] {\n if (c.job !== \"generate\") return [];\n const uses = colourUses(c.spec, c.brand, c.clean);\n if (uses.length === 0) return [];\n const list = uses.map((u) => `${u.object} in ${u.name}${u.hex ? ` (${u.hex})` : \"\"}`).join(\"; \");\n return [c.flowing ? sentence(capitalize(list)) : `Colour: ${sentence(list)}`];\n}\n\n/** 6. The safe area, by what fills each band, from its one owner. */\nfunction layoutSection(c: Ctx): string {\n const items = keptInside(\n c.pics.some((p) => p.role === \"logo\"),\n c.spec.copy.some(isButton),\n );\n const safe = c.opts.zoned\n ? \"\"\n : c.opts.band?.margin\n ? marginText(c.opts.band, items, c.profile.brief.structure)\n : c.opts.band\n ? bandText(c.opts.band, items, c.flowing, staticSafeArea(c.format, c.opts.placements).placement !== \"feed\")\n : safeAreaText(c.format, c.opts.placements, items, c.profile.brief.structure);\n const out = [safe, legibilityText(c), fillText(c)].filter(Boolean).join(\" \");\n return c.flowing ? out : `Layout: ${out}`;\n}\n\n/**\n * One legibility sentence: \"every line sits on a ground it reads against\" was on every old\n * prompt and on 2/216 briefs, and contrast and the logo dark on dark were the judge's reasons\n * (BENCH.md § E1). The logo is never recoloured, so its ground is where it goes.\n */\nfunction legibilityText(c: Ctx): string {\n const logo = c.pics.some((p) => p.role === \"logo\");\n if (c.spec.copy.length === 0) return logo ? \"The logo sits on a ground it reads against.\" : \"\";\n return logo\n ? \"Every line and the logo sit on a ground they read against.\"\n : \"Every line sits on a ground it reads against.\";\n}\n\n/** A concept that is meant to be sparse: it keeps its empty space. */\nconst MINIMAL = /\\b(minimal(?:ist)?|negative space|white space|empty space|sparse|airy)\\b/i;\n\n/**\n * The frame filled on purpose: the user found the briefs' ads emptier than the old prompt's\n * (\"less empty white space\", E1 gallery, BENCH.md § \"E1 v2\"), unless the concept is minimal.\n */\nfunction fillText(c: Ctx): string {\n // The second and third versions' directions say how they fill the frame.\n if (c.job !== \"generate\" || c.level >= EXTRAS_SHORT_LEVEL || c.opts.variant === 1 || c.opts.variant === 2) return \"\";\n const { scene, direction } = c.spec;\n if (MINIMAL.test([scene.description, scene.mood, scene.layout, direction].join(\" \"))) return \"\";\n return c.flowing ? \"The subject and the words fill the frame with purpose.\" : \"Fill the frame; no large empty areas.\";\n}\n\n/** 7. Direction: which of the three reads this render takes, and the concept's own note. */\nfunction directionSection(c: Ctx): string[] {\n if (c.job !== \"generate\") return [];\n const note = c.spec.direction && c.level < 2 ? sentence(plain(c.spec.direction, c.clean)) : \"\";\n const variant = c.opts.variant ?? 0;\n // The first version is the brief as written: it needs no direction of its own.\n if (variant !== 1 && variant !== 2) return note ? [c.flowing ? capitalize(note) : `Direction: ${note}`] : [];\n const direction = variantDirection(c);\n return [c.flowing ? `This version is ${direction} ${note}`.trim() : `Direction: ${direction} ${note}`.trim()];\n}\n\n/** Gemini's closing allow-list, phrased positively (gemini §2). */\nfunction positiveConstraints(c: Ctx): string {\n const n = c.spec.copy.length;\n const logo = c.pics.find((p) => p.role === \"logo\");\n const others = thirdPartyNames(c.spec);\n if (n === 0)\n return `The only words in the finished image are none at all: it is picture only${logo ? `, with the one logo from Image ${logo.index}` : \"\"}.`;\n const label = productLabel(c) ? `,${productLabel(c)}` : \"\";\n const onlyLogo = !logo\n ? \"\"\n : others\n ? `, and the logos are the one in Image ${logo.index} and ${others}, the only brand marks anywhere, vehicles, packaging and clothing included`\n : `, and the one logo is the one in Image ${logo.index}, the only brand mark anywhere, vehicles, packaging and clothing included`;\n return `The only words in the finished image are the ${n} quoted lines above, each exactly once; every other surface carries picture only${label}${onlyLogo}.`;\n}\n\n/** 8. The constraints, last (openai §7; Gemini phrased positively, gemini §2). */\nfunction constraintsSection(c: Ctx): string {\n if (c.profile.brief.constraints === \"positive_last\") return positiveConstraints(c);\n const n = c.spec.copy.length;\n const logo = c.pics.find((p) => p.role === \"logo\");\n const logos = [\n ...(logo ? [`Image ${logo.index}`] : []),\n ...(thirdPartyNames(c.spec) ? [thirdPartyNames(c.spec)] : []),\n ];\n const avoid = [\n n > 0 ? `any words other than the ${n} quoted lines` : \"any words or letters\",\n // A Toyota badge appeared on a car-loan ad's SUV (BENCH.md § \"Final\"): the allow-list covers the scene's things too.\n // The words and the identity's \"show them once\" are said above; brief v2 spends the words on the logo and the button.\n `${logos.length > 0 ? `any logo, badge, icon, seal or sticker other than ${logos.join(\" and \")}` : \"any logo or wordmark, badge, icon, seal or sticker\"}, on vehicles, packaging and clothing too`,\n \"watermarks\",\n ...(c.job === \"generate\" && isNativeCapture(c.spec) ? [\"a staged, retouched or studio-lit look\"] : []),\n // The user found the people, a dog and a phone screen \"very AI\" (BENCH.md § \"Fix round\").\n ...(c.job !== \"generate\"\n ? []\n : showsPeople(c.spec)\n ? [\"glossy, CGI or airbrushed skin; faces in shadow\"]\n : looksRendered(c.spec)\n ? [\"a glossy CGI look\"]\n : []),\n ];\n return `Avoid: ${avoid.join(\"; \")}.`;\n}\n\nfunction assemble(c: Ctx): string {\n return [\n deliverableSection(c),\n ...picturesSection(c),\n ...sceneSection(c),\n textSection(c),\n ...colourSection(c),\n layoutSection(c),\n ...directionSection(c),\n constraintsSection(c),\n ].join(\"\\n\\n\");\n}\n\n/** The copy lines a compiled brief quotes, in order (critique, set-prompt and the review read them back). */\nexport function briefCopyLines(prompt: string): string[] {\n return [...prompt.matchAll(/^\\d+\\. \"(.*)\" — /gm)].map((m) => m[1] ?? \"\");\n}\n\n// --- The judge. ---\n\n/**\n * The criteria the static judge (`image_select`) compares the candidates on: the brief they\n * were rendered from, pairwise and in both orders, checks first. VLMs rank but cannot score\n * (research §5), so it ranks. Given the brand and the viewer, so \"reads as the brand's\" and\n * \"one idea for this viewer\" are judged against someone rather than no one.\n *\n * Kept as is after E9 (BENCH.md § \"E9 — judge calibrated to the user\"): on 100 pairs the user\n * rated blind, four rewrites around what the user named (logo, button, text size, people,\n * polish; ranked gates, an overall call, no brief, a line per image) and Gemini Pro never beat\n * it; on the held-out third it picks the user's ad in 13 of the 20 pairs it decides, as often\n * as Claude Opus, and the best tuned rewrite fell to 10 of 17.\n */\nexport function judgeBrief(\n brief: string,\n opts: { brand?: string; viewer?: string; mechanisms?: readonly string[] } = {},\n): string {\n const whose = opts.brand ? ` for ${opts.brand}` : \"\";\n const forWhom = opts.viewer ? `, made for ${opts.viewer}` : \"\";\n const mechanism =\n opts.mechanisms && opts.mechanisms.length > 0 ? `; the ad still rests on ${opts.mechanisms.join(\"; \")}` : \"\";\n return [\n `You compare candidate versions of ONE static ad${whose}${forWhom}, all rendered from the BRIEF below. Compare the candidates with each other and pick the one that best delivers the brief; a candidate that fails an earlier check loses to one that passes it:`,\n \"1. Words: every quoted line of the brief is printed exactly once, spelled exactly (accents and symbols included), and no other words appear.\",\n \"2. Picture: no AI artifacts on people, animals or products (hands, faces, mouths, warped objects), no seams or smears.\",\n \"3. Space: every word, the logo and the button sit where the brief's Layout keeps them; nothing is clipped or overlapping.\",\n \"4. One idea: one dominant element carries the idea and still reads at a glance shrunk to a phone-feed thumbnail.\",\n `5. Brief: the scene, the lettering of each line, the colours and the pictures are the ones the brief asks for${mechanism}.`,\n `6. Brand: with any logo covered, the ad still reads as ${opts.brand ? `${opts.brand}'s` : \"the brand's\"}.`,\n 'Compare every pair both ways before deciding. Begin `reasoning` with one line per criterion, exactly \"<criterion>: Image X > Image Y > …\" (best first), then one sentence on why the winner won.',\n \"\",\n \"BRIEF:\",\n brief,\n ].join(\"\\n\");\n}\n\n// --- The lint. ---\n\nexport type BriefIssue = { code: string; detail: string };\n\ntype LintOptions = { profile?: ImageModelProfile; copy?: readonly string[] };\n/** `p` is the brief with its numbered copy blanked (`\"\"`): what the brief itself says. `raw` is the brief as sent. */\ntype Check = (p: string, opts: LintOptions, raw: string) => BriefIssue | null;\n\nconst NUMBERED_COPY = /^(\\d+\\. )\"(.*)\"( — )/gm;\n\n/** The brief with every numbered copy line's words blanked, so a claim, a figure or quotes in the copy are not read as the brief's own text. */\nconst withoutCopy = (prompt: string): string => prompt.replace(NUMBERED_COPY, '$1\"\"$3');\n\n/** The words a brief spends on its own text, its quoted copy aside: what `over_word_budget` counts. */\nexport const briefWords = (prompt: string): number => wordCount(withoutCopy(prompt));\n\n/** How often `text` occurs in `p` as a whole phrase. */\nconst occurrences = (p: string, text: string): number => (p.match(phrase(text)) ?? []).length;\n\nconst ROLE_LABEL = /^(PERSON|ANIMAL|PRODUCT|BADGE|PROP)_\\d+$/;\nconst issue = (code: string, detail: string): BriefIssue => ({ code, detail });\nconst when = (test: boolean, code: string, detail: string): BriefIssue | null => (test ? issue(code, detail) : null);\n\n/** The copy a prompt allow-lists: given, its numbered lines, or (an older prompt) every quoted phrase. */\nfunction copyOf(prompt: string, opts: LintOptions): readonly string[] {\n if (opts.copy) return opts.copy;\n const numbered = briefCopyLines(prompt);\n return numbered.length > 0 ? numbered : [...prompt.matchAll(/\"([^\"\\n]+ [^\"\\n]+)\"/g)].map((m) => m[1] ?? \"\");\n}\n\nconst CHECKS: readonly Check[] = [\n (p, o) => {\n const budget = o.profile?.brief.wordBudget ?? 400;\n const words = wordCount(p);\n return when(words > budget, \"over_word_budget\", `${words} words, over the ${budget}-word budget`);\n },\n (p) => when(/\\{\\{|\"[a-z_]+\":\\s/.test(p), \"json_in_prompt\", \"a JSON blob or template slot is sent to the model\"),\n (p) =>\n when(\n /match the hex|hex precisely|exact hex/i.test(p),\n \"hex_precision\",\n \"asks the model to match hex codes precisely\",\n ),\n (p) => {\n const bare = (p.match(/#[0-9a-f]{6}\\b/gi) ?? []).length - (p.match(/[a-z]+ \\(#[0-9a-f]{6}\\)/gi) ?? []).length;\n return when(bare > 0, \"untied_hex\", `${bare} hex code(s) not tied to a named colour and object`);\n },\n (p) => {\n const tops = new Set([...p.matchAll(/\\btop (\\d+)%/gi)].map((m) => m[1]));\n const bottoms = new Set([...p.matchAll(/\\bbottom (\\d+)%/gi)].map((m) => m[1]));\n return when(\n tops.size > 1 || bottoms.size > 1,\n \"conflicting_safe_area\",\n `safe areas disagree: top ${[...tops].join(\"/\")}%, bottom ${[...bottoms].join(\"/\")}%`,\n );\n },\n (p) =>\n when(\n /\\bsource (ad|composition)\\b|\\bORIGINAL\\b|faithful|reference ad's|competitor/i.test(p),\n \"source_ad\",\n \"the source ad is named or followed\",\n ),\n (p) => {\n const labels = [...new Set(p.match(/\\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\\b/g) ?? [])].filter((l) => !ROLE_LABEL.test(l));\n return when(\n labels.length > 0,\n \"advertiser_label\",\n `labels that are not role-based: ${labels.slice(0, 5).join(\", \")}`,\n );\n },\n (p) =>\n when(\n /every word[^.\\n]*typeface|typeface[^.\\n]*every word/i.test(p) && /handwrit|in_scene|in-scene|marker/i.test(p),\n \"one_face_for_every_word\",\n \"one typeface is asked for every word while some lines are lettered in the scene\",\n ),\n (p) =>\n when(\n /print-ready/i.test(p) && /phone photo|unedited|not an ad|smartphone|a post\\b/i.test(p),\n \"polish_vs_native\",\n \"asks for a print-ready ad and a native, unedited photo at once\",\n ),\n (p) =>\n when(\n /\\d\\s?mm\\b|f\\/\\s?\\d|\\bISO\\s?\\d|focal_length|aperture/i.test(p),\n \"pseudo_precise_numbers\",\n \"lens or exposure figures a model cannot follow\",\n ),\n (p) =>\n when(\n /\\b(8k|hyper-?realistic|masterpiece|ultra-detailed|award-winning)\\b/i.test(p),\n \"buzzwords\",\n \"quality buzzwords instead of capture cues\",\n ),\n (p, o, raw) => {\n // A numbered line's own words are blanked in `p`, so any occurrence left restates it; an\n // older prompt quotes its copy once and repeats it elsewhere (the blueprint JSON).\n const own = briefCopyLines(raw).length > 0 ? 0 : 1;\n const repeated = [...new Set(copyOf(raw, o))].filter((c) => longEnough(c) && occurrences(p, c) > own);\n return when(\n repeated.length > 0,\n \"copy_repeated\",\n `copy stated more than once: ${repeated.slice(0, 3).join(\" / \")}`,\n );\n },\n (p, o, raw) => {\n if (briefCopyLines(raw).length === 0 && !o.copy) return null;\n const allowed = new Set(copyOf(raw, o));\n const stray = [...p.matchAll(/\"([^\"\\n]+)\"/g)].map((m) => m[1] ?? \"\").filter((q) => q && !allowed.has(q));\n return when(\n stray.length > 0,\n \"unlisted_quoted_text\",\n `quoted text that is not copy: ${stray.slice(0, 3).join(\" / \")}`,\n );\n },\n (p) => {\n const heads = [...p.matchAll(/^(Avoid|Exclusions|Constraints):.*$/gm)];\n const last = heads.at(-1);\n const after = last ? p.slice((last.index ?? 0) + last[0].length).trim() : \"\";\n return when(\n heads.length > 1 || after !== \"\",\n \"constraints_not_last\",\n \"the constraints are not one closing section\",\n );\n },\n (p, o) => {\n if (o.profile?.brief.constraints !== \"positive_last\") return null;\n const negatives = p.replace(/\"[^\"\\n]*\"/g, \"\").match(/\\b(no|not|never|don't|do not|avoid|without)\\b/gi);\n return when(\n Boolean(negatives),\n \"negative_phrasing\",\n `negative phrasing for a model that wants it positive: ${(negatives ?? []).slice(0, 3).join(\", \")}`,\n );\n },\n (p) => {\n const images = [...p.matchAll(/^Image (\\d+)\\b/gm)].map((m) => m[1]);\n return when(new Set(images).size !== images.length, \"reference_roles\", \"a picture is given more than one role\");\n },\n (p) =>\n when(\n /draw no logo|no logo\\b/i.test(p) && /\\bLOGO\\b/.test(p),\n \"logo_conflict\",\n \"a logo is both wired and forbidden\",\n ),\n (p, _o, raw) => {\n // The user found every hand-lettered surface odd (§ \"Fix round\"): the compiler sets it as type.\n const lettered = [...raw.matchAll(/^\\d+\\. \".*\" — [^\\n]*?lettered on ([^\\n]+)$/gm)].map((m) => m[1] ?? \"\");\n const own = p.replace(/^\\d+\\. \"\" — [^\\n]*$/gm, \"\");\n const found = lettered.join(\"\\n\").match(HANDWRITING)?.[0] ?? own.match(HAND_LETTERED)?.[0];\n return when(Boolean(found), \"handwritten_text\", `asks for handwriting: ${found}`);\n },\n (p) => {\n const runs = p.replace(/\"[^\"\\n]*\"/g, '\"\"').match(/[.!?,;:](?:\\s*[.,;:])+/g) ?? [];\n const doubled = runs.filter((r) => !/^\\.{3,}$/.test(r));\n return when(doubled.length > 0, \"doubled_punctuation\", `punctuation doubled: ${doubled.slice(0, 3).join(\" \")}`);\n },\n (p) => {\n const repeated = briefSentences(p).filter((s, i, all) =>\n all.some((other, j) => j !== i && (other === s ? j < i : ` ${other} `.includes(` ${s} `))),\n );\n return when(repeated.length > 0, \"repeated_sentence\", `said twice: ${repeated.slice(0, 2).join(\" / \")}`);\n },\n];\n\n/** Minimum words for a sentence to count as a restatement (shorter ones recur legitimately). */\nconst REPEAT_MIN_WORDS = 5;\n\n/**\n * The brief's own sentences, normalized, section labels off, for the repeat check. Picture\n * lines (\"Image 2 …\") repeat their role's instruction per picture by design, and quoted copy\n * is checked by `copy_repeated`, so both are left out.\n */\nfunction briefSentences(prompt: string): string[] {\n return prompt\n .split(\"\\n\")\n .filter((line) => !/^(Image \\d|\\d+\\. \")/.test(line))\n .map((line) => line.replace(/^[A-Z][a-z]+: /, \"\").replace(/\"[^\"\\n]*\"/g, \"\"))\n .flatMap((line) => line.match(/[^.!?]+[.!?]*/g) ?? [])\n .map((s) => normalized(s.replace(/^[A-Z][a-z ]+: /, \"\")))\n .filter((s) => wordCount(s) >= REPEAT_MIN_WORDS);\n}\n\n/**\n * The contradictions an image prompt can carry (prompt-audit.md §1a, §4), found by reading\n * it. A compiled brief has none; the bench gates on that (\"0 contradictions by lint\").\n */\nexport function lintBrief(prompt: string, opts: LintOptions = {}): BriefIssue[] {\n const own = withoutCopy(prompt);\n return CHECKS.flatMap((check) => {\n const found = check(own, opts, prompt);\n return found ? [found] : [];\n });\n}\n","/**\n * Every size of a static ad, from one picture.\n *\n * The ad is generated once, at the master shape. By default (`relayout`) the master is the\n * lead size, delivered as it is, and every other size is the chosen ad re-laid out for its own\n * frame by one image request (`static-adapt.ts`). The other shapes, kept for the bench, cut\n * each size from the master instead, keeping its content band (where every word, the logo and\n * the button sit) inside the target's safe area, and adapt a shape no cut reaches.\n *\n * Pure: formats and sizes in, geometry and plans out.\n */\nimport { nearestSupportedAspectRatio } from \"@baker/canvas-contract\";\nimport { type PixelRect, type SafeArea, safeRectPx, staticSafeArea } from \"./platform-safe-area.ts\";\n\nexport type Size = { width: number; height: number };\n\n/** A crop in master pixels, and whether it keeps the content band inside the target's safe area. */\nexport type BandCrop = { x: number; y: number; width: number; height: number; fits: boolean };\n\nexport function ratioValue(ratio: string): number {\n const [w, h] = ratio.split(\":\").map(Number);\n if (!w || !h || !Number.isFinite(w) || !Number.isFinite(h)) throw new Error(`not a W:H ratio: \"${ratio}\"`);\n return w / h;\n}\n\n/** One axis: where the crop starts so the band sits inside the target's margins, centred when it can. */\nfunction place(\n full: number,\n length: number,\n band: { start: number; end: number },\n margin: { start: number; end: number },\n): { at: number; fits: boolean } {\n // Float noise (0.94 × 1080 = 1015.1999…) must not decide a pixel either way.\n const lo = Math.max(0, Math.ceil(band.end - length * (1 - margin.end) - 1e-6));\n const hi = Math.min(full - length, Math.floor(band.start - length * margin.start + 1e-6));\n const centred = Math.round((band.start + band.end) / 2 - (length * (margin.start + 1 - margin.end)) / 2);\n if (lo > hi) return { at: Math.min(full - length, Math.max(0, centred)), fits: false };\n return { at: Math.min(hi, Math.max(lo, centred)), fits: true };\n}\n\n/**\n * The largest `target`-shaped crop of the master that keeps the master's content `band`\n * (fractions lost at each edge of the master) inside `targetSafe` (the target's safe area,\n * fractions of the crop), centred on the band. `fits` is false when no such crop exists:\n * the size must then be adapted, not cut.\n */\nexport function bandCropGeometry(\n master: Size,\n target: string,\n band: SafeArea,\n targetSafe: SafeArea = staticSafeArea(target),\n): BandCrop {\n const r = ratioValue(target);\n let width = master.width;\n let height = Math.round(master.width / r);\n if (height > master.height) {\n height = master.height;\n width = Math.round(master.height * r);\n }\n const y = place(\n master.height,\n height,\n { start: band.top * master.height, end: (1 - band.bottom) * master.height },\n { start: targetSafe.top, end: targetSafe.bottom },\n );\n const x = place(\n master.width,\n width,\n { start: band.left * master.width, end: (1 - band.right) * master.width },\n { start: targetSafe.left, end: targetSafe.right },\n );\n return { x: x.at, y: y.at, width, height, fits: x.fits && y.fits };\n}\n\n/**\n * How the master is shaped, one decision for every static ad (`relayout`; the cut shapes are\n * the bench's baselines):\n * - `relayout`: the ad is rendered once at the format the ad set leans on most (4:5 for feed,\n * else the square, else the first portrait size), delivered as it is, and every other size is\n * the chosen ad re-laid out for its own frame by one image request (`static-adapt.ts`): the\n * same words, people, product and logo, recomposed and re-scaled, never cut or pasted.\n * - `9x16_band`: the master is the tallest portrait size (9:16 on Meta) with everything in\n * its Reels band, and the shorter sizes are cuts of it. Zero extra generations.\n * - `4x5_adapt`: the master is the 4:5 feed ad, the square is a cut of it, and the 9:16 is\n * adapted from it by the reframe model (one billed adapt call).\n */\nexport const MASTER_SHAPES = [\"relayout\", \"9x16_band\", \"4x5_adapt\"] as const;\nexport type MasterShape = (typeof MASTER_SHAPES)[number];\n/**\n * Bench E5 (.context/static-review/BENCH.md § \"E5 — true per-format adaptation\"): the user\n * rated every geometric way of making the sizes (cuts, re-frames, the square master on a\n * painted or coloured ground) and rejected them as \"an image on top of another thing\"; the\n * square master with a painted scene was acceptable in 2 of 19. Each size re-laid out from the\n * chosen ad by GPT Image 2.5, its frame stated as zones, is one native ad per size.\n */\nexport const DEFAULT_MASTER_SHAPE: MasterShape = \"relayout\";\n\n/** Meta's delivery pixels per format; any other shape is 1080 on its short side. */\nconst META_PIXELS: Readonly<Record<string, Size>> = {\n \"9:16\": { width: 1080, height: 1920 },\n \"4:5\": { width: 1080, height: 1350 },\n \"1:1\": { width: 1080, height: 1080 },\n \"16:9\": { width: 1920, height: 1080 },\n \"1.91:1\": { width: 1200, height: 628 },\n};\n\nexport function deliveredSize(format: string): Size {\n const known = META_PIXELS[format];\n if (known) return known;\n const r = ratioValue(format);\n return r <= 1 ? { width: 1080, height: Math.round(1080 / r) } : { width: Math.round(1080 * r), height: 1080 };\n}\n\n/**\n * The shapes `image_aspect_adapt` renders: ad-format ratios only (the extreme banner ratios are\n * excluded, and the registry cannot gate array elements per model). Anything else is adapted\n * to its nearest and cut.\n */\nexport const ASPECT_ADAPT_FORMATS = [\"1:1\", \"2:3\", \"3:2\", \"3:4\", \"4:3\", \"4:5\", \"5:4\", \"9:16\", \"16:9\", \"21:9\"] as const;\n\nfunction nearestAdaptFormat(format: string): string {\n if ((ASPECT_ADAPT_FORMATS as readonly string[]).includes(format)) return format;\n const wanted = ratioValue(format);\n return [...ASPECT_ADAPT_FORMATS].sort(\n (a, b) => Math.abs(Math.log(ratioValue(a) / wanted)) - Math.abs(Math.log(ratioValue(b) / wanted)),\n )[0] as string;\n}\n\nexport type SizePlan = {\n format: string;\n /** The delivered pixels. */\n size: Size;\n /**\n * `master`: the master itself. `relayout`: the chosen ad re-laid out for this frame (one\n * billed request). `crop`: cut from the master (free). `adapt`: reframed from the master\n * (one billed call).\n */\n from: \"master\" | \"relayout\" | \"crop\" | \"adapt\";\n /** For a cut, the rectangle in the master's pixels. */\n crop?: BandCrop;\n /** For an adapted size, the shape the reframe node renders before it is cut to `size`. */\n adaptFormat?: string;\n};\n\nexport type StaticFormatPlan = {\n masterShape: MasterShape;\n /** The ad set's placements the plan keeps clear of (`staticSafeArea`); absent = Advantage+ (Reels included). */\n placements?: readonly string[];\n master: { format: string; renderRatio: string; size: Size };\n /** Where every word, the logo and the button sit on the master, as fractions lost at each edge: inside every cut's safe area. */\n band: SafeArea;\n /** Every delivered size, in the order delivered. */\n sizes: SizePlan[];\n};\n\nfunction masterFormat(formats: readonly string[], shape: MasterShape): string {\n const portrait = formats.filter((f) => ratioValue(f) <= 1);\n if (portrait.length === 0) return formats[0] as string;\n if (shape === \"4x5_adapt\" && portrait.includes(\"4:5\")) return \"4:5\";\n return [...portrait].sort((a, b) => ratioValue(a) - ratioValue(b))[0] as string;\n}\n\n/**\n * The master's content band: its own safe area, with the sides pulled in to the widest\n * margin any cut needs and the height shrunk (about its centre) to fit the shortest cut's\n * safe height, so every cut can hold it.\n */\nexport function masterContentBand(master: string, cuts: readonly string[], placements?: readonly string[]): SafeArea {\n const own = staticSafeArea(master, placements);\n const band: SafeArea = { top: own.top, bottom: own.bottom, left: own.left, right: own.right };\n const m = ratioValue(master);\n for (const cut of cuts) {\n const safe = staticSafeArea(cut, placements);\n band.left = Math.max(band.left, safe.left);\n band.right = Math.max(band.right, safe.right);\n // A cut wider than the master takes its full width and this share of its height.\n const share = m / ratioValue(cut);\n // Less a pixel's worth at each edge, so the cut's whole-pixel offset still holds it.\n const room = share * (1 - safe.top - safe.bottom) - 0.002;\n const height = 1 - band.top - band.bottom;\n if (height > room) {\n const shrink = (height - room) / 2;\n band.top += shrink;\n band.bottom += shrink;\n }\n }\n return band;\n}\n\n/**\n * How each delivered format is made from the one master: the master itself, the chosen ad\n * re-laid out for that frame (`relayout`, the default), or, on the cut shapes, a cut that keeps\n * the content band inside the size's safe area or an adapt for a shape no cut reaches\n * (landscape from a portrait master; the 9:16 of a `4x5_adapt` master). `placements` are the\n * ad set's (see `staticSafeArea`).\n */\nexport function planStaticFormats(\n genModel: string,\n formats: readonly string[],\n opts: { masterShape?: MasterShape; placements?: readonly string[] } = {},\n): StaticFormatPlan {\n if (formats.length === 0) throw new Error(\"a static ad needs at least one format\");\n const masterShape = opts.masterShape ?? DEFAULT_MASTER_SHAPE;\n if (masterShape === \"relayout\") return planRelayout(genModel, formats, opts.placements);\n const format = masterFormat(formats, masterShape);\n const size = deliveredSize(format);\n const m = ratioValue(format);\n const cuts = formats.filter((f) => f !== format && ratioValue(f) >= m && ratioValue(f) <= Math.max(1, m));\n const band = masterContentBand(format, cuts, opts.placements);\n const sizes = formats.map((f): SizePlan => {\n if (f === format) return { format: f, size, from: \"master\" };\n if (cuts.includes(f)) {\n const crop = bandCropGeometry(size, f, band, staticSafeArea(f, opts.placements));\n if (crop.fits) return { format: f, size: deliveredSize(f), from: \"crop\", crop };\n }\n return { format: f, size: deliveredSize(f), from: \"adapt\", adaptFormat: nearestAdaptFormat(f) };\n });\n return {\n masterShape,\n ...(opts.placements && opts.placements.length > 0 ? { placements: [...opts.placements] } : {}),\n master: { format, renderRatio: nearestSupportedAspectRatio(\"image_generate\", genModel, format), size },\n band,\n sizes,\n };\n}\n\n/** The format the ad set leans on most: the 4:5 feed, else the square, else the first portrait size, else the first. */\nfunction leadFormat(formats: readonly string[]): string {\n for (const lead of [\"4:5\", \"1:1\"]) if (formats.includes(lead)) return lead;\n return formats.find((f) => ratioValue(f) <= 1) ?? (formats[0] as string);\n}\n\n/**\n * `relayout`: the master is the lead format, delivered as it is, its own safe area its band;\n * every other size is re-laid out from it. Nothing is cut.\n */\nfunction planRelayout(\n genModel: string,\n formats: readonly string[],\n placements: readonly string[] | undefined,\n): StaticFormatPlan {\n const format = leadFormat(formats);\n const size = deliveredSize(format);\n const { top, bottom, left, right } = staticSafeArea(format, placements);\n return {\n masterShape: \"relayout\",\n ...(placements && placements.length > 0 ? { placements: [...placements] } : {}),\n master: { format, renderRatio: nearestSupportedAspectRatio(\"image_generate\", genModel, format), size },\n band: { top, bottom, left, right },\n sizes: formats.map(\n (f): SizePlan =>\n f === format ? { format: f, size, from: \"master\" } : { format: f, size: deliveredSize(f), from: \"relayout\" },\n ),\n };\n}\n\n/** The logo's box as a share of the frame width, and its assumed shape (a wordmark) before the file is read. */\nconst LOGO_WIDTH_SHARE = 0.15;\nconst LOGO_ASPECT = 3;\n/** A slot may overlap copy by at most this share of its own area (bench E6's pass line). */\nconst MAX_TEXT_OVERLAP = 0.02;\n\nexport type LogoSlot = {\n rect: PixelRect;\n /** Which version of the logo reads on the ground under it: `dark` on a light ground, `light` on a dark one. */\n variant: \"light\" | \"dark\";\n};\n\nfunction overlapArea(a: PixelRect, b: PixelRect): number {\n const w = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x);\n const h = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y);\n return w > 0 && h > 0 ? w * h : 0;\n}\n\n/**\n * Where a composited logo goes on one size: the first corner (then edge centre) of the\n * size's safe area that the copy leaves clear, read off the OCR boxes of the finished ad;\n * the logo version from the ground's luminance there (0 black, 1 white). With no boxes (the\n * scaffold, before anything rendered) it is the top-left corner of the safe area.\n * Composited logos are off by default until bench E6 passes (`LOGO_COMPOSITE_DEFAULT`).\n */\nexport function logoSlot(\n frame: Size,\n safe: SafeArea,\n opts: { textBoxes?: readonly PixelRect[]; luminance?: (rect: PixelRect) => number; aspect?: number } = {},\n): LogoSlot {\n const area = safeRectPx(safe, frame.width, frame.height);\n const width = Math.round(frame.width * LOGO_WIDTH_SHARE);\n const height = Math.round(width / (opts.aspect ?? LOGO_ASPECT));\n const left = area.x;\n const right = area.x + area.width - width;\n const centre = Math.round(area.x + (area.width - width) / 2);\n const top = area.y;\n const bottom = area.y + area.height - height;\n const candidates: PixelRect[] = [\n [left, top],\n [right, top],\n [left, bottom],\n [right, bottom],\n [centre, top],\n [centre, bottom],\n ].map(([x, y]) => ({ x: x as number, y: y as number, width, height }));\n const boxes = opts.textBoxes ?? [];\n const clear = (r: PixelRect) =>\n boxes.reduce((sum, b) => sum + overlapArea(r, b), 0) <= MAX_TEXT_OVERLAP * r.width * r.height;\n const rect = candidates.find(clear) ?? (candidates[0] as PixelRect);\n const ground = opts.luminance?.(rect) ?? 1;\n return { rect, variant: ground >= 0.5 ? \"dark\" : \"light\" };\n}\n","/**\n * One size of a static ad, re-laid out for its frame from the chosen ad.\n *\n * The chosen ad (the master) is the reference of one image request per other size: the same\n * message, words, people, product and logo, recomposed for the frame, with the text and the\n * button re-scaled for it, inside the frame's safe area. Never a crop, a shrink onto a\n * background or a pasted panel: the user rated every geometric way of making the sizes\n * (crops, re-frames, the square on a painted or coloured ground) and rejected them as \"an\n * image on top of another thing\" (.context/static-review/BENCH.md § \"E5 — true per-format\n * adaptation\").\n *\n * Pure: formats in, the request's plan, its brief and the argv of the fill to the delivered\n * pixels out.\n */\nimport { nearestSupportedAspectRatio } from \"@baker/canvas-contract\";\nimport type { ImageModelProfile } from \"../lib/prompt-profiles.ts\";\nimport { blockingArea, type SafeArea, staticSafeArea } from \"./platform-safe-area.ts\";\nimport type { AdSpec } from \"./static-ad-spec.ts\";\nimport {\n briefWords,\n type CompiledBrief,\n compileStaticBrief,\n isBeforeAfter,\n PEOPLE,\n type PlannedBand,\n quotedCopy,\n} from \"./static-brief.ts\";\nimport { deliveredSize, ratioValue, type Size } from \"./static-sizes.ts\";\n\n/** One size re-laid out from the chosen ad by one image request (`relayout`): its brief is projected from prompt.json. */\nexport const STATIC_ADAPT_FORMAT_ROLE = \"static_adapt_format\";\n\nexport type AdaptPlan = {\n /** The delivered format. */\n format: string;\n /** The ad set's placements the size keeps clear of (`staticSafeArea`); absent = Advantage+. */\n placements?: readonly string[];\n /** The ratio the model renders: the format, or the model's nearest when it has no such ratio. */\n renderRatio: string;\n /** The delivered pixels. */\n size: Size;\n /** The format's safe area on the rendered frame, so it holds once the render is cut to the format. */\n safe: SafeArea;\n};\n\n/**\n * How one size is rendered: at the model's nearest ratio to the format, then filled (cut at\n * the centre) to the delivered pixels. The safe area is stated on the rendered frame, so a\n * 3:4 GPT render cut to 4:5 still keeps the feed margin after the cut.\n */\nexport function adaptPlan(model: string, format: string, placements?: readonly string[]): AdaptPlan {\n const renderRatio = nearestSupportedAspectRatio(\"image_generate\", model, format);\n return {\n format,\n ...(placements && placements.length > 0 ? { placements: [...placements] } : {}),\n renderRatio,\n size: deliveredSize(format),\n safe: safeAreaOnFrame(renderRatio, format, placements),\n };\n}\n\n/**\n * `format`'s safe area stated on a frame of ratio `frame` that is filled (cut at the centre) to\n * it: the strip the fill trims, then the format's own margin on what it keeps.\n */\nexport function safeAreaOnFrame(frame: string, format: string, placements?: readonly string[]): SafeArea {\n const own = blockingArea(format, placements);\n const rendered = ratioValue(frame);\n const delivered = ratioValue(format);\n // The share of the rendered frame's height (or width) the fill keeps, centred.\n const keepH = rendered < delivered ? rendered / delivered : 1;\n const keepW = rendered > delivered ? delivered / rendered : 1;\n return {\n top: (1 - keepH) / 2 + own.top * keepH,\n bottom: (1 - keepH) / 2 + own.bottom * keepH,\n left: (1 - keepW) / 2 + own.left * keepW,\n right: (1 - keepW) / 2 + own.right * keepW,\n };\n}\n\n/**\n * The margin a render keeps when the fill to its format trims it (a 3:4 GPT render filled to\n * 4:5): the format's safe area mapped onto the rendered frame, stated as edges. Undefined when\n * the model renders the format itself and its own safe area holds.\n */\nexport function trimmedRenderBand(plan: AdaptPlan): PlannedBand | undefined {\n const own = blockingArea(plan.format, plan.placements);\n const trimmed = ([\"top\", \"bottom\", \"left\", \"right\"] as const).some(\n (edge) => Math.abs(own[edge] - plan.safe[edge]) > 1e-9,\n );\n return trimmed ? { area: plan.safe, cut: false, margin: true } : undefined;\n}\n\n/** Fill `size` exactly (Lanczos, centred): what the master and every re-laid-out size go through. */\nexport function fillArgs(size: Size): string[] {\n const px = `${size.width}x${size.height}`;\n return [\n \"{{in.image}}\",\n \"-filter\",\n \"Lanczos\",\n \"-resize\",\n `${px}^`,\n \"-gravity\",\n \"center\",\n \"-extent\",\n px,\n \"+repage\",\n \"{{out.image}}\",\n ];\n}\n\n/** The pictures one adapt request is handed, as the refs the canvas passes them by. */\nexport type AdaptImages = {\n /** The chosen ad: what is re-laid out. */\n base: string;\n logo?: string;\n /** The specimen of the brand's typeface. */\n type?: string;\n /** The spec's own pictures (PRODUCT_1, PERSON_1 …) by reference id, so every size keeps them exactly. */\n refs?: Readonly<Record<string, string>>;\n};\n\n/** What the size is, in the words of the deliverable. */\nfunction adaptNoun(plan: AdaptPlan): string {\n const r = ratioValue(plan.format);\n if (plan.format === \"9:16\") {\n const safe = staticSafeArea(plan.format, plan.placements);\n return safe.placement === \"stories\"\n ? \"full-screen vertical ad for Stories\"\n : safe.placement === \"vertical\"\n ? `full-screen vertical ad for ${safe.surface}`\n : \"full-screen vertical ad for Stories and Reels\";\n }\n if (r === 1) return \"square feed ad\";\n return r < 1 ? \"vertical feed ad\" : \"landscape ad\";\n}\n\n/** The deliverable: this ad, re-laid out for the frame; never the ad shrunk onto a background. */\nfunction adaptDeliverable(plan: AdaptPlan, spec: AdSpec, flowing: boolean, ground: boolean, short: boolean): string {\n const brand = spec.brand?.name;\n const noun = `${adaptNoun(plan)}${brand ? ` for ${brand}` : \"\"}`;\n if (short && !flowing) {\n const keep = [\n ...(showsPeople(spec) ? [\"each person the same face and clothes\"] : []),\n ...(isBeforeAfter(spec) ? [\"the two states as different\"] : []),\n ];\n return `Deliverable: the ad in Image 1, re-laid out as a finished ${noun}: the same message, words, people, product, logo, colours and typeface, the scene filling the frame edge to edge, never Image 1 shrunk onto a background. Every person, product and line appears whole and once, as in Image 1${keep.length > 0 ? `, ${keep.join(\", \")}` : \"\"}; add nothing new.`;\n }\n const cast = [\n SAME_CAST,\n ...(showsPeople(spec) ? [SAME_PERSON] : []),\n ...(isBeforeAfter(spec) ? [SAME_CHANGE] : []),\n ...(ground ? [GROUND_ONLY] : []),\n ].join(\" \");\n return flowing\n ? `Image 1 is a finished ad. Re-lay out this exact ad as a ${noun}: the same message, words, people, product, logo, colours and typeface in a new layout for this frame, the scene filling it edge to edge rather than Image 1 set on a background as a card, panel or square. ${cast}`\n : `Deliverable: the ad in Image 1, re-laid out as a finished ${noun}: the same message, words, people, product, logo, colours and typeface in a new layout for this frame, the scene filling it edge to edge, never Image 1 shrunk onto a background or set as a card, panel or square. ${cast}`;\n}\n\n/**\n * Every size shows the same cast. Bench E5: the room a new frame adds was filled with a new\n * scene, often a new person, and the 4:5 and the 9:16 of one ad could show different people.\n */\nconst SAME_CAST =\n \"Every person, product and line appears whole and once, the same individuals as in Image 1; add no person, animal, product or object that Image 1 does not show.\";\n/** The headphones ad's man looked different on its 9:16 (BENCH.md § \"Final\"). */\nconst SAME_PERSON = \"Each person is the same person as in Image 1: same face, same hair, same clothes.\";\n/** The serum's two faces barely differed in its re-laid sizes (BENCH.md § \"Final\"). */\nconst SAME_CHANGE = \"The two states it compares stay as clearly different as in Image 1.\";\n\nconst showsPeople = (spec: AdSpec): boolean =>\n spec.references.some((r) => r.id.startsWith(\"PERSON_\")) ||\n (spec.scene.look !== \"illustration\" && PEOPLE.test(`${spec.scene.description} ${spec.scene.layout ?? \"\"}`));\n\n/** What the room a new frame adds carries when Image 1 is its words on a ground (the zones say it when they are stated). */\nconst GROUND_ONLY =\n \"Image 1 shows no people and no product, so the room this frame adds carries more of Image 1's own background and the brand's colours, never a new scene.\";\n\n/** The chosen ad is its words on the brand's ground: no picture of anyone or anything is wired, and its scene names no one. */\nfunction wordsOnly(spec: AdSpec): boolean {\n return (\n spec.references.length === 0 &&\n spec.scene.look !== \"photo\" &&\n !PEOPLE.test(`${spec.scene.description} ${spec.scene.layout ?? \"\"}`)\n );\n}\n\n/** A frame much taller than it is wide (9:16): the one whose words a middle band holds. */\nconst isTall = (plan: AdaptPlan): boolean => ratioValue(plan.format) < 0.7;\n\n/** Copy laid out as a list or a table: rows a tall frame restacks rather than shrinks. */\nconst LIST_ROLES = /^(list_item|bullet|row|item)$/i;\nconst TABLE = /\\b(tables?|columns?|rows?|checklists?|lists?)\\b/i;\nconst hasRows = (spec: AdSpec): boolean =>\n spec.copy.some((l) => LIST_ROLES.test(l.role ?? \"\")) ||\n TABLE.test(`${spec.scene.description} ${spec.scene.layout ?? \"\"}`);\n\n/**\n * The words set for the frame, not at Image 1's size. On a tall frame they fill the band: the\n * user found the final 9:16s' words small in their middle band, with \"too much empty space\"\n * around them (BENCH.md § \"Fix round\"); a list or a table is restacked rather than shrunk.\n */\nfunction scaleText(plan: AdaptPlan, spec: AdSpec, flowing: boolean, zoned: boolean, short: boolean): string {\n if (short && !flowing) {\n return isTall(plan) && zoned\n ? \"Scale: larger than in Image 1, the headline across most of the band's width, the words spread through the band.\"\n : \"Scale: larger than in Image 1, the largest line across most of the width.\";\n }\n if (!(isTall(plan) && zoned)) {\n return flowing\n ? \"Every line is set at the size this frame needs rather than at its size in Image 1: the largest line spans most of the width between the margins, and the words fill their space.\"\n : \"Scale: set every line for this frame, not at its size in Image 1: the largest line spans most of the width between the margins; no small text in a large empty area.\";\n }\n const rows = hasRows(spec)\n ? flowing\n ? \" Rows, columns and lists are restacked for the tall frame, one item per line, each larger.\"\n : \" Rows, columns and lists are restacked for the tall frame, one item per line, each larger, rather than shrunk.\"\n : \"\";\n return flowing\n ? `The words are set for this frame, larger than in Image 1: the headline large enough to fill most of the band's width, and every line, the logo and the button spread through the band's height, inside its edges.${rows}`\n : `Scale: larger than in Image 1: the headline set large enough to fill most of the band's width; every line, the logo and the button spread through the band's height, inside its edges, never a small block in its middle.${rows}`;\n}\n\n/**\n * Room the zones ask inside the safe area of a tall frame the app covers. Asked for exactly the\n * checked band (16-78%), the model set the button at the bottom as in a feed ad: 0 of 6\n * re-layouts kept inside it and every ad paid a repair round (dev, Oct 2026). Asked for 22-70%,\n * 5 of 9 did, and the rest overshot by less. Any other frame is asked for its own band.\n */\nconst COVERED_HEADROOM = { top: 0.06, bottom: 0.08, side: 0.02 };\nconst NO_HEADROOM = { top: 0, bottom: 0, side: 0 };\n\nconst pct = (share: number): string => `${Math.round(share * 100)}%`;\n\n/**\n * The frame as zones: the words' band (every word, the logo and the button), and around it the\n * ad's own picture. On a tall frame the app covers the parts above and below the band, so they\n * show the ad's picture larger, never flat colour, a panel or the ad as a square on a ground: the\n * user rejected every one (BENCH.md § \"E5\", § \"Fix round\"). The covered bands are named as\n * carrying neither words nor the logo: without it, the fix round's first 9:16s set the logo above\n * the band and none passed the Stories area. One statement of the frame: the Layout states no\n * safe area of its own (`zoned`).\n */\nfunction zonesText(plan: AdaptPlan, flowing: boolean, ground: boolean, short: boolean): string {\n const covered = staticSafeArea(plan.format, plan.placements).placement !== \"feed\";\n const room = isTall(plan) && covered ? COVERED_HEADROOM : NO_HEADROOM;\n const top = plan.safe.top + room.top;\n const bottom = plan.safe.bottom + room.bottom;\n const side = Math.max(plan.safe.left, plan.safe.right) + room.side;\n const band = `from ${pct(top)} to ${pct(1 - bottom)} of the height, at least ${pct(side)} in from each side`;\n if (short && !flowing) {\n const around =\n isTall(plan) && covered\n ? \"only the ad's own picture, larger, never a flat colour, a panel or a square\"\n : \"only the ad's own picture\";\n return `Zones: every word, the logo and the button sit ${band}; around them, ${around}.`;\n }\n if (!(isTall(plan) && covered)) {\n return flowing\n ? `The logo, every line and the button sit ${band}; around them the frame shows only the ad's own picture.`\n : `Zones: the logo, every line and the button sit ${band}; around them, only the ad's own picture.`;\n }\n const picture = ground\n ? \"Image 1's background with its shapes and graphics enlarged in the brand's colours\"\n : \"Image 1's people, product and setting extended\";\n return flowing\n ? `The words' band runs ${band}, holding the logo, every line and the button. Above and below it the app's interface covers the frame, so there it shows only the ad's own picture, larger: ${picture}, edge to edge.`\n : `Zones: the app's buttons and captions cover the top and the bottom of this frame, hiding any word there. The logo, every line and the button sit in one band, ${band}: the headline starts below the top fifth, and the button ends directly under the last line, never at the bottom edge where a feed ad puts it. Above and below it, where the app's interface covers the frame, only the ad's own picture, larger: ${picture}, never a flat empty colour, a panel or a square on a background.`;\n}\n\n/**\n * The brief of one adapt request: the compiled re-lay brief for the format (the exact copy,\n * its hierarchy, the logo, the typeface and the safe area, from `compileStaticBrief`) with the\n * square's own per-line positions dropped (they place the words for Image 1's shape), led by\n * the re-layout deliverable, with the words re-scaled for the frame and the frame stated as\n * zones (`zones: false` leaves them out: the bench's arm without them).\n */\nexport function adaptBrief(\n spec: AdSpec,\n profile: ImageModelProfile,\n opts: { plan: AdaptPlan; images: AdaptImages; zones?: boolean },\n): CompiledBrief {\n const { plan, images } = opts;\n const flowing = profile.brief.structure === \"flowing_sentences\";\n const { logo_position: _logoPosition, ...rest } = spec;\n const unplaced: AdSpec = { ...rest, copy: spec.copy.map(({ position: _position, ...line }) => line) };\n const band = trimmedRenderBand(plan);\n const zoned = opts.zones !== false;\n const ground = wordsOnly(spec);\n const compile = (wordBudget: number) =>\n compileStaticBrief(\n unplaced,\n undefined,\n plan.format,\n undefined,\n { ...profile, brief: { ...profile.brief, wordBudget } },\n {\n job: \"relay\",\n images: {\n base: images.base,\n ...(images.logo ? { logo: images.logo } : {}),\n ...(images.type ? { type: images.type } : {}),\n refs: images.refs ?? {},\n },\n ...(plan.placements ? { placements: plan.placements } : {}),\n ...(band ? { band } : {}),\n ...(zoned ? { zoned } : {}),\n },\n );\n const relaid = (compiled: CompiledBrief, short = false): string => {\n const paragraphs = compiled.prompt.split(\"\\n\\n\");\n paragraphs[0] = adaptDeliverable(plan, spec, flowing, ground && !zoned, short);\n const first = spec.copy[0] ? quotedCopy(spec.copy[0].text) : undefined;\n const textAt = first ? paragraphs.findIndex((p) => p.includes(first)) : -1;\n if (textAt >= 0) {\n const added = [\n ...(zoned ? [zonesText(plan, flowing, ground, short)] : []),\n scaleText(plan, spec, flowing, zoned, short),\n ];\n paragraphs.splice(textAt + 1, 0, ...added);\n } else if (zoned) {\n // No line to anchor on (an ad of only a logo or a picture): the zones are still its only safe area.\n const layoutAt = paragraphs.findIndex((p) => p.startsWith(\"Layout:\"));\n paragraphs.splice(layoutAt > 0 ? layoutAt : 1, 0, zonesText(plan, flowing, ground, short));\n }\n return paragraphs.join(\"\\n\\n\");\n };\n // The re-layout's own sentences count against the profile's budget: the relay brief under\n // them is trimmed (never its copy) until the whole fits, and then, a crowded ad, they take\n // their short forms.\n const budget = profile.brief.wordBudget;\n let compiled = compile(budget);\n let prompt = relaid(compiled);\n for (let pass = 0; pass < 3 && briefWords(prompt) > budget; pass++) {\n compiled = compile(compiled.words - (briefWords(prompt) - budget));\n prompt = relaid(compiled);\n }\n if (briefWords(prompt) > budget) prompt = relaid(compiled, true);\n return { ...compiled, prompt, words: wordsIn(prompt) };\n}\n\nconst wordsIn = (text: string): number => text.split(/\\s+/).filter(Boolean).length;\n","/**\n * The render graph of a static ad, and the roles every reader finds its nodes by.\n *\n * gen_1..3 (image_generate, one call per variant, each on its own direction) role static_variant_call\n * → gen (collect: the variants as one list; a single variant is gen itself) role static_variants\n * → prefilter (static_prefilter: ranks the variants by the checks and passes the first as\n * `image`; it asks the judge only on a real tie) role variant_prefilter\n * → master (imagemagick: the chosen picture at the master's delivered pixels) role static_master\n * → relayout (the default): adapt_<size> (image_generate: the chosen ad re-laid out for that\n * frame) role static_adapt_format → size_<size> (imagemagick: filled to its pixels) role static_size\n * → the cut shapes (bench): a band-anchored cut of the master, or an adapt of it\n * (image_aspect_adapt, landscape) role static_size / static_adapt\n * → safe_<size> (static_safe_fit, every 9:16 Reels or Stories covers: the words and logo\n * brought inside the band by a small shrink and shift, free) role static_safe_fit\n * → optional logo composite per size role logo_composite\n * → final (collect, one label per format)\n *\n * Readers (the run review, `--fix`, the render budget, the lint, `--regenerate`, the\n * dashboard) go by `tags.role`, never by node id: tags never enter the cache key, and ids\n * change with the topology. `scaffoldStaticAd` and `upgradeStaticCanvas` both build the\n * graph here, so a new creative and an upgraded one are the same graph.\n *\n * Pure.\n */\nimport type { NodeDecl } from \"../schema/canvas.ts\";\nimport { imageRenderParams } from \"./lib/image-params.ts\";\nimport { type SafeArea, staticSafeArea } from \"./platform-safe-area.ts\";\nimport { adaptPlan, fillArgs, STATIC_ADAPT_FORMAT_ROLE, safeAreaOnFrame } from \"./static-adapt.ts\";\nimport {\n ASPECT_ADAPT_FORMATS,\n deliveredSize,\n logoSlot,\n type Size,\n type SizePlan,\n type StaticFormatPlan,\n} from \"./static-sizes.ts\";\n\n/** The node whose `images` are the variants of a version (the dashboard's options). */\nexport const STATIC_VARIANTS_ROLE = \"static_variants\";\n/** One call of a variants node fanned out, for a model that returns one picture per call (`tags.region` = variant). */\nexport const STATIC_VARIANT_CALL_ROLE = \"static_variant_call\";\n/** Drops variants that fail the deterministic checks before the judge sees them. */\nexport const VARIANT_PREFILTER_ROLE = \"variant_prefilter\";\n/**\n * The judge node of an older graph; its image output was the variant the sizes were made from.\n * The current graph has none: the prefilter passes the chosen picture as `image` and asks the\n * judge only on a tie. Still read on older run records, and carried by a person's own choice.\n */\nexport const STATIC_PICK_ROLE = \"pick\";\n/** The chosen picture at the master's delivered pixels: what `--fix` edits and every size is made from. */\nexport const STATIC_MASTER_ROLE = \"static_master\";\n/** One delivered size cut (or resized) from the master or its adapt. */\nexport const STATIC_SIZE_ROLE = \"static_size\";\nexport { STATIC_ADAPT_FORMAT_ROLE };\n/** The reframe of the master into the shapes a cut cannot reach. */\nexport const STATIC_ADAPT_ROLE = \"static_adapt\";\n/** A 9:16 shrunk and shifted (free) so its words and logo sit inside the band Reels or Stories leave clear. */\nexport const STATIC_SAFE_FIT_ROLE = \"static_safe_fit\";\n/** An autofix round's placing of one size inside its band, for its margins to be painted (`static_place`). */\nexport const STATIC_PLACE_ROLE = \"static_place\";\n/** The brand's logo file composited onto one size. */\nexport const LOGO_COMPOSITE_ROLE = \"logo_composite\";\n/** The correction render `--fix` puts in place of the variants. */\nexport const STATIC_FIX_ROLE = \"static_fix\";\n\n/**\n * Composite the logo file instead of having the model draw it: off. Bench E6\n * (.context/static-review/BENCH.md § E6, n=16): a composite never overlapped copy (100% of\n * sizes within 2%), but the judge found it placed naturally in 8% of sizes against 61% for the\n * drawn logo: too small, low contrast, over faces. On again only when a slot fixes those.\n */\nexport const LOGO_COMPOSITE_DEFAULT = false;\n\n/** The reframe tier: 2K is the same price as 1K on Nano Banana Pro. */\nconst ADAPT_IMAGE_SIZE = \"2K\";\n\ntype Node = {\n id?: unknown;\n type?: unknown;\n params?: Record<string, unknown>;\n inputs?: Record<string, unknown>;\n tags?: { role?: unknown; region?: unknown };\n};\n\nfunction nodesOf(canvas: unknown): Node[] {\n const nodes = (canvas as { nodes?: unknown } | null)?.nodes;\n return Array.isArray(nodes) ? (nodes as Node[]) : [];\n}\n\n/** The nodes of a canvas carrying `role`, in canvas order. */\nexport function nodesWithRole(canvas: unknown, role: string): Array<{ id: string; type: string } & Node> {\n return nodesOf(canvas).filter(\n (n): n is { id: string; type: string } & Node =>\n n?.tags?.role === role && typeof n.id === \"string\" && typeof n.type === \"string\",\n );\n}\n\n/** Each node id of a canvas with its role, for readers that only hold outputs keyed by id. */\nexport function rolesById(canvas: unknown): Record<string, string> {\n const out: Record<string, string> = {};\n for (const n of nodesOf(canvas)) {\n if (typeof n?.id === \"string\" && typeof n.tags?.role === \"string\") out[n.id] = n.tags.role;\n }\n return out;\n}\n\n/** A format as a node-id fragment: `4:5` → `4x5`, `1.91:1` → `1_91x1`. */\nexport function formatSlug(format: string): string {\n return format.replace(\":\", \"x\").replace(\".\", \"_\");\n}\n\nconst EXTENT = /^(\\d+)x(\\d+)$/;\n\n/** The master's delivered format, read off the master node's pixels; null when the canvas has none. */\nexport function staticMasterFormat(canvas: unknown): string | null {\n const master = nodesWithRole(canvas, STATIC_MASTER_ROLE)[0];\n const args = master?.params?.args;\n if (!Array.isArray(args)) return null;\n const at = args.indexOf(\"-extent\");\n const match = typeof args[at + 1] === \"string\" ? (args[at + 1] as string).match(EXTENT) : null;\n if (at < 0 || !match) return null;\n return formatOfSize({ width: Number(match[1]), height: Number(match[2]) });\n}\n\n/** Every format a canvas plans, so one whose ratio is not in lowest terms (21:9) reads back as itself. */\nconst KNOWN_FORMATS = [\"9:16\", \"4:5\", \"1:1\", \"16:9\", \"1.91:1\", ...ASPECT_ADAPT_FORMATS];\n\nfunction formatOfSize(size: Size): string {\n for (const format of KNOWN_FORMATS) {\n const d = deliveredSize(format);\n if (d.width === size.width && d.height === size.height) return format;\n }\n const gcd = (a: number, b: number): number => (b === 0 ? a : gcd(b, a % b));\n const g = gcd(size.width, size.height);\n return `${size.width / g}:${size.height / g}`;\n}\n\n/** A format's safe area as the reframe node takes it (fractions only). */\n/**\n * The safe area the reframe keeps on one of its formats: its own, widened by the strip the fill\n * to each size cut from it trims (a 1.91:1 filled from the 16:9 loses its top and bottom), so a\n * line at the edge still clears each delivered size's margin.\n */\nfunction safeAreaOf(adaptFormat: string, delivered: readonly string[]): SafeArea {\n const { top, bottom, left, right } = staticSafeArea(adaptFormat);\n return delivered\n .map((f) => safeAreaOnFrame(adaptFormat, f))\n .reduce(\n (a, b) => ({\n top: Math.max(a.top, b.top),\n bottom: Math.max(a.bottom, b.bottom),\n left: Math.max(a.left, b.left),\n right: Math.max(a.right, b.right),\n }),\n { top, bottom, left, right },\n );\n}\n\nfunction magick(id: string, role: string, image: string, args: string[], extra: Record<string, string> = {}): NodeDecl {\n return {\n id,\n type: \"imagemagick\",\n inputs: { image, ...extra },\n params: { args, outputs: { image: { kind: \"image\", ext: \"png\" } } },\n tags: { role },\n };\n}\n\nexport type StaticRenderGraph = {\n /** The generation model and its request params (quality, size), without prompt, shape or `n`. */\n genModel: string;\n genParams: Record<string, unknown>;\n /** Variants rendered: 1-3. */\n takes: number;\n /** The pictures each render is given (element slots, the logo when the model draws it, the type specimen). */\n reference: string[];\n /** The ad spec the prefilter checks the variants against (`$ref:prompt.asset`). */\n specRef: string;\n judgeModel: string;\n /** The reframe model: adapts a size a cut cannot reach (the cut shapes). */\n adaptModel: string;\n /** The model that re-lays out each other size from the chosen ad (`relayout`). */\n relayoutModel: string;\n /** The brand's logo file when the model draws it: each re-laid-out size gets it too. */\n logoRef?: string;\n plan: StaticFormatPlan;\n /** The logo file, composited on every size (never also given to the model). */\n compositeLogo?: string;\n /** The placeholder every compiled prompt starts as (the brief projection writes the real one). */\n prompt: string;\n};\n\n/**\n * The variants: one call per variant, gathered into one list; never one call with `n`, even\n * on a model that takes it. Bench E3 (.context/static-review/BENCH.md#E3): same price and wall\n * time, and 1.5x the distinct options, because each call keeps its own direction of the brief.\n */\nfunction variantNodes(g: StaticRenderGraph): NodeDecl[] {\n const render = (id: string, extra: Record<string, unknown>, tags: NodeDecl[\"tags\"]): NodeDecl => ({\n id,\n type: \"image_generate\",\n ...(g.reference.length > 0 ? { inputs: { reference: [...g.reference] } } : {}),\n params: { model: g.genModel, aspect_ratio: g.plan.master.renderRatio, ...g.genParams, ...extra, prompt: g.prompt },\n tags,\n });\n if (g.takes === 1) return [render(\"gen\", {}, { role: STATIC_VARIANTS_ROLE })];\n // The calls run in parallel and cost what one n-call would (n is billed per picture); each\n // keeps its own cache key, so a failed variant re-runs alone.\n const calls = Array.from({ length: g.takes }, (_, i) =>\n render(`gen_${i + 1}`, {}, { role: STATIC_VARIANT_CALL_ROLE, region: i }),\n );\n return [\n ...calls,\n {\n id: \"gen\",\n type: \"collect\",\n inputs: { images: calls.map((c) => `$ref:${c.id}.images#0`) },\n tags: { role: STATIC_VARIANTS_ROLE },\n },\n ];\n}\n\n/**\n * Everything from the variants to the delivered sizes: the static ad's render graph, ending\n * in the `final` collect labelled by format (in `plan.sizes` order).\n */\nexport function staticRenderNodes(g: StaticRenderGraph): NodeDecl[] {\n const { plan } = g;\n const nodes: NodeDecl[] = [\n ...variantNodes(g),\n {\n id: \"prefilter\",\n type: \"static_prefilter\",\n inputs: { images: \"$ref:gen.images\", spec: g.specRef },\n params: {\n formats: plan.sizes.map((s) => s.format),\n master_shape: plan.masterShape,\n ...(plan.placements ? { placements: [...plan.placements] } : {}),\n // The judge breaks a real tie only; one variant never asks it.\n ...(g.takes > 1 ? { judge_model: g.judgeModel, judge_prompt: g.prompt } : {}),\n },\n tags: { role: VARIANT_PREFILTER_ROLE },\n },\n magick(\"master\", STATIC_MASTER_ROLE, \"$ref:prefilter.image\", fillArgs(plan.master.size)),\n ];\n const adapted = plan.sizes.filter((s) => s.from === \"adapt\");\n const adaptFormats = [...new Set(adapted.map((s) => s.adaptFormat ?? s.format))];\n if (adaptFormats.length > 0) {\n nodes.push({\n id: \"adapt\",\n type: \"image_aspect_adapt\",\n inputs: { source: \"$ref:master.image\" },\n params: {\n model: g.adaptModel,\n formats: adaptFormats,\n image_size: ADAPT_IMAGE_SIZE,\n safe_areas: Object.fromEntries(\n adaptFormats.map((f) => [\n f,\n safeAreaOf(\n f,\n adapted.filter((s) => (s.adaptFormat ?? s.format) === f).map((s) => s.format),\n ),\n ]),\n ),\n },\n tags: { role: STATIC_ADAPT_ROLE },\n });\n }\n nodes.push(...relayoutNodes(g));\n const refs = plan.sizes.map((s) => {\n const slug = formatSlug(s.format);\n const made = sizeNodes(s, plan, adaptFormats);\n nodes.push(...made);\n const last = made[made.length - 1];\n const ref = last ? `$ref:${last.id}.image` : \"$ref:master.image\";\n if (!g.compositeLogo) return ref;\n const slot = logoSlot(s.size, staticSafeArea(s.format)).rect;\n nodes.push(\n magick(\n `logo_${slug}`,\n LOGO_COMPOSITE_ROLE,\n ref,\n [\n \"{{in.image}}\",\n \"(\",\n \"{{in.logo}}\",\n \"-resize\",\n `${slot.width}x${slot.height}`,\n \")\",\n \"-gravity\",\n \"NorthWest\",\n \"-geometry\",\n `+${slot.x}+${slot.y}`,\n \"-composite\",\n \"{{out.image}}\",\n ],\n { logo: g.compositeLogo },\n ),\n );\n return `$ref:logo_${slug}.image`;\n });\n nodes.push({\n id: \"final\",\n type: \"collect\",\n inputs: { images: refs },\n params: { labels: plan.sizes.map((s) => s.format) },\n });\n return nodes;\n}\n\n/** The nodes that deliver one size after the renders, in order: its size node (none for the master itself) and the free safe fit an interface asks for. */\nfunction sizeNodes(s: SizePlan, plan: StaticFormatPlan, adaptFormats: readonly string[]): NodeDecl[] {\n const size = sizeNode(s, adaptFormats);\n const fit = safeFitNode(s.format, size ? `$ref:${size.id}.image` : \"$ref:master.image\", plan.placements);\n return [...(size ? [size] : []), ...(fit ? [fit] : [])];\n}\n\n/**\n * The free correction after a size an interface covers (a 9:16 in Reels or Stories): the\n * image model cannot move an element a few points on request, so a word or logo a little\n * past the band is brought inside it by a shrink and shift instead of another render. One too\n * far past it for that is passed on unchanged: the review names what the app covers and the run\n * lays that size out again (never the ad framed over its own blur: \"the faded background looks bad\").\n * Null for a size nothing covers.\n */\nexport function safeFitNode(format: string, image: string, placements: readonly string[] | undefined): NodeDecl | null {\n if (staticSafeArea(format, placements).placement === \"feed\") return null;\n return {\n id: `safe_${formatSlug(format)}`,\n type: \"static_safe_fit\",\n inputs: { image },\n params: { format, ...(placements ? { placements: [...placements] } : {}) },\n tags: { role: STATIC_SAFE_FIT_ROLE },\n };\n}\n\n/**\n * The node that makes one size, when it is not the master itself: a re-laid-out size or an\n * adapted one brought to its exact pixels, or a cut of the master.\n */\nfunction sizeNode(s: SizePlan, adaptFormats: readonly string[]): NodeDecl | null {\n const id = `size_${formatSlug(s.format)}`;\n if (s.from === \"crop\" && s.crop) {\n const c = s.crop;\n const exact = c.width === s.size.width && c.height === s.size.height;\n return magick(id, STATIC_SIZE_ROLE, \"$ref:master.image\", [\n \"{{in.image}}\",\n \"-crop\",\n `${c.width}x${c.height}+${c.x}+${c.y}`,\n \"+repage\",\n ...(exact ? [] : [\"-filter\", \"Lanczos\", \"-resize\", `${s.size.width}x${s.size.height}!`]),\n \"{{out.image}}\",\n ]);\n }\n if (s.from === \"relayout\") {\n return magick(id, STATIC_SIZE_ROLE, `$ref:adapt_${formatSlug(s.format)}.images#0`, fillArgs(s.size));\n }\n if (s.from !== \"adapt\") return null;\n const index = adaptFormats.indexOf(s.adaptFormat ?? s.format);\n return magick(id, STATIC_SIZE_ROLE, `$ref:adapt.images#${index}`, fillArgs(s.size));\n}\n\n/** The quality every re-laid-out size asks for: the variants' own step (`prompt-profiles.ts`), xhigh for speed. */\nconst RELAYOUT_QUALITY = \"xhigh\";\n\n/**\n * relayout: one request per other size, each handed the chosen ad (and the logo file the model\n * draws) and rendered at the model's nearest ratio to its format; the brief is projected from\n * prompt.json on every run (`projectAdaptBriefs`).\n */\nfunction relayoutNodes(g: StaticRenderGraph): NodeDecl[] {\n return g.plan.sizes.filter((s) => s.from === \"relayout\").map((s) => relayoutNode(s, g));\n}\n\nfunction relayoutNode(s: SizePlan, g: StaticRenderGraph): NodeDecl {\n return {\n id: `adapt_${formatSlug(s.format)}`,\n type: \"image_generate\",\n inputs: { reference: [\"$ref:master.image\", ...(g.logoRef ? [g.logoRef] : [])] },\n params: {\n model: g.relayoutModel,\n aspect_ratio: adaptPlan(g.relayoutModel, s.format, g.plan.placements).renderRatio,\n ...imageRenderParams(g.relayoutModel, { size: ADAPT_IMAGE_SIZE, quality: RELAYOUT_QUALITY }),\n prompt: g.prompt,\n },\n tags: { role: STATIC_ADAPT_FORMAT_ROLE },\n };\n}\n\n/** The ids the render graph takes, so no element slot collides with them. */\nexport const RENDER_GRAPH_IDS = [\"gen\", \"prefilter\", \"pick\", \"master\", \"adapt\", \"final\", \"fix\", \"previous\"] as const;\n\n/** The ids an older static graph gave its renders: a take, a re-lay, a centred crop. */\nconst LEGACY_RENDER_ID = /^(?:ad(?:_[a-c])?|ad_\\d+x\\d+|fit_\\d+x\\d+)$/;\n\n/** The renders a static ad's variants come from: the one request, or each request of a fanned-out one. */\nfunction variantRenders(canvas: unknown): string[] {\n const fanned = nodesWithRole(canvas, STATIC_VARIANT_CALL_ROLE).map((n) => n.id);\n if (fanned.length > 0) return fanned;\n return nodesWithRole(canvas, STATIC_VARIANTS_ROLE)\n .filter((n) => n.type === \"image_generate\")\n .map((n) => n.id);\n}\n\n/**\n * What `--regenerate` names, as node ids: an id as it is; a role as every node that carries\n * it (`static_variants` is the renders the variants come from, whatever their ids); and, on a\n * static ad, the id of the collect that gathers the variants (`gen`) or an id from the older\n * graph (`ad_a`, `ad_9x16`, `fit_4x5`) as the variants, since every size is now made from the\n * pick. Names that match nothing come back in `unknown`.\n */\nexport function regenerateTargets(canvas: unknown, requested: readonly string[]): { ids: string[]; unknown: string[] } {\n const known = new Set(nodesOf(canvas).flatMap((n) => (typeof n?.id === \"string\" ? [n.id] : [])));\n const gathered = new Set(\n nodesWithRole(canvas, STATIC_VARIANTS_ROLE)\n .filter((n) => n.type === \"collect\")\n .map((n) => n.id),\n );\n const ids: string[] = [];\n const unknown: string[] = [];\n for (const name of requested) {\n const variants =\n name === STATIC_VARIANTS_ROLE || gathered.has(name) || (!known.has(name) && LEGACY_RENDER_ID.test(name));\n const found = variants\n ? variantRenders(canvas)\n : known.has(name)\n ? [name]\n : nodesWithRole(canvas, name).map((n) => n.id);\n if (found.length === 0) unknown.push(name);\n for (const id of found) if (!ids.includes(id)) ids.push(id);\n }\n return { ids, unknown };\n}\n","import { imageProfileFor } from \"../../../lib/prompt-profiles.ts\";\nimport {\n STATIC_ADAPT_FORMAT_ROLE,\n STATIC_FIX_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_VARIANTS_ROLE,\n} from \"../../../scaffold/static-topology.ts\";\nimport type { LintContext, LintRule } from \"../context.ts\";\n\nconst CODES = {\n IMAGE_PROFILE_MISSING: \"IMAGE_PROMPT_PROFILE_MISSING\",\n PARALLEL_FORMAT_RENDERS: \"IMAGE_PARALLEL_FORMAT_RENDERS\",\n} as const;\n\n/** Advisory: an image frame renders on a model with no per-model prompt profile, so\n * it got the generic frame template (exclusion placement / photoreal cue not tuned). */\nfunction checkImageProfile(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"image_generate\") continue;\n const model = (n.params as Record<string, unknown> | undefined)?.model;\n if (typeof model !== \"string\" || imageProfileFor(model)) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.model`,\n code: CODES.IMAGE_PROFILE_MISSING,\n severity: \"warning\",\n node_id: n.id,\n message: `\"${n.id}\" renders on \"${model}\", an image model with no frame-prompt profile — it got the generic template (constraint placement / photoreal cue not tuned). Add an ImageModelProfile for this model`,\n });\n }\n}\n\n// Two prompts this similar (Jaccard over their word sets) are the SAME creative — a\n// per-ratio copy, not a second concept. Deliberately high: variants of one concept\n// share wording too, and they're only flagged when the RATIOS also differ.\nconst SAME_CREATIVE_PROMPT_SIMILARITY = 0.6;\n\nfunction promptWordSet(text: string): Set<string> {\n return new Set(\n text\n .toLowerCase()\n .split(/[^a-z0-9']+/)\n .filter((w) => w.length >= 3),\n );\n}\n\nfunction jaccard(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 || b.size === 0) return 0;\n let shared = 0;\n for (const w of a) if (b.has(w)) shared++;\n return shared / (a.size + b.size - shared);\n}\n\ntype FormatRenderNode = { id: string; prompt: string; ratio: string };\n\n/**\n * Groups of `image_generate` nodes that render the SAME creative once per aspect\n * ratio (near-identical prompts, different `aspect_ratio`). Pure, so the clustering\n * is testable on its own. Single-link clustering: any two similar-prompt nodes with\n * different ratios join the same group.\n */\nexport function parallelFormatRenderGroups(nodes: readonly FormatRenderNode[]): FormatRenderNode[][] {\n const parent = nodes.map((_, i) => i);\n const find = (i: number): number => {\n let root = i;\n while (parent[root] !== root) root = parent[root] as number;\n return root;\n };\n const words = nodes.map((n) => promptWordSet(n.prompt));\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i + 1; j < nodes.length; j++) {\n const a = nodes[i] as FormatRenderNode;\n const b = nodes[j] as FormatRenderNode;\n if (a.ratio === b.ratio) continue;\n if (jaccard(words[i] as Set<string>, words[j] as Set<string>) < SAME_CREATIVE_PROMPT_SIMILARITY) continue;\n parent[find(i)] = find(j);\n }\n }\n const groups = new Map<number, FormatRenderNode[]>();\n for (let i = 0; i < nodes.length; i++) {\n const root = find(i);\n const group = groups.get(root) ?? [];\n group.push(nodes[i] as FormatRenderNode);\n groups.set(root, group);\n }\n return [...groups.values()].filter((g) => g.length > 1);\n}\n\n/**\n * The roles of a static ad's own renders (`static-topology.ts`): its variants are one render\n * whatever requests they take, its correction replaces them, each other size is the chosen\n * picture re-laid out for its frame with that picture as the reference. None is an\n * independent render per format.\n */\nconst STATIC_RENDER_ROLES: ReadonlySet<string> = new Set([\n STATIC_VARIANTS_ROLE,\n STATIC_VARIANT_CALL_ROLE,\n STATIC_FIX_ROLE,\n STATIC_ADAPT_FORMAT_ROLE,\n]);\n\n/**\n * Advisory: the same creative generated once per placement ratio instead of once,\n * then fanned out. Each render is an independent generation, so the \"same\" ad comes\n * back with different type, crop, and color per format — the set stops looking like\n * one campaign. A render fed a sibling's output (a re-lay) is no exception: it is\n * still a new picture per format. The fix is ONE render and every other ratio made\n * from it (the static scaffold re-lays them out from its chosen picture; anything else ends in a\n * terminal `image_aspect_adapt` node). Read by role, a static ad's variants are one render.\n */\nfunction checkParallelFormatRenders(ctx: LintContext): void {\n const candidates: FormatRenderNode[] = [];\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"image_generate\" || STATIC_RENDER_ROLES.has(n.tags?.role ?? \"\")) continue;\n const params = n.params as Record<string, unknown> | undefined;\n const prompt = params?.prompt;\n if (typeof prompt !== \"string\" || prompt.length === 0) continue;\n candidates.push({ id: n.id, prompt, ratio: String(params?.aspect_ratio ?? \"(unset)\") });\n }\n if (candidates.length < 2) return;\n for (const group of parallelFormatRenderGroups(candidates)) {\n const ids = group.map((g) => g.id);\n const ratios = [...new Set(group.map((g) => g.ratio))].sort();\n ctx.issues.push({\n path: \"nodes\",\n code: CODES.PARALLEL_FORMAT_RENDERS,\n severity: \"warning\",\n message: `${ids.join(\", \")} render the same creative once per aspect ratio (${ratios.join(\", \")}) — each is an independent generation, so the formats come back with different type, crop, and color and stop reading as one ad. Keep ONE hero render and add a terminal image_aspect_adapt node fed from it (params.formats or params.platform) so every ratio is re-framed from the same image`,\n });\n }\n}\n\nexport const parallelFormatRendersRule: LintRule = {\n name: \"parallel-format-renders\",\n codes: { [CODES.PARALLEL_FORMAT_RENDERS]: \"warning\" },\n check: checkParallelFormatRenders,\n};\n\nexport const imageProfileRule: LintRule = {\n name: \"image-profile\",\n codes: { [CODES.IMAGE_PROFILE_MISSING]: \"warning\" },\n check: checkImageProfile,\n};\n","import { supportsLastFrame } from \"@baker/canvas-contract\";\nimport { clipProfileFor } from \"../../../lib/prompt-profiles.ts\";\nimport type { NodeDecl } from \"../../../schema/canvas.ts\";\nimport { isTagged, type LintContext, type LintRule, onVideoCanvas, refNodeOf, type VideoMetaT } from \"../context.ts\";\n\n// Checks that read one clip's or frame's prompt and params against what its model\n// does with them. The first group runs on ANY canvas (a hand-edited canvas that lost\n// its metadata still gets the prompt/param advisories); the rest only on a canvas\n// carrying a video scaffold's timing plan.\n\nconst CODES = {\n PROMPT_PROFILE_MISSING: \"VIDEO_PROMPT_PROFILE_MISSING\",\n PROMPT_DECISION_MISSING: \"VIDEO_PROMPT_DECISION_MISSING\",\n HOOK_LAYER_MISSING: \"VIDEO_HOOK_LAYER_MISSING\",\n PERSON_GENERATION_MISSING: \"VIDEO_PERSON_GENERATION_MISSING\",\n PROMPT_OVER_BUDGET: \"VIDEO_PROMPT_OVER_BUDGET\",\n RAW_FACE_KEYFRAME: \"VIDEO_RAW_FACE_KEYFRAME\",\n REFERENCE_MISSING: \"VIDEO_REFERENCE_MISSING\",\n MODEL_IGNORES_INPUT: \"VIDEO_MODEL_IGNORES_INPUT\",\n UI_IN_PROMPT: \"VIDEO_UI_IN_PROMPT\",\n BRANDMARK_IN_PROMPT: \"VIDEO_BRANDMARK_IN_PROMPT\",\n} as const;\n\n/**\n * Advisory: a clip on a video model with NO prompt profile was authored with the\n * default Seedance syntax — its dialogue/emotion markup may not carry over (Veo,\n * for one, renders [brackets] as literal text). Review before billing.\n */\nfunction checkPromptProfile(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const model = (n.params as Record<string, unknown> | undefined)?.model;\n if (typeof model !== \"string\" || clipProfileFor(model)) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.model`,\n code: CODES.PROMPT_PROFILE_MISSING,\n severity: \"warning\",\n node_id: n.id,\n message: `\"${n.id}\" runs on \"${model}\", a model with no clip-prompt profile — its prompt was authored with the default Seedance syntax. Review the dialogue/emotion markup for this model before billing`,\n });\n }\n}\n\n// A clip prompt that made the TECHNIQUE decision names a camera/motion move.\nconst TECHNIQUE_CUE =\n /\\b(camera|push[- ]?in|pull[- ]?out|pan|dolly|zoom|tilt|track|handheld|locked|orbit|crane|whip|motion|move)\\b/i;\n\n// A clip prompt that made the NEGATIVES/constraints decision: Seedance phrases it as an\n// affirmative stability line (\"Keep it clean and stable: …\"), Veo/Kling take a real\n// `negative_prompt` param, and a hand-written prompt may still use a literal `Avoid:` tail.\nconst NEGATIVES_CUE = /\\bavoid:|\\bno subtitles|\\bnegative\\b|keep it clean and stable/i;\n\n// The VIBE layer: lighting / grade / tone words in the hook's frame or clip prompt.\nconst VIBE_CUE =\n /\\b(light|lighting|golden|moody|warm|cool|tone|grade|contrast|shadow|glow|rim|backlit|neon|soft|harsh)\\b/i;\n\n/**\n * Advisory (T3.1): mechanizes the skill's \"six decisions\" (route / spec / beats /\n * copy / technique / negatives) at the two that are checkable from a single clip\n * node — TECHNIQUE (a named camera/motion move) and NEGATIVES (an `Avoid:` line or\n * a `negative_prompt` param). A clip prompt missing BOTH is a thin prompt the model\n * will fill with its own defaults; warn so the operator decides the missing beats.\n * Doctrine: `creative-canvas-ads/references/prompt-anatomy.md`.\n */\nfunction checkPromptDecisions(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const params = n.params as Record<string, unknown> | undefined;\n const prompt = typeof params?.prompt === \"string\" ? params.prompt : \"\";\n if (!prompt) continue;\n const hasTechnique = TECHNIQUE_CUE.test(prompt);\n const hasNegatives = NEGATIVES_CUE.test(prompt) || typeof params?.negative_prompt === \"string\";\n if (hasTechnique || hasNegatives) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,\n code: CODES.PROMPT_DECISION_MISSING,\n severity: \"warning\",\n node_id: n.id,\n message: `\"${n.id}\" is a thin clip prompt — it names no camera/motion move (TECHNIQUE) and no constraints (NEGATIVES: an affirmative \"Keep it clean and stable\" line, a negative_prompt param, or an \"Avoid:\" tail). Make the six decisions (route / spec / beats / copy / technique / negatives) so the model doesn't fill the gaps with drift. See prompt-anatomy.md`,\n });\n }\n}\n\n/**\n * Advisory (T3.1): a strong hook works on all FOUR layers — text (overlay), sound\n * (line/SFX), visual (the frame), vibe (lighting+tone). This scans scene 0's nodes\n * and warns when two or more layers are absent, so a bare hook doesn't ship. Only\n * fires on scene-based video canvases (a node tagged scene 0, or on an untagged canvas\n * a node id starting `s0`). Doctrine:\n * `creative-canvas-ads/references/hook-craft.md`.\n */\nfunction checkHookLayers(ctx: LintContext): void {\n const scene0 = isTagged(ctx)\n ? ctx.canvas.nodes.filter((n) => n.tags?.scene === 0)\n : ctx.canvas.nodes.filter((n) => /^s0(?:[_a-z0-9])*_/.test(n.id) || n.id === \"s0\");\n const hookClip = scene0.find((n) => n.type === \"video_generate\");\n if (!hookClip) return;\n const scene0Text = scene0\n .map((n) => {\n const p = (n.params as Record<string, unknown> | undefined)?.prompt;\n return typeof p === \"string\" ? p : \"\";\n })\n .join(\"\\n\");\n const hookParams = hookClip.params as Record<string, unknown> | undefined;\n\n const hasOverlayLayer = ctx.canvas.nodes.some((n) => /hyperframe/.test(n.type));\n const hasSound =\n hookParams?.generate_audio === true ||\n /Dialogue:|Audio:/.test(scene0Text) ||\n ctx.canvas.nodes.some((n) => n.type === \"tts\" || n.type === \"dialogue\" || n.type === \"music\");\n const hasVisual = scene0Text.trim().length > 40;\n const hasVibe = VIBE_CUE.test(scene0Text);\n\n const missing: string[] = [];\n if (!hasOverlayLayer) missing.push(\"text (overlay)\");\n if (!hasSound) missing.push(\"sound (line/SFX)\");\n if (!hasVisual) missing.push(\"visual (the frame)\");\n if (!hasVibe) missing.push(\"vibe (lighting/tone)\");\n if (missing.length < 2) return;\n\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(hookClip.id) ?? -1}]`,\n code: CODES.HOOK_LAYER_MISSING,\n severity: \"warning\",\n node_id: hookClip.id,\n message: `the hook (scene 0) is thin across layers — missing ${missing.join(\", \")}. A scroll-stopping hook works on all four layers (text / sound / visual / vibe); strengthen the missing ones. See hook-craft.md`,\n });\n}\n\n/** Advisory: a Veo image-to-video clip must set `person_generation` (only `allow_adult`\n * is legal for image-driven modes, and the sole legal value in the EU/UK). */\nfunction checkPersonGeneration(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const params = n.params as Record<string, unknown> | undefined;\n const model = params?.model;\n if (clipProfileFor(typeof model === \"string\" ? model : \"\")?.id !== \"veo\") continue;\n const hasKeyframe = Boolean(n.inputs?.first_frame ?? n.inputs?.reference);\n if (!hasKeyframe || params?.person_generation) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params`,\n code: CODES.PERSON_GENERATION_MISSING,\n severity: \"warning\",\n node_id: n.id,\n message: `Veo clip \"${n.id}\" drives from a keyframe but sets no person_generation — set \"allow_adult\" (the only legal value for image-to-video, and the only one allowed in the EU/UK)`,\n });\n }\n}\n\n/** Advisory: a clip prompt runs past the model's quality word budget — long prompts\n * pack conflicting instructions and the model starts dropping beats. */\nfunction checkPromptBudget(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const params = n.params as Record<string, unknown> | undefined;\n const model = params?.model;\n const prompt = params?.prompt;\n const profile = typeof model === \"string\" ? clipProfileFor(model) : undefined;\n if (!profile || typeof prompt !== \"string\") continue;\n const words = prompt.trim().split(/\\s+/).filter(Boolean).length;\n if (words <= profile.wordBudget) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,\n code: CODES.PROMPT_OVER_BUDGET,\n severity: \"warning\",\n node_id: n.id,\n message: `\"${n.id}\" prompt is ${words} words — over ${model}'s ~${profile.wordBudget}-word budget; trim it (the frames carry the content, so keep the clip prompt to motion + audio)`,\n });\n }\n}\n\n/** Advisory: a Seedance image-to-video clip whose first frame is a RAW ingested image\n * (reuse mode, or a dropped photo) rather than a generated frame. If it contains a real\n * human face, ByteDance's real-person filter rejects it (a non-retryable 422) — the fix\n * is an AI-generated portrait keyframe (recast), or route the clip to Veo. */\nfunction checkRawFaceKeyframe(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const model = (n.params as Record<string, unknown> | undefined)?.model;\n if (clipProfileFor(typeof model === \"string\" ? model : \"\")?.id !== \"seedance\") continue;\n const src = refNodeOf(ctx, n.inputs?.first_frame ?? n.inputs?.reference);\n if (!src || src.type !== \"ingest\") continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].inputs.first_frame`,\n code: CODES.RAW_FACE_KEYFRAME,\n severity: \"warning\",\n node_id: n.id,\n message: `Seedance clip \"${n.id}\" animates a RAW ingested image (\"${src.id}\"), not a generated frame — if it shows a real human face, ByteDance's real-person filter rejects it (422). Anchor on an AI-generated portrait (recast), or route this clip to Veo`,\n });\n }\n}\n\n// Type-noun synonyms per element type — the low-false-positive nouns whose presence\n// in a frame description strongly implies the element is on screen. Person is\n// deliberately excluded: pronouns/role-words are too noisy and identity is already\n// locked by the multi-view sheet.\nconst ELEMENT_TYPE_KEYWORDS: Record<string, string[]> = {\n animal: [\"dog\", \"puppy\", \"pup\", \"cat\", \"kitten\", \"kitty\", \"pet\", \"canine\", \"feline\"],\n logo: [\"logo\", \"wordmark\"],\n badge: [\"badge\", \"seal\"],\n product: [],\n};\n\nconst CHECKED_ELEMENT_TYPES = new Set(Object.keys(ELEMENT_TYPE_KEYWORDS));\n\n// Tokens too generic to signal a specific element in a frame description.\nconst KEYWORD_STOPWORDS = new Set([\n \"the\",\n \"and\",\n \"with\",\n \"for\",\n \"brand\",\n \"logo\",\n \"image\",\n \"shot\",\n \"main\",\n \"hero\",\n \"element\",\n \"product\",\n \"reference\",\n]);\n\n/** Significant lowercase tokens (len ≥ 3, non-stopword) of a label/description. */\nfunction keywordTokens(text: string | undefined): string[] {\n if (!text) return [];\n return text\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .filter((t) => t.length >= 3 && !KEYWORD_STOPWORDS.has(t));\n}\n\n/**\n * The keyword set that, in a frame description, implies an element of this\n * type/label/description is on screen. Shared with the scaffold's wiring pass so a\n * frame the validator would flag is wired by construction (same matcher both sides).\n */\nexport function elementMentionKeywords(el: { type: string; label?: string; description?: string }): string[] {\n const typeWords = ELEMENT_TYPE_KEYWORDS[el.type.toLowerCase()] ?? [];\n return [...new Set([...typeWords, ...keywordTokens(el.label), ...keywordTokens(el.description)])];\n}\n\ntype VideoElement = NonNullable<VideoMetaT[\"elements\"]>[number];\n\n/** The keyword set that, in a frame description, implies this element is on screen. */\nfunction keywordsForElement(el: VideoElement): string[] {\n return elementMentionKeywords(el);\n}\n\n/** Does `text` contain `word` as a whole word (avoids \"cat\" ⊂ \"category\")? */\nfunction containsWord(text: string, word: string): boolean {\n const esc = word.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n return new RegExp(`\\\\b${esc}\\\\b`, \"i\").test(text);\n}\n\n/**\n * Advisory (§13, the wrong-dog defect): every `image_generate` frame whose FRAME\n * DESCRIPTION plausibly includes a recurring element (a pet, product, logo, badge)\n * must wire that element's reference. When it doesn't, the frame model has nothing to\n * lock the element's identity to — its own system prompt forbids trusting prose — so\n * it invents a generic one (a different breed of dog). Scoped to non-person elements,\n * where a keyword→missing-ref match is high-signal; person identity is sheet-backed.\n */\ntype KeywordedElement = { el: VideoElement; keywords: string[] };\n\n// The scaffold's frame prompts wrap the editable FRAME DESCRIPTION in boilerplate\n// that itself mentions element keywords (\"If a reference image (a logo, a desk…)\",\n// \"do not invent brand logos\") — matching the whole prompt false-positived on every\n// scaffolded frame. Only the description between these markers is the frame's OWN\n// content; a hand-authored prompt (no marker) is matched whole.\nconst FRAME_DESCRIPTION_START = \"FRAME DESCRIPTION (this frame's editable prompt):\";\n\nconst FRAME_DESCRIPTION_END = \"Render exactly what the FRAME DESCRIPTION\";\n\nexport function frameDescriptionOf(prompt: string): string {\n const i = prompt.indexOf(FRAME_DESCRIPTION_START);\n if (i < 0) return prompt;\n const rest = prompt.slice(i + FRAME_DESCRIPTION_START.length);\n const j = rest.indexOf(FRAME_DESCRIPTION_END);\n return j < 0 ? rest : rest.slice(0, j);\n}\n\n/** Warn for each keyworded element a frame describes but doesn't wire. */\nfunction checkFrameReferences(ctx: LintContext, node: NodeDecl, index: number, keyworded: KeywordedElement[]): void {\n const rawPrompt = (node.params as Record<string, unknown> | undefined)?.prompt;\n if (typeof rawPrompt !== \"string\" || rawPrompt.length === 0) return;\n const prompt = frameDescriptionOf(rawPrompt);\n const inputsBlob = JSON.stringify(node.inputs ?? {});\n for (const { el, keywords } of keyworded) {\n if (inputsBlob.includes(el.ref)) continue; // element already wired on this frame\n const hit = keywords.find((kw) => containsWord(prompt, kw));\n if (!hit) continue;\n ctx.issues.push({\n path: `nodes[${index}].inputs.reference`,\n code: CODES.REFERENCE_MISSING,\n severity: \"warning\",\n message: `frame \"${node.id}\" describes a \"${hit}\" but the \"${el.label}\" reference (${el.ref}) isn't wired on this node — the generator will invent a wrong-identity ${el.type} from prose. Add ${el.ref} to inputs.reference`,\n node_id: node.id,\n node_type: node.type,\n });\n }\n}\n\nfunction checkReferenceCompleteness(ctx: LintContext, meta: VideoMetaT): void {\n const elements = (meta.elements ?? []).filter((el) => CHECKED_ELEMENT_TYPES.has(el.type.toLowerCase()));\n const keyworded = elements\n .map((el) => ({ el, keywords: keywordsForElement(el) }))\n .filter((e) => e.keywords.length > 0);\n if (keyworded.length === 0) return;\n\n // A picture a reference sheet is fused from (an invented person's portrait) is an\n // element's source, not a scene frame: it shows that one subject alone, so it wires no\n // logo, product or pet by design.\n const sheetSources = JSON.stringify(\n ctx.canvas.nodes.filter((n) => n.type === \"image_reference_sheet\").map((n) => n.inputs ?? {}),\n );\n for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (n?.type !== \"image_generate\" || sheetSources.includes(`\"$ref:${n.id}.`)) continue;\n checkFrameReferences(ctx, n, i, keyworded);\n }\n}\n\n/**\n * Advisory: a clip wiring a `last_frame` into a model that takes only ONE\n * conditioning image (Omni — its Interactions API has no end-keyframe concept), so\n * the backend drops the second frame. The canvas still renders, which is exactly\n * the problem: the author paid to GENERATE that keyframe image and it silently does\n * nothing. Free to catch here, a billed image per clip to discover afterwards.\n */\nfunction checkUnusableLastFrame(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const model = (n.params as Record<string, unknown> | undefined)?.model;\n if (typeof model !== \"string\" || supportsLastFrame(model)) continue;\n const inputs = n.inputs as Record<string, unknown> | undefined;\n if (!inputs?.last_frame) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].inputs.last_frame`,\n code: CODES.MODEL_IGNORES_INPUT,\n severity: \"warning\",\n node_id: n.id,\n node_type: \"video_generate\",\n message: `\"${n.id}\" wires a last_frame but runs on ${model}, which takes only ONE conditioning image — the end keyframe is dropped and the image generated for it is wasted. Drop the last_frame, or move this clip to Veo/Seedance, which honour both frames`,\n });\n }\n}\n\n// A screen/UI surface narrated in a video_generate prompt: a generative model garbles\n// UI text and brand chrome, so a screen surface must be composited as a REAL asset on\n// the overlay layer, never described into a clip. Narrow on purpose (screen-recording\n// language, not any mention of a phone) — advisory, not blocking.\nconst UI_IN_PROMPT_RE =\n /\\bscreen[- ]?(?:recording|capture|grab|share)\\b|\\bapp (?:interface|screen)\\b|\\bphone screen overlay\\b/i;\n\n/** Advisory: a clip prompt asking the video model to render a screen/UI surface. */\nfunction checkUiInPrompt(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\") continue;\n const prompt = (n.params as Record<string, unknown> | undefined)?.prompt;\n if (typeof prompt !== \"string\" || !UI_IN_PROMPT_RE.test(prompt)) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,\n code: CODES.UI_IN_PROMPT,\n severity: \"warning\",\n message: `\"${n.id}\" asks the video model to render a screen/UI surface — generative video garbles UI text and chrome. Composite the real screen on the overlay layer (screenshot / brand HTML) and keep this prompt to the background plate`,\n node_id: n.id,\n node_type: \"video_generate\",\n });\n }\n}\n\n// A brand mark REQUESTED as picture content: \"…logo overlay/animation/sting\", or a\n// named third-party mark. A logo merely worn/present in the filmed scene (on a polo,\n// a storefront) is part of the picture and stays — the pattern needs the mark to be\n// the rendered element itself. Generative models garble wordmarks, and a real mark\n// belongs on the overlay layer as a sourced asset. Advisory, not blocking.\nconst BRANDMARK_IN_PROMPT_RE =\n /\\b(?:logo|wordmark) (?:overlay|animation|sting|card|reveal)\\b|\\b(?:google|facebook|instagram|tiktok|youtube|amazon|apple|microsoft|whatsapp|netflix|spotify|excel|trustpilot) (?:logo|wordmark|branding)\\b/i;\n\n/** Advisory: a generate prompt asking the model to paint a brand logo/wordmark. */\nfunction checkBrandmarkInPrompt(ctx: LintContext): void {\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"video_generate\" && n.type !== \"image_generate\") continue;\n const prompt = (n.params as Record<string, unknown> | undefined)?.prompt;\n if (typeof prompt !== \"string\" || !BRANDMARK_IN_PROMPT_RE.test(prompt)) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,\n code: CODES.BRANDMARK_IN_PROMPT,\n severity: \"warning\",\n message: `\"${n.id}\" asks the model to render a brand logo/wordmark — generation garbles marks and third-party logos carry IP exposure. Source the real mark (baker images logo <domain>) and composite it on the overlay layer`,\n node_id: n.id,\n node_type: n.type,\n });\n }\n}\n\nexport const promptProfileRule: LintRule = {\n name: \"video-prompt-profile\",\n codes: { [CODES.PROMPT_PROFILE_MISSING]: \"warning\" },\n check: checkPromptProfile,\n};\n\nexport const personGenerationRule: LintRule = {\n name: \"video-person-generation\",\n codes: { [CODES.PERSON_GENERATION_MISSING]: \"warning\" },\n check: checkPersonGeneration,\n};\n\nexport const promptBudgetRule: LintRule = {\n name: \"video-prompt-budget\",\n codes: { [CODES.PROMPT_OVER_BUDGET]: \"warning\" },\n check: checkPromptBudget,\n};\n\nexport const rawFaceKeyframeRule: LintRule = {\n name: \"video-raw-face-keyframe\",\n codes: { [CODES.RAW_FACE_KEYFRAME]: \"warning\" },\n check: checkRawFaceKeyframe,\n};\n\nexport const promptDecisionsRule: LintRule = {\n name: \"video-prompt-decisions\",\n codes: { [CODES.PROMPT_DECISION_MISSING]: \"warning\" },\n check: checkPromptDecisions,\n};\n\nexport const hookLayersRule: LintRule = {\n name: \"video-hook-layers\",\n codes: { [CODES.HOOK_LAYER_MISSING]: \"warning\" },\n check: checkHookLayers,\n};\n\n/** No clip prompt should narrate a screen/UI surface (garbled UI). */\nexport const uiInPromptRule: LintRule = {\n name: \"video-ui-in-prompt\",\n codes: { [CODES.UI_IN_PROMPT]: \"warning\" },\n check: onVideoCanvas((ctx) => checkUiInPrompt(ctx)),\n};\n\n/** No generate prompt should request a brand logo/wordmark as content. */\nexport const brandmarkInPromptRule: LintRule = {\n name: \"video-brandmark-in-prompt\",\n codes: { [CODES.BRANDMARK_IN_PROMPT]: \"warning\" },\n check: onVideoCanvas((ctx) => checkBrandmarkInPrompt(ctx)),\n};\n\n/**\n * A frame that describes a recurring element (a pet, product, logo) must WIRE that\n * element's reference, or the generator invents a wrong-identity one.\n */\nexport const referenceCompletenessRule: LintRule = {\n name: \"video-reference-completeness\",\n codes: { [CODES.REFERENCE_MISSING]: \"warning\" },\n check: onVideoCanvas(checkReferenceCompleteness),\n};\n\nexport const unusableLastFrameRule: LintRule = {\n name: \"video-unusable-last-frame\",\n codes: { [CODES.MODEL_IGNORES_INPUT]: \"warning\" },\n check: onVideoCanvas((ctx) => checkUnusableLastFrame(ctx)),\n};\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { maxClipSeconds, supportsParam } from \"@baker/canvas-contract\";\n// The Dialogue-marker parser lives with the prompt profiles so the emitter and this\n// validator can never drift on the parse contract.\nimport { nativeDialogueOf } from \"../../../lib/prompt-profiles.ts\";\nimport { NATIVE_CLIP_WORDS_PER_SECOND, OBSERVED_WPS_MAX, OBSERVED_WPS_MIN, wordCount } from \"../../../lib/speech.ts\";\nimport { parseSpineInputLens } from \"../../../lib/spine-input.ts\";\nimport type { Canvas, NodeDecl } from \"../../../schema/canvas.ts\";\nimport type { StaticTiming } from \"../../define.ts\";\nimport type { ValidationIssue } from \"../../errors.ts\";\nimport {\n type DeepLintRule,\n isTagged,\n type LintContext,\n type LintRule,\n onVideoCanvas,\n refNodeOf,\n type VideoMetaT,\n} from \"../context.ts\";\n\n// Whole-canvas video invariants, proven STATICALLY from the timing plan a video\n// scaffold records in `metadata.video` (per-node `validateExtra` can't see across\n// nodes). These catch the failure modes that otherwise only surface after a\n// ~50cr/clip render: voiceover turns playing on top of each other, an audio mix\n// that runs well past the picture, and a talking subject whose clip was never\n// natively voiced. Silent when no `metadata.video` is present (non-video canvases).\n\nconst CODES = {\n VO_OVERLAP: \"VIDEO_VO_OVERLAP\",\n AUDIO_DURATION: \"VIDEO_AUDIO_DURATION\",\n LIPSYNC_MISSING: \"VIDEO_LIPSYNC_MISSING\",\n SPEECH_OVERRUN: \"VIDEO_SPEECH_OVERRUN\",\n SPEECH_EXCEEDS_EXTRACT: \"VIDEO_SPEECH_EXCEEDS_EXTRACT\",\n ASPECT_MISMATCH: \"VIDEO_ASPECT_MISMATCH\",\n SPAN_EXCEEDS_MODEL: \"VIDEO_SPAN_EXCEEDS_MODEL\",\n TIMELINE_TOTAL: \"VIDEO_TIMELINE_TOTAL_MISMATCH\",\n NATIVE_SEG_OVERLAP: \"VIDEO_NATIVE_SEG_OVERLAP\",\n SPINE_UNNORMALIZED: \"VIDEO_SPINE_UNNORMALIZED\",\n ODD_DIMENSIONS: \"VIDEO_ODD_DIMENSIONS\",\n REGION_DROPPED: \"VIDEO_REGION_DROPPED\",\n OVERLAY_OUT_OF_BOUNDS: \"VIDEO_OVERLAY_OUT_OF_BOUNDS\",\n} as const;\n\n// A clip span may sit a hair above a model's max after rounding; only warn on a real gap.\nconst SPAN_MODEL_SLACK_S = 0.25;\n\n// A voiceover turn may legitimately spill slightly past the next turn's start (tts\n// length is approximate), and the audio mix may run a touch past the video. Only\n// flag clear breakage beyond this slack.\nconst VIDEO_TIME_SLACK_S = 0.75;\n\n// Conversational speech pace used to estimate a native line's spoken length from its\n// word count: the scaffold's own planning rate (`NATIVE_CLIP_WORDS_PER_SECOND`). The\n// overrun ratio is deliberately LOOSER than the scaffold's 1.3 advisory: word count is\n// the only signal here (no real timings survive a script re-craft), and real fast UGC\n// speech runs 3.5–4 words/s, so flagging above 2.5×1.6 = 4 words/s catches only lines\n// that physically cannot fit their clip (the audit's 4.3 w/s render-killer) without\n// false-positives on energetic-but-renderable reads.\n\nconst SPEECH_OVERRUN_RATIO = 1.6;\n\n/** The duration an ffmpeg node's output carries: the LAST `-t` value in its args. */\nexport function ffmpegTrimSeconds(node: NodeDecl): number | null {\n const args = (node.params as Record<string, unknown> | undefined)?.args;\n if (!Array.isArray(args)) return null;\n let t: number | null = null;\n for (let i = 0; i < args.length - 1; i++) {\n if (args[i] === \"-t\") {\n const v = Number(args[i + 1]);\n if (Number.isFinite(v)) t = v;\n }\n }\n return t;\n}\n\n/**\n * A node's on-timeline video duration, recomputed from the LIVE graph: an ffmpeg\n * trim's `-t`, a `video_generate`'s `duration` param, or — for a composite/passthrough\n * ffmpeg with no `-t` — its first video input's duration (bounded recursion). Null\n * when the graph doesn't say (the caller skips the check rather than guessing).\n */\nfunction liveNodeDurationS(ctx: LintContext, node: NodeDecl | null, depth = 0): number | null {\n if (!node || depth > 3) return null;\n if (node.type === \"video_generate\") {\n const d = (node.params as Record<string, unknown> | undefined)?.duration;\n return typeof d === \"number\" ? d : null;\n }\n if (node.type === \"ffmpeg\") {\n const t = ffmpegTrimSeconds(node);\n if (t !== null) return t;\n for (const v of Object.values(node.inputs ?? {})) {\n const upstream = refNodeOf(ctx, v);\n const d = liveNodeDurationS(ctx, upstream, depth + 1);\n if (d !== null) return d;\n }\n return null;\n }\n return null;\n}\n\n/**\n * The seeded overlay windows in a composition's index.html, flagged when they run\n * past the video end. An out-of-bounds element simply never hides — the \"stuck last\n * overlay\" render bug — and re-timed scenes are exactly when it happens. Pure.\n */\nexport function overlayTimingIssues(html: string, durationS: number): Array<{ start: number; dur: number }> {\n const out: Array<{ start: number; dur: number }> = [];\n for (const m of html.matchAll(/<[^>]*\\bdata-start=\"([\\d.]+)\"[^>]*\\bdata-dur=\"([\\d.]+)\"[^>]*>/g)) {\n const start = Number(m[1]);\n const dur = Number(m[2]);\n if (!Number.isFinite(start) || !Number.isFinite(dur)) continue;\n if (start >= durationS || start + dur > durationS + 0.25) out.push({ start, dur });\n }\n return out;\n}\n\n/** Deep advisory: every hyperframe composition's seeded overlay windows fit the video. */\nasync function checkOverlayTimingBounds(canvas: Canvas, issues: ValidationIssue[]): Promise<void> {\n const durationS = canvas.metadata?.video?.duration_s;\n if (typeof durationS !== \"number\") return;\n for (let i = 0; i < canvas.nodes.length; i++) {\n const n = canvas.nodes[i];\n if (n?.type !== \"hyperframe_render\") continue;\n const composition = (n.params as Record<string, unknown> | undefined)?.composition;\n if (typeof composition !== \"string\" || !path.isAbsolute(composition)) continue;\n const html = await readFile(path.join(composition, \"index.html\"), \"utf8\").catch(() => null);\n if (!html) continue;\n for (const w of overlayTimingIssues(html, durationS)) {\n issues.push({\n path: `nodes[${i}].params.composition`,\n code: CODES.OVERLAY_OUT_OF_BOUNDS,\n severity: \"warning\",\n node_id: n.id,\n node_type: n.type,\n message: `an overlay in ${path.basename(composition)}/index.html runs [${w.start}s, ${w.start + w.dur}s] but the video ends at ${durationS}s — it never hides on screen. Re-time its data-start/data-dur to fit the video`,\n });\n }\n }\n}\n\n/**\n * A scene's generated clips: every `video_generate` tagged with the scene, or on an\n * untagged canvas the full-frame `s<scene>_clip` and region `s<scene>_r<n>_clip` ids.\n */\nfunction sceneClips(ctx: LintContext, scene: number): NodeDecl[] {\n if (isTagged(ctx)) return ctx.canvas.nodes.filter((n) => n.type === \"video_generate\" && n.tags?.scene === scene);\n const nativeClipRe = new RegExp(`^s${scene}(_r\\\\d+)?_clip$`);\n return ctx.canvas.nodes.filter((n) => nativeClipRe.test(n.id) && n.type === \"video_generate\");\n}\n\ntype TalkingSceneEntry =\n | number\n | { scene: number; voice_convert_node: string; est_speech_s?: number; speech_words?: number }\n | { scene: number; lipsync_node: string };\n\n/**\n * Does the scene's native clip audio (or its extraction) reach the audio mix\n * directly, without the STS convert node? This is the valid fallback config when\n * the one-brand-voice convert chain is intentionally bypassed: the native clip\n * already carries lip-matched speech, so routing it straight to the mix keeps the\n * lips correct (only brand-voice normalization is given up). Accepts the scene's\n * voice extract or clip audio (by tag; untagged canvases by the `s<scene>_voextract` /\n * `s<scene>_clip` / region-clip ids) wired into any `audio_timeline`.\n */\nfunction nativeAudioReachesMix(ctx: LintContext, scene: number): boolean {\n if (isTagged(ctx)) {\n return ctx.canvas.nodes.some(\n (n) =>\n n.type === \"audio_timeline\" &&\n Object.values(n.inputs ?? {}).some((v) => {\n const source = refNodeOf(ctx, v);\n return source?.tags?.scene === scene && (source.tags.role === \"voice_extract\" || source.tags.role === \"clip\");\n }),\n );\n }\n const wanted = [\n `$ref:s${scene}_voextract.audio`,\n `$ref:s${scene}_clip.audio`,\n new RegExp(`\\\\$ref:s${scene}_r\\\\d+_(voextract|clip)\\\\.audio`),\n ];\n return ctx.canvas.nodes.some((n) => {\n if (n.type !== \"audio_timeline\") return false;\n const blob = JSON.stringify(n.inputs ?? {});\n return wanted.some((w) => (typeof w === \"string\" ? blob.includes(w) : w.test(blob)));\n });\n}\n\n/**\n * Is a single-on-camera-speaker scene correctly voiced? Native scaffold output\n * (`{ scene, voice_convert_node }`) requires the clip to carry native audio\n * (`generate_audio: true`) — the invariant that keeps lips matched — AND that the\n * native audio reaches the mix, EITHER through the `audio_voice_convert` node\n * (one-brand-voice, the default) OR routed directly (native-audio fallback).\n * Legacy veed forms (`{ scene, lipsync_node }` / bare index) require a\n * `video_lipsync` node.\n */\nfunction talkingSceneSatisfied(ctx: LintContext, entry: TalkingSceneEntry, scene: number): boolean {\n const nodes = ctx.canvas.nodes;\n if (typeof entry === \"object\" && \"voice_convert_node\" in entry) {\n // The scene's native clip is its full-frame clip, or the PRESENTER region's clip\n // for a composited scene: either one carrying the generated speech is what the\n // voice-convert node re-voices.\n const clipNativeAudio = sceneClips(ctx, scene).some((n) => {\n const params = n.params as Record<string, unknown> | undefined;\n // A model with no audio toggle renders speech unconditionally, so an ABSENT\n // `generate_audio` is a satisfied requirement there, not a missing one —\n // demanding `=== true` would fail every talking scene on the default model,\n // which cannot express the flag at all.\n if (typeof params?.model === \"string\" && !supportsParam(\"video_generate\", params.model, \"generate_audio\")) {\n return true;\n }\n return params?.generate_audio === true;\n });\n const converted = nodes.some((n) => n.id === entry.voice_convert_node && n.type === \"audio_voice_convert\");\n return clipNativeAudio && (converted || nativeAudioReachesMix(ctx, scene));\n }\n if (typeof entry === \"object\") {\n return nodes.some((n) => n.id === entry.lipsync_node && n.type === \"video_lipsync\");\n }\n return nodes.some((n) => {\n if (n.type !== \"video_lipsync\") return false;\n const video = (n.inputs as Record<string, unknown>)?.video;\n return video === `$ref:s${scene}_trim.video` || video === `$ref:s${scene}_clip.video`;\n });\n}\n\n// Deep enough to size a typed spine's clip through a passthrough composite to its\n// generated clip, the same reach the legacy spine sizing has.\nconst TIMING_MAX_DEPTH = 4;\n\n/**\n * A node's static timing, when its definition declares one: the node's own params\n * plus the timing of everything wired into it, computed without running anything.\n * Upstream nodes without a hook are sized the legacy way (a `video_generate`'s\n * duration, an ffmpeg `-t`). Null when the node has no hook or its params don't parse.\n */\nfunction typedTiming(ctx: LintContext, node: NodeDecl, depth = 0): StaticTiming | null {\n const def = ctx.registry.get(node.type);\n if (!def?.staticTiming || depth > TIMING_MAX_DEPTH) return null;\n const parsed = def.params.safeParse(node.params ?? {});\n if (!parsed.success) return null;\n const inputs: Record<string, StaticTiming> = {};\n for (const [slot, value] of Object.entries(node.inputs ?? {})) {\n const upstream = refNodeOf(ctx, value);\n if (!upstream) continue;\n const timing = typedTiming(ctx, upstream, depth + 1) ?? legacyTiming(ctx, upstream);\n if (timing) inputs[slot] = timing;\n }\n return def.staticTiming({ params: parsed.data, inputs });\n}\n\nfunction legacyTiming(ctx: LintContext, node: NodeDecl): StaticTiming | null {\n const d = liveNodeDurationS(ctx, node);\n return d === null ? null : { duration_s: d };\n}\n\n/** The spine: the stamped node, else the node tagged as the spine, else the legacy `spine` id. */\nfunction spineIdOf(ctx: LintContext, meta: VideoMetaT): string | null {\n if (meta.timeline?.spine_node) return meta.timeline.spine_node;\n const tagged = ctx.canvas.nodes.find((n) => n.tags?.role === \"spine\");\n if (tagged) return tagged.id;\n return ctx.idToIndex.has(\"spine\") ? \"spine\" : null;\n}\n\n/** The spine's LIVE total: Σ input clip durations, minus any xfade overlap it eats. */\nfunction spineTotalS(ctx: LintContext, spine: NodeDecl): number | null {\n let total = 0;\n const inputs = Object.entries(spine.inputs ?? {}).filter(([k]) => /^c\\d+$/.test(k));\n if (inputs.length === 0) return null;\n // The spine's own per-input chain is the authority when it absorbs a length (a\n // trim= cut or a looped still's input-side -t — spine-input.ts); only an input\n // with no absorbed length falls back to sizing the upstream node.\n const rawArgs = (spine.params as Record<string, unknown>).args;\n const absorbed = Array.isArray(rawArgs) ? parseSpineInputLens(rawArgs.map(String)) : new Map<number, number>();\n for (const [k, v] of inputs) {\n const inputIdx = Number(k.slice(1));\n const d = absorbed.get(inputIdx) ?? liveNodeDurationS(ctx, refNodeOf(ctx, v));\n if (d === null) return null; // an input the graph can't size — skip, don't guess\n total += d;\n }\n const graph = ((spine.params as Record<string, unknown>).args as unknown[])?.join?.(\" \") ?? \"\";\n for (const m of String(graph).matchAll(/xfade=transition=[^:]+:duration=([\\d.]+)/g)) {\n total -= Number(m[1]);\n }\n return total;\n}\n\n/**\n * The one-clock length gate: the spine's live total, every per-speaker voice track,\n * and the final mix must all equal the stamped `expected_total_s` — a mismatch means\n * `-shortest` truncates real content (a cut CTA) with no error at render time.\n */\nfunction checkTimelineContract(ctx: LintContext, meta: VideoMetaT): void {\n const stamp = meta.timeline;\n const spineId = spineIdOf(ctx, meta);\n if (!spineId) return;\n const expected = stamp?.expected_total_s ?? meta.duration_s;\n checkSpineTotal(ctx, spineId, expected);\n checkAudioTimelineTotals(ctx, stamp?.audio_mix_node, expected);\n}\n\n/** The spine's live summed length must equal the stamped total. */\nfunction checkSpineTotal(ctx: LintContext, spineId: string, expected: number): void {\n const spineIdx = ctx.idToIndex.get(spineId);\n const spine = spineIdx === undefined ? undefined : ctx.canvas.nodes[spineIdx];\n if (!spine || spineIdx === undefined) return;\n if (ctx.registry.get(spine.type)?.staticTiming) {\n checkTypedSpineTotal(ctx, spine, spineIdx, expected);\n return;\n }\n const spineLen = spineTotalS(ctx, spine);\n if (spineLen === null || Math.abs(spineLen - expected) <= VIDEO_TIME_SLACK_S) return;\n ctx.issues.push({\n path: `nodes[${spineIdx}].params.args`,\n code: CODES.TIMELINE_TOTAL,\n node_id: spineId,\n message: `the picture sums to ${round2(spineLen)}s but the timeline is pinned to ${expected}s — \\`-shortest\\` at the final mux will silently cut the longer stream. Re-time the seg trims (or the audio total_ms) so both match`,\n });\n}\n\n/** A typed spine's length comes from its own params (and its sized inputs), not argv. */\nfunction checkTypedSpineTotal(ctx: LintContext, spine: NodeDecl, spineIdx: number, expected: number): void {\n const spineLen = typedTiming(ctx, spine)?.duration_s;\n if (spineLen === undefined || Math.abs(spineLen - expected) <= VIDEO_TIME_SLACK_S) return;\n ctx.issues.push({\n path: `nodes[${spineIdx}].params.clips`,\n code: CODES.TIMELINE_TOTAL,\n node_id: spine.id,\n message: `the picture sums to ${round2(spineLen)}s but the timeline is pinned to ${expected}s. The final mux ends at the shorter stream and silently cuts the longer one. Re-time the clips' duration_s (or the audio total_ms) so both match`,\n });\n}\n\n/**\n * Full-length audio timelines must equal the stamped total: the mix, plus every\n * per-speaker voice track (their absolute placement collapses if the track isn't\n * timeline-length).\n */\nfunction checkAudioTimelineTotals(ctx: LintContext, audioMixNode: string | undefined, expected: number): void {\n const mustBeFullLength = new Set<string>();\n if (audioMixNode) mustBeFullLength.add(audioMixNode);\n ctx.canvas.nodes.forEach((n) => {\n if (n.type !== \"audio_voice_convert\") return;\n const track = refNodeOf(ctx, n.inputs?.audio);\n if (track?.type === \"audio_timeline\") mustBeFullLength.add(track.id);\n });\n for (const id of mustBeFullLength) {\n const idx = ctx.idToIndex.get(id);\n const node = idx === undefined ? null : (ctx.canvas.nodes[idx] as NodeDecl);\n const totalMs = (node?.params as Record<string, unknown> | undefined)?.total_ms;\n if (typeof totalMs !== \"number\") continue;\n if (Math.abs(totalMs / 1000 - expected) > VIDEO_TIME_SLACK_S) {\n ctx.issues.push({\n path: `nodes[${idx}].params.total_ms`,\n code: CODES.TIMELINE_TOTAL,\n node_id: id,\n message: `audio timeline \"${id}\" is pinned to ${totalMs}ms but the picture timeline is ${expected}s — \\`-shortest\\` at the final mux will silently cut the longer stream. Pin total_ms to ${Math.round(expected * 1000)}`,\n });\n }\n }\n}\n\n/**\n * No two windows on a per-speaker voice track may overlap: both extracts PLAY — the\n * voice doubles into an echo. Windows come from the LIVE graph: each track entry's\n * `duration_s` cap, else its extract node's `-t`. (The frozen vo_segments check has a\n * 0.75s slack for tts estimates; native extract windows are exact, so 10ms is real.)\n */\nfunction checkNativeSegOverlap(ctx: LintContext): void {\n for (const conv of ctx.canvas.nodes) {\n if (conv.type !== \"audio_voice_convert\") continue;\n const track = refNodeOf(ctx, conv.inputs?.audio);\n if (track?.type !== \"audio_timeline\") continue;\n const params = track.params as { tracks?: Array<{ slot: string; start_s: number; duration_s?: number }> };\n const windows = (params.tracks ?? [])\n .map((t) => {\n const extract = refNodeOf(ctx, track.inputs?.[t.slot]);\n const len = t.duration_s ?? (extract ? ffmpegTrimSeconds(extract) : null);\n return len === null ? null : { slot: t.slot, start: t.start_s, end: t.start_s + len };\n })\n .filter((w): w is { slot: string; start: number; end: number } => w !== null)\n .sort((a, b) => a.start - b.start);\n for (let i = 1; i < windows.length; i++) {\n const prev = windows[i - 1];\n const cur = windows[i];\n if (!prev || !cur || cur.start >= prev.end - 0.01) continue;\n const trackIdx = ctx.idToIndex.get(track.id);\n ctx.issues.push({\n path: `nodes[${trackIdx}].params.tracks`,\n code: CODES.NATIVE_SEG_OVERLAP,\n node_id: track.id,\n message: `voice track \"${track.id}\": \"${prev.slot}\" runs to ${round2(prev.end)}s but \"${cur.slot}\" starts at ${cur.start}s — both play at once (echo). Cap the first with duration_s: ${round2(cur.start - prev.start)} or re-time the windows`,\n });\n }\n }\n}\n\n/**\n * Warn when a legacy ffmpeg concat spine feeds bare `[i:v]` labels (inputs with\n * unproven fps/SAR). A `video_concat` spine normalizes every input by construction.\n */\nfunction checkSpineNormalized(ctx: LintContext, meta: VideoMetaT): void {\n const spineId = spineIdOf(ctx, meta);\n const idx = spineId === null ? undefined : ctx.idToIndex.get(spineId);\n const spine = idx === undefined ? undefined : ctx.canvas.nodes[idx];\n if (!spine || spine.type !== \"ffmpeg\") return;\n const args = (spine.params as Record<string, unknown>).args;\n const graph = Array.isArray(args) ? args.join(\" \") : \"\";\n if (!graph.includes(\"concat=n=\")) return;\n if (/\\[\\d+:v\\](?:\\[|concat)/.test(graph)) {\n ctx.issues.push({\n path: `nodes[${idx}].params.args`,\n code: CODES.SPINE_UNNORMALIZED,\n severity: \"warning\",\n node_id: spine.id,\n message: `the spine concat feeds raw input labels — generated clips carry no fps/SAR guarantee, and one 24fps clip silently stretches the picture off the audio/overlay clock. Chain \\`format=yuv420p,fps=30,setsar=1,settb=AVTB\\` on every input before the concat`,\n });\n }\n}\n\n/** Warn on odd scale/crop/pad dimensions in any ffmpeg node — libx264 yuv420p hard-fails. */\nfunction checkOddDimensions(ctx: LintContext): void {\n ctx.canvas.nodes.forEach((node, idx) => {\n if (node.type !== \"ffmpeg\") return;\n const args = (node.params as Record<string, unknown>).args;\n const blob = Array.isArray(args) ? args.join(\" \") : \"\";\n for (const m of blob.matchAll(/(?:scale|crop|pad)=(\\d+):(\\d+)/g)) {\n const w = Number(m[1]);\n const h = Number(m[2]);\n if (w % 2 === 0 && h % 2 === 0) continue;\n ctx.issues.push({\n path: `nodes[${idx}].params.args`,\n code: CODES.ODD_DIMENSIONS,\n severity: \"warning\",\n node_id: node.id,\n message: `\"${m[0]}\" produces an odd dimension — libx264 yuv420p rejects odd sizes, so this node fails at render time. Round both to even numbers`,\n });\n return; // one warning per node is enough\n }\n });\n}\n\n/** Warn when a scene generated more region clips than its composite consumes — billed, then dropped. */\nfunction checkRegionDropped(ctx: LintContext): void {\n if (isTagged(ctx)) {\n checkTaggedRegionDropped(ctx);\n return;\n }\n const regionClips = new Map<string, number>();\n for (const node of ctx.canvas.nodes) {\n const m = /^s(\\d+)_r\\d+_clip$/.exec(node.id);\n if (m && node.type === \"video_generate\")\n regionClips.set(m[1] as string, (regionClips.get(m[1] as string) ?? 0) + 1);\n }\n for (const [scene, billed] of regionClips) {\n const idx = ctx.idToIndex.get(`s${scene}_composite`);\n if (idx === undefined) continue;\n const composite = ctx.canvas.nodes[idx] as NodeDecl;\n const consumed = Object.keys(composite.inputs ?? {}).filter((k) => /^c\\d+$/.test(k)).length;\n if (billed > consumed) {\n ctx.issues.push({\n path: `nodes[${idx}].inputs`,\n code: CODES.REGION_DROPPED,\n severity: \"warning\",\n node_id: composite.id,\n message: `scene s${scene} bills ${billed} region clips but its composite consumes ${consumed} — the extra generation is paid for and never reaches the frame. Wire every region into the composite or delete the unused clip node`,\n });\n }\n }\n}\n\n/**\n * The tagged form: a scene's region clips are its `video_generate` nodes tagged with a\n * region; the composite consumes the ones it reaches through its inputs (directly, or\n * through a cut-out or trim in between).\n */\nfunction checkTaggedRegionDropped(ctx: LintContext): void {\n for (const composite of ctx.canvas.nodes) {\n const scene = composite.tags?.scene;\n if (composite.tags?.role !== \"composite\" || scene === undefined) continue;\n const billed = ctx.canvas.nodes.filter(\n (n) => n.type === \"video_generate\" && n.tags?.scene === scene && n.tags.region !== undefined,\n );\n const reached = upstreamIds(ctx, composite);\n const consumed = billed.filter((n) => reached.has(n.id)).length;\n if (billed.length <= consumed) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(composite.id) ?? -1}].inputs`,\n code: CODES.REGION_DROPPED,\n severity: \"warning\",\n node_id: composite.id,\n message: `scene s${scene} bills ${billed.length} region clips but its composite consumes ${consumed} — the extra generation is paid for and never reaches the frame. Wire every region into the composite or delete the unused clip node`,\n });\n }\n}\n\n/** The nodes a node's inputs reference directly (a list input counts each element). */\nfunction wiredNodes(ctx: LintContext, node: NodeDecl): NodeDecl[] {\n return Object.values(node.inputs ?? {})\n .flatMap((v) => (Array.isArray(v) ? v : [v]))\n .map((ref) => refNodeOf(ctx, ref))\n .filter((up): up is NodeDecl => up !== null);\n}\n\n/** Every node id a node reads from, transitively. */\nfunction upstreamIds(ctx: LintContext, node: NodeDecl): Set<string> {\n const seen = new Set<string>();\n const stack = [node];\n for (let cur = stack.pop(); cur; cur = stack.pop()) {\n for (const up of wiredNodes(ctx, cur)) {\n if (seen.has(up.id)) continue;\n seen.add(up.id);\n stack.push(up);\n }\n }\n return seen;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\n/**\n * Advisory (§2): a scene's natural visual span must fit the assigned model's max clip\n * length. Switching a subset of clips to a shorter-max model (Seedance's 15s → Veo's\n * 8s) leaves any >8s scene unable to render its full content — it truncates. Caught\n * from the scaffold's recorded `clip_spans` BEFORE a billed run, not after.\n */\nfunction checkClipSpanFitsModel(ctx: LintContext, meta: VideoMetaT): void {\n for (const { node, span_s } of meta.clip_spans ?? []) {\n const target = ctx.canvas.nodes.find((n) => n.id === node && n.type === \"video_generate\");\n if (!target) continue;\n const model = (target.params as Record<string, unknown> | undefined)?.model;\n if (typeof model !== \"string\") continue;\n const max = maxClipSeconds(model);\n if (max === undefined || span_s <= max + SPAN_MODEL_SLACK_S) continue;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(node) ?? -1}].params.duration`,\n code: CODES.SPAN_EXCEEDS_MODEL,\n severity: \"warning\",\n message: `scene for clip \"${node}\" needs ~${span_s}s but ${model} caps clips at ${max}s — the clip will truncate. Split the scene, or keep it on a model whose max duration covers the span`,\n node_id: node,\n node_type: \"video_generate\",\n });\n }\n}\n\n/**\n * Seconds per word to budget a native line at. Prefers the speaker's OBSERVED pace —\n * the scaffold stamps `est_speech_s` (measured from the deconstruct's word timings)\n * with `speech_words` (the word count it was measured for) — so a fast talker's line\n * isn't failed by the generic wps guess, and a re-authored line still scales at the\n * real rate. Falls back to the guess when no plausible stamp exists.\n */\nfunction secondsPerWord(stamped?: { est_speech_s?: number; speech_words?: number }): number {\n const est = stamped?.est_speech_s;\n const words = stamped?.speech_words;\n if (est && words && est > 0 && words > 0) {\n const wps = words / est;\n if (wps >= OBSERVED_WPS_MIN && wps <= OBSERVED_WPS_MAX) return est / words;\n }\n return 1 / NATIVE_CLIP_WORDS_PER_SECOND;\n}\n\n/** The overrun of one candidate native clip node, or null when it fits / isn't one. */\nfunction speechOverrunOf(\n node: Canvas[\"nodes\"][number],\n secPerWord: number,\n): { estSpeechS: number; duration: number } | null {\n const params = node.params as Record<string, unknown> | undefined;\n if (params?.generate_audio !== true) return null;\n const line = nativeDialogueOf(params.prompt);\n const duration = typeof params.duration === \"number\" ? params.duration : undefined;\n if (!line || !duration) return null;\n const estSpeechS = wordCount(line) * secPerWord;\n return estSpeechS > duration * SPEECH_OVERRUN_RATIO ? { estSpeechS, duration } : null;\n}\n\n/** Invariant 4: est. speech from each native clip's prompt vs its generated duration. */\nfunction checkSpeechOverrun(ctx: LintContext, talkingScenes: TalkingSceneEntry[]): void {\n for (const entry of talkingScenes) {\n if (typeof entry !== \"object\" || !(\"voice_convert_node\" in entry)) continue;\n for (const n of sceneClips(ctx, entry.scene)) {\n const overrun = speechOverrunOf(n, secondsPerWord(entry));\n if (overrun) {\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(n.id) ?? -1}].params.prompt`,\n code: CODES.SPEECH_OVERRUN,\n message: `\"${n.id}\" asks Seedance to speak ~${Math.round(overrun.estSpeechS * 10) / 10}s of dialogue inside a ${overrun.duration}s clip — the line cannot fit (>${SPEECH_OVERRUN_RATIO}× the clip). Shorten the line, split the scene, or lengthen the clip duration`,\n });\n continue;\n }\n checkSpeechExceedsExtract(ctx, n, entry);\n }\n }\n}\n\n/**\n * The voice extract cut from a scene's native clip, and the name to report it by: on a\n * tagged canvas the scene's `voice_extract` (the clip's own region first); on an\n * untagged one the `s<scene>[_r<n>]_voextract` id, reported as `s<scene>_voextract`.\n */\nfunction sceneExtract(ctx: LintContext, clip: NodeDecl, scene: number): { extract: NodeDecl; label: string } | null {\n if (isTagged(ctx)) {\n const extracts = ctx.canvas.nodes.filter((n) => n.tags?.role === \"voice_extract\" && n.tags.scene === scene);\n const extract = extracts.find((n) => n.tags?.region === clip.tags?.region) ?? extracts[0];\n return extract ? { extract, label: extract.id } : null;\n }\n const regionTag = /^s\\d+(_r\\d+)?_clip$/.exec(clip.id)?.[1] ?? \"\";\n const extractIdx = ctx.idToIndex.get(`s${scene}${regionTag}_voextract`) ?? ctx.idToIndex.get(`s${scene}_voextract`);\n const extract = extractIdx === undefined ? undefined : ctx.canvas.nodes[extractIdx];\n return extract ? { extract, label: `s${scene}_voextract` } : null;\n}\n\n/**\n * The REAL audio budget for a re-authored native line is the extract window (the\n * `s<i>_voextract` `-t`), not the padded ≥4s generated clip: the picture is trimmed\n * back to the scene window on the spine, so a 3s line in a 1s window passes the clip\n * check and then gets cut mid-word at render. Compared against the extract — not the\n * seg — because a voiceover deliberately carries over b-roll cutaways (the extract\n * window IS that full spoken span). Blocking: the cut is certain, not stylistic.\n */\nfunction checkSpeechExceedsExtract(\n ctx: LintContext,\n clip: NodeDecl,\n entry: { scene: number; est_speech_s?: number; speech_words?: number },\n): void {\n const params = clip.params as Record<string, unknown> | undefined;\n if (params?.generate_audio !== true) return;\n const line = nativeDialogueOf(params.prompt);\n if (!line) return;\n const found = sceneExtract(ctx, clip, entry.scene);\n const windowS = found ? ffmpegTrimSeconds(found.extract) : null;\n if (!found || windowS === null) return;\n const estSpeechS = wordCount(line) * secondsPerWord(entry);\n if (estSpeechS <= windowS * SPEECH_OVERRUN_RATIO) return;\n ctx.issues.push({\n path: `nodes[${ctx.idToIndex.get(clip.id) ?? -1}].params.prompt`,\n code: CODES.SPEECH_EXCEEDS_EXTRACT,\n node_id: clip.id,\n message: `\"${clip.id}\"'s line is ~${Math.round(estSpeechS * 10) / 10}s of speech but its extract window (${found.label}) is ${windowS}s — the read gets cut mid-word on the spine. Shorten the Dialogue line, lengthen the scene in prompt.json and re-scaffold, or raise the voextract \\`-t\\` when the read should carry over the next cutaway`,\n });\n}\n\n/** Invariant 5: all video_generate nodes agree on one aspect_ratio (none silently defaulted). */\nfunction checkAspectConsistency(ctx: LintContext): void {\n const clips = ctx.canvas.nodes.filter((n) => n.type === \"video_generate\");\n if (clips.length < 2) return;\n const ratios = new Set(clips.map((n) => String((n.params as Record<string, unknown>)?.aspect_ratio ?? \"(unset)\")));\n if (ratios.size <= 1) return;\n ctx.issues.push({\n path: \"nodes\",\n code: CODES.ASPECT_MISMATCH,\n message: `video_generate nodes disagree on aspect_ratio (${[...ratios].sort().join(\", \")}) — the spine's segments would render at different shapes and the composite silently crops. Pin every clip to the same aspect_ratio`,\n });\n}\n\n/** The voiceover turns in start order. */\nfunction sortedVoSegments(meta: VideoMetaT): VideoMetaT[\"vo_segments\"] {\n return [...meta.vo_segments].sort((a, b) => a.start_s - b.start_s);\n}\n\n/** Invariant 1: no two voiceover turns overlap in time (the parallel-monologue symptom). */\nfunction checkVoOverlap(ctx: LintContext, meta: VideoMetaT): void {\n const segments = sortedVoSegments(meta);\n for (let i = 1; i < segments.length; i++) {\n const prev = segments[i - 1];\n const cur = segments[i];\n if (!prev || !cur) continue;\n if (cur.start_s < prev.end_s - VIDEO_TIME_SLACK_S) {\n ctx.issues.push({\n path: \"metadata.video.vo_segments\",\n code: CODES.VO_OVERLAP,\n message: `voiceover turns overlap: \"${prev.slot}\" runs to ${prev.end_s}s but \"${cur.slot}\" starts at ${cur.start_s}s — sequence them so dialogue alternates instead of stacking`,\n });\n }\n }\n}\n\n/** Invariant 2: the audio timeline should not run materially longer than the video. */\nfunction checkAudioDuration(ctx: LintContext, meta: VideoMetaT): void {\n const audioEnd = sortedVoSegments(meta).reduce((m, s) => Math.max(m, s.end_s), 0);\n if (audioEnd > meta.duration_s + VIDEO_TIME_SLACK_S) {\n ctx.issues.push({\n path: \"metadata.video.duration_s\",\n code: CODES.AUDIO_DURATION,\n message: `audio runs to ${audioEnd}s but the video is ${meta.duration_s}s — trim the voiceover or extend the video so audio ≈ video length`,\n });\n }\n}\n\n/**\n * Invariant 3: every single-on-camera-speaker scene must be a NATIVELY-voiced talking\n * head: the Seedance clip generates the speech+lips (`generate_audio: true` + the line\n * in its prompt) and an `audio_voice_convert` node re-voices it to the brand voice. We\n * confirm both — the clip carries native audio AND the convert node is wired — so the\n * lips can't drift (they were generated with the audio). Entry forms:\n * { scene, voice_convert_node } — native (current scaffold)\n * { scene, lipsync_node } / bare index — legacy veed canvases (still accepted)\n */\nfunction checkTalkingScenes(ctx: LintContext, meta: VideoMetaT): void {\n for (const entry of meta.talking_scenes) {\n const scene = typeof entry === \"number\" ? entry : entry.scene;\n if (!talkingSceneSatisfied(ctx, entry, scene)) {\n ctx.issues.push({\n path: \"metadata.video.talking_scenes\",\n code: CODES.LIPSYNC_MISSING,\n message: `scene ${scene} is a single-on-camera-speaker talking head but its clip lacks native audio (generate_audio: true) — the lips would drift. Generate the clip with native audio, then route it to the mix via either the audio_voice_convert node (one brand voice) or directly (s${scene}_voextract → audio_mix)`,\n });\n }\n }\n}\n\nexport const voOverlapRule: LintRule = {\n name: \"video-vo-overlap\",\n codes: { [CODES.VO_OVERLAP]: \"error\" },\n check: onVideoCanvas(checkVoOverlap),\n};\n\nexport const audioDurationRule: LintRule = {\n name: \"video-audio-duration\",\n codes: { [CODES.AUDIO_DURATION]: \"error\" },\n check: onVideoCanvas(checkAudioDuration),\n};\n\nexport const talkingScenesRule: LintRule = {\n name: \"video-talking-scenes\",\n codes: { [CODES.LIPSYNC_MISSING]: \"error\" },\n check: onVideoCanvas(checkTalkingScenes),\n};\n\n/**\n * Invariant 4: a native line must physically FIT its generated clip. The scaffold's\n * overstuff advisories ran against the ORIGINAL blueprint; the operator then re-crafts\n * the script inside the canvas, so re-derive the spoken length from each native clip's\n * CURRENT prompt and fail when it clearly overruns the clip duration (the render either\n * fails or crams — either way lips and timeline break).\n */\nexport const speechOverrunRule: LintRule = {\n name: \"video-speech-overrun\",\n codes: { [CODES.SPEECH_OVERRUN]: \"error\", [CODES.SPEECH_EXCEEDS_EXTRACT]: \"error\" },\n check: onVideoCanvas((ctx, meta) => checkSpeechOverrun(ctx, meta.talking_scenes)),\n};\n\n/**\n * Invariant 5: every clip must land on ONE canvas geometry. Mixed (or\n * partially-defaulted) aspect_ratio params across video_generate nodes ship a spine\n * whose segments disagree in shape — the composite silently crops/letterboxes.\n */\nexport const aspectConsistencyRule: LintRule = {\n name: \"video-aspect-consistency\",\n codes: { [CODES.ASPECT_MISMATCH]: \"error\" },\n check: onVideoCanvas((ctx) => checkAspectConsistency(ctx)),\n};\n\n/**\n * A scene's natural span must fit the assigned model's max clip length (a 9.2s scene\n * on Veo, which caps at 8s, would truncate). Caught before a run.\n */\nexport const clipSpanRule: LintRule = {\n name: \"video-clip-span\",\n codes: { [CODES.SPAN_EXCEEDS_MODEL]: \"warning\" },\n check: onVideoCanvas(checkClipSpanFitsModel),\n};\n\n// The one-clock contract, recomputed from the LIVE graph — the frozen metadata plan\n// catches scaffold-time breakage, but operators re-craft the canvas by hand and\n// `-shortest` at the final mux silently truncates whichever stream runs longer, so the\n// REAL node params are what must agree.\n\nexport const timelineContractRule: LintRule = {\n name: \"video-timeline-contract\",\n codes: { [CODES.TIMELINE_TOTAL]: \"error\" },\n check: onVideoCanvas(checkTimelineContract),\n};\n\nexport const nativeSegOverlapRule: LintRule = {\n name: \"video-native-seg-overlap\",\n codes: { [CODES.NATIVE_SEG_OVERLAP]: \"error\" },\n check: onVideoCanvas((ctx) => checkNativeSegOverlap(ctx)),\n};\n\nexport const spineNormalizedRule: LintRule = {\n name: \"video-spine-normalized\",\n codes: { [CODES.SPINE_UNNORMALIZED]: \"warning\" },\n check: onVideoCanvas(checkSpineNormalized),\n};\n\nexport const oddDimensionsRule: LintRule = {\n name: \"video-odd-dimensions\",\n codes: { [CODES.ODD_DIMENSIONS]: \"warning\" },\n check: onVideoCanvas((ctx) => checkOddDimensions(ctx)),\n};\n\nexport const regionDroppedRule: LintRule = {\n name: \"video-region-dropped\",\n codes: { [CODES.REGION_DROPPED]: \"warning\" },\n check: onVideoCanvas((ctx) => checkRegionDropped(ctx)),\n};\n\nexport const overlayTimingRule: DeepLintRule = {\n name: \"video-overlay-timing\",\n codes: { [CODES.OVERLAY_OUT_OF_BOUNDS]: \"warning\" },\n check: checkOverlayTimingBounds,\n};\n","/**\n * How long a line takes to say. Two rates, on purpose — they answer different questions:\n *\n * - `NATIVE_CLIP_WORDS_PER_SECOND` (2.5, ~150 wpm) is a PLANNING rate for speech the video\n * model voices itself. It errs slow: a clip sized at it is long enough that the model\n * never has to cram the line, and a scene whose lines overrun it is flagged as\n * over-stuffed, by the scaffold and by the validator's speech-fit gates alike. A\n * reproduced ad's actual timing comes from the deconstruct's word stamps\n * (`estSpeechWindowS`); this is only the fallback.\n * - `READ_WORDS_PER_SECOND` (3.06) is a MEASURED rate for an ElevenLabs read, off a real\n * Spanish ad (81 words in 26.48s; the previous 2.6 over-predicted every beat by 18%). An\n * authored ad (`scaffold-ad`) lays its beat clock on it, so the picture tracks the read.\n *\n * One rate for both would either cram native lines into clips too short to say them, or\n * pad every authored beat with dead air.\n */\nexport const NATIVE_CLIP_WORDS_PER_SECOND = 2.5;\nexport const READ_WORDS_PER_SECOND = 3.06;\n\nexport function wordCount(text: string): number {\n return text.trim().split(/\\s+/).filter(Boolean).length;\n}\n\n/** A model-voiced line's planning length (see `NATIVE_CLIP_WORDS_PER_SECOND`). */\nexport function estSpeechS(text: string): number {\n return wordCount(text) / NATIVE_CLIP_WORDS_PER_SECOND;\n}\n\n// Sanity band for an OBSERVED speech rate (deconstruct word timings, or a stamp the\n// validator reads back): real speech sits well inside 1–6 words/s. Inside it the observed\n// window is the truth (a fast talker's long line genuinely fits its clip — the 2.5wps\n// guess fatally flagged exactly that); outside it the timing is junk (a line\n// over-attributed to a scene) and the guess wins.\nexport const OBSERVED_WPS_MIN = 1;\nexport const OBSERVED_WPS_MAX = 6;\n\n/** A line's length: its observed window when the stamps are plausible, else the planning estimate. */\nexport function estSpeechWindowS(text: string, startS: number | undefined, endS: number | undefined): number {\n const words = wordCount(text);\n const window = (endS ?? 0) - (startS ?? 0);\n if (words > 0 && window > 0.3) {\n const wps = words / window;\n if (wps >= OBSERVED_WPS_MIN && wps <= OBSERVED_WPS_MAX) return window;\n }\n return estSpeechS(text);\n}\n","/**\n * Reading a LEGACY spine: the raw `ffmpeg` concat node canvases carried before the\n * spine became a typed `video_concat`. Those canvases are still on disk in client\n * workspaces and must keep validating, so the validator re-derives each input's\n * on-timeline length from the argv they froze. A `video_concat` spine states its\n * lengths in its params and never comes through here.\n *\n * The legacy spine absorbed per-scene length work into each input's chain:\n * - a TRIM: a generated clip or a phrase-clip window cut inside the spine's filter\n * chain (`trim=[start=<s>:]duration=<len>`);\n * - a STILL: a held keyframe / real screenshot looped via input flags\n * (`-loop 1 -t <len>` before its `-i`).\n */\n\n/**\n * Re-derive each legacy spine input's on-timeline length from the spine node's own\n * args. Returns a per-input-index map; an input with no absorbed length (a clip\n * generated at its exact scene length) is absent, and the caller falls back to\n * sizing the upstream node.\n */\nexport function parseSpineInputLens(args: readonly string[]): Map<number, number> {\n const lens = new Map<number, number>();\n // Input-side `-t` (stills): the flags immediately precede that input's `-i`.\n let inputIndex = 0;\n for (let i = 0; i < args.length; i++) {\n if (args[i] !== \"-i\") continue;\n if (args[i - 2] === \"-t\") {\n const v = Number(args[i - 1]);\n if (Number.isFinite(v)) lens.set(inputIndex, v);\n }\n inputIndex++;\n }\n // Filter-side `trim=` on a `[N:v]…` chain (clips cut inside the spine).\n const graph = args.join(\" \");\n for (const m of graph.matchAll(/\\[(\\d+):v\\][^;[]*?trim=(?:start=[\\d.]+:)?duration=([\\d.]+)/g)) {\n const idx = Number(m[1]);\n const v = Number(m[2]);\n if (Number.isFinite(idx) && Number.isFinite(v)) lens.set(idx, v);\n }\n return lens;\n}\n","import type { Canvas } from \"../../schema/canvas.ts\";\nimport type { ValidationIssue } from \"../errors.ts\";\nimport type { DeepLintRule, LintContext, LintRule } from \"./context.ts\";\nimport { audioBedMixRule } from \"./rules/audio.ts\";\nimport { imageProfileRule, parallelFormatRendersRule } from \"./rules/image.ts\";\nimport {\n brandmarkInPromptRule,\n hookLayersRule,\n personGenerationRule,\n promptBudgetRule,\n promptDecisionsRule,\n promptProfileRule,\n rawFaceKeyframeRule,\n referenceCompletenessRule,\n uiInPromptRule,\n unusableLastFrameRule,\n} from \"./rules/video-prompt.ts\";\nimport {\n aspectConsistencyRule,\n audioDurationRule,\n clipSpanRule,\n nativeSegOverlapRule,\n oddDimensionsRule,\n overlayTimingRule,\n regionDroppedRule,\n speechOverrunRule,\n spineNormalizedRule,\n talkingScenesRule,\n timelineContractRule,\n voOverlapRule,\n} from \"./rules/video-timeline.ts\";\n\n/**\n * Every domain rule the validator runs after its structural checks, in the order\n * their issues are reported. Adding a rule is adding an entry here.\n */\nconst LINT_RULES: readonly LintRule[] = [\n parallelFormatRendersRule,\n audioBedMixRule,\n promptProfileRule,\n imageProfileRule,\n personGenerationRule,\n promptBudgetRule,\n rawFaceKeyframeRule,\n promptDecisionsRule,\n hookLayersRule,\n voOverlapRule,\n audioDurationRule,\n talkingScenesRule,\n speechOverrunRule,\n aspectConsistencyRule,\n uiInPromptRule,\n brandmarkInPromptRule,\n referenceCompletenessRule,\n clipSpanRule,\n timelineContractRule,\n unusableLastFrameRule,\n nativeSegOverlapRule,\n spineNormalizedRule,\n oddDimensionsRule,\n regionDroppedRule,\n];\n\n/** Rules that read files the canvas points at, run by the deep pass only. */\nconst DEEP_LINT_RULES: readonly DeepLintRule[] = [overlayTimingRule];\n\n/**\n * A rule may only raise the codes it declares, at the severity it declares: the\n * registry is what says which codes exist and whether they block, so it cannot be\n * allowed to drift from what the rules actually report.\n */\nfunction assertDeclared(rule: LintRule | DeepLintRule, raised: readonly ValidationIssue[]): void {\n for (const issue of raised) {\n const declared = rule.codes[issue.code];\n const severity = issue.severity ?? \"error\";\n if (declared !== severity) {\n throw new Error(\n `lint rule \"${rule.name}\" raised ${issue.code} as ${severity}, but declares ${declared ?? \"no such code\"}`,\n );\n }\n }\n}\n\nexport function runLintRules(ctx: LintContext): void {\n for (const rule of LINT_RULES) {\n const before = ctx.issues.length;\n rule.check(ctx);\n assertDeclared(rule, ctx.issues.slice(before));\n }\n}\n\nexport async function runDeepLintRules(canvas: Canvas, issues: ValidationIssue[]): Promise<void> {\n for (const rule of DEEP_LINT_RULES) {\n const before = issues.length;\n await rule.check(canvas, issues);\n assertDeclared(rule, issues.slice(before));\n }\n}\n","import { canonicalize } from \"@baker/canvas-contract\";\nimport type { BackendClient } from \"../client/backend-client.ts\";\nimport { mapWithConcurrency, resolveConcurrency } from \"../lib/concurrency.ts\";\nimport { forEachObject, mapObjects, mapObjectsAsync, mapStrings } from \"../lib/json-walk.ts\";\nimport { ulid } from \"../lib/ulid.ts\";\nimport { persistOutputAssetUrls } from \"../nodes/remote/upload.ts\";\nimport type { Canvas, NodeDecl } from \"../schema/canvas.ts\";\nimport { renderTemplate } from \"../schema/prompts.ts\";\nimport { type AssetRef, contentShaOf, isAssetRef, isAssetRefArray, isUrlOnlyRef } from \"../schema/refs.ts\";\nimport type { AssetStore } from \"../storage/asset-store.ts\";\nimport type { CacheStore } from \"../storage/cache-store.ts\";\nimport { OutputWriter } from \"../storage/output-writer.ts\";\nimport { computeCacheKey } from \"./cache-key.ts\";\nimport type { ExecCtx, LogFn } from \"./context.ts\";\nimport { estimateNodeCredits } from \"./credits.ts\";\nimport type { AnyNodeDefinition } from \"./define.ts\";\nimport {\n failureDetail,\n LayerExecutionError,\n type LayerFailure,\n NodeExecutionError,\n type NodeFailureDetail,\n RunAbortedError,\n ValidationError,\n} from \"./errors.ts\";\nimport { buildDependencyGraph } from \"./graph.ts\";\nimport { renderedPromptOverLimit } from \"./prompt-limit.ts\";\nimport { type AllOutputs, resolveRefs } from \"./refs.ts\";\nimport type { NodeRegistry } from \"./registry.ts\";\nimport { reachableNodes, subgraph, topologicalLayers } from \"./scheduler.ts\";\nimport { validateCanvas, validateCanvasDeep } from \"./validator.ts\";\n\nexport type RunResult = {\n run_id: string;\n output: AssetRef | AssetRef[] | unknown;\n outputs_by_node: Record<string, Record<string, unknown>>;\n stats: { total_nodes: number; cached_nodes: number; total_credits: number; duration_ms: number };\n outputs_dir: string;\n node_runs: NodeRunRecord[];\n};\n\nexport type NodeRunRecord = {\n node_id: string;\n node_type: string;\n cached: boolean;\n duration_ms: number;\n credits: number;\n};\n\n/**\n * Live run telemetry: the plan (post-prune node list + dependency edges) fires\n * once before any node executes, then start/settled/failed per node. Consumers\n * stream these into run-history progress snapshots; a throwing consumer is\n * swallowed — telemetry must never fail a paid run.\n */\nexport type RunProgressEvent =\n | {\n kind: \"plan\";\n nodes: Array<{ node_id: string; node_type: string; deps: string[]; params: unknown; role?: string }>;\n }\n | { kind: \"node_start\"; node_id: string }\n | { kind: \"node_settled\"; run: NodeRunRecord; outputs: Record<string, unknown> }\n | { kind: \"node_failed\"; node_id: string; error: NodeFailureDetail };\n\ntype RunOptions = {\n signal?: AbortSignal;\n run_id?: string;\n cache_policy?: \"read_write\" | \"bypass\" | \"read_only\";\n /** Max nodes of one layer in flight at once; invalid or missing → 8 (DEFAULT_CONCURRENCY). */\n concurrency?: number;\n /**\n * Node ids to force fresh for THIS run only (the `--regenerate` flag). Folds\n * the run id into each named node's cache key so it re-renders regardless of\n * the content cache, without editing the canvas. Persistent regeneration uses\n * the per-node `regenerate` field instead.\n */\n regenerate?: ReadonlySet<string>;\n /**\n * Node types to stop BEFORE, leaving the run resumable.\n *\n * Every defect in a finished ad was caught at the end, after every clip had been paid\n * for — while the frames that carry the look land minutes earlier and cost a fraction of\n * them. This is what lets someone see those first: the run settles everything cheap and\n * stops on the doorstep of the expensive half.\n *\n * It raises `RunAbortedError`, the completed\n * nodes are already cached, and continuing is re-running the same `--run-id`, which pays\n * for none of them twice.\n */\n stop_before_kinds?: ReadonlySet<string>;\n onProgress?: (event: RunProgressEvent) => void;\n};\n\ntype EngineOptions = {\n registry: NodeRegistry;\n client: BackendClient;\n assets: AssetStore;\n cache: CacheStore;\n outputsDir: string;\n log?: LogFn;\n /**\n * Stamp durable canvas-assets URLs onto fresh node outputs before they are\n * cached — the precondition for remote cache portability and run-history\n * records. Best-effort: a failed upload logs a warning and keeps the entry\n * local-only, never fails the node.\n */\n persistAssets?: boolean;\n};\n\n/**\n * The nodes in this layer the caller asked to stop before, if any.\n *\n * Per LAYER rather than per node so the stop is clean: a half-dispatched layer would bill\n * some of exactly what the caller asked not to run. Pulled out of the loop because the loop\n * was already at the complexity ceiling, and a stop condition is easier to read named.\n */\nfunction heldByCheckpoint(\n canvas: { nodes: { id: string; type: string }[] },\n layer: readonly string[],\n kinds: ReadonlySet<string> | undefined,\n): string[] {\n if (!kinds?.size) return [];\n return layer.filter((id) => {\n const type = canvas.nodes.find((n) => n.id === id)?.type;\n return type !== undefined && kinds.has(type);\n });\n}\n\nexport class Engine {\n private readonly registry: NodeRegistry;\n private readonly client: BackendClient;\n private readonly assets: AssetStore;\n private readonly cache: CacheStore;\n private readonly outputsDir: string;\n private readonly log: LogFn;\n private readonly persistAssets: boolean;\n\n constructor(opts: EngineOptions) {\n this.registry = opts.registry;\n this.client = opts.client;\n this.assets = opts.assets;\n this.cache = opts.cache;\n this.outputsDir = opts.outputsDir;\n this.log = opts.log ?? (() => undefined);\n this.persistAssets = opts.persistAssets ?? false;\n }\n\n validate(canvas: unknown) {\n return validateCanvas(canvas, this.registry);\n }\n\n /** Async variant — runs every node's `validateExtra` hook in addition to the shallow checks. */\n validateDeep(canvas: unknown) {\n return validateCanvasDeep(canvas, this.registry);\n }\n\n async run(input: unknown, opts: RunOptions = {}): Promise<RunResult> {\n // Always run the deep validator before any node executes. This catches\n // composition-meta mistakes (bad param shape, missing required param,\n // unknown @baker composition) upfront — before paying for any remote node.\n // Without this, a typo in the final hyperframe_render call would only\n // surface after all 14 video_generate clips had spent credits.\n const validation = await this.validateDeep(input);\n if (!validation.ok) throw new ValidationError(validation.issues);\n const canvas = validation.canvas;\n\n const runId = opts.run_id ?? `r_${ulid()}`;\n const writer = new OutputWriter({ outputsDir: this.outputsDir, runId });\n await writer.ensure();\n\n this.log(`[validate] ok (${canvas.nodes.length} nodes, est. ${validation.estimatedCredits} credits)`);\n // Advisory warnings never block the run, but a billed render should still print\n // them (a missing element reference, a scene span past the model's cap) so the\n // operator sees them without a separate `validate` pass.\n for (const w of validation.warnings ?? []) {\n this.log(`[warn ] ${w.code}${w.node_id ? ` (${w.node_id})` : \"\"}: ${w.message}`);\n }\n\n const t0 = Date.now();\n const outputs: AllOutputs = {};\n const counters = { cachedNodes: 0, totalCredits: 0 };\n const nodeRuns: NodeRunRecord[] = [];\n\n const graph = this.pruneToOutput(canvas, buildDependencyGraph(canvas));\n const needsBytes = computeNeedsLocalBytes(canvas, graph, this.registry);\n this.emitProgress(opts, {\n kind: \"plan\",\n nodes: [...graph.entries()].map(([id, deps]) => {\n const node = canvas.nodes.find((n) => n.id === id);\n const role = node?.tags?.role;\n return {\n node_id: id,\n node_type: node?.type ?? \"unknown\",\n deps: [...deps],\n params: node?.params,\n ...(role !== undefined ? { role } : {}),\n };\n }),\n });\n\n await this.runLayers(canvas, graph, outputs, runId, writer, opts, counters, nodeRuns, needsBytes);\n\n const output = pickFinalOutput(canvas, outputs);\n const stats = {\n total_nodes: canvas.nodes.length,\n cached_nodes: counters.cachedNodes,\n total_credits: counters.totalCredits,\n duration_ms: Date.now() - t0,\n };\n\n await writer.writeManifest(\"manifest\", {\n run_id: runId,\n canvas,\n outputs_by_node: outputs,\n output,\n stats,\n node_runs: nodeRuns,\n started_at: t0,\n completed_at: t0 + stats.duration_ms,\n });\n await this.writeFinal(writer, output);\n\n this.log(\n `[done ] ${stats.cached_nodes}/${stats.total_nodes} cached, ${stats.total_credits} credits, ${stats.duration_ms}ms`,\n );\n this.log(`outputs in: ${writer.runDir}`);\n\n return { run_id: runId, output, outputs_by_node: outputs, stats, outputs_dir: writer.runDir, node_runs: nodeRuns };\n }\n\n private async runLayers(\n canvas: Canvas,\n graph: Map<string, Set<string>>,\n outputs: AllOutputs,\n runId: string,\n writer: OutputWriter,\n opts: RunOptions,\n counters: { cachedNodes: number; totalCredits: number },\n nodeRuns: NodeRunRecord[],\n needsBytes: ReadonlySet<string>,\n ): Promise<void> {\n const layers = topologicalLayers(graph);\n const limit = resolveConcurrency(opts.concurrency);\n for (const layer of layers) {\n this.assertLayerMayRun(canvas, layer, opts);\n // Settle the whole layer: a failing node must not abandon in-flight\n // siblings, or their cache writes are lost and repeated runs of a canvas\n // with several flaky nodes never converge.\n const settled = await mapWithConcurrency(layer, limit, (nodeId) => {\n // A signal mid-layer stops DISPATCHING further nodes; already-running\n // siblings still settle so their cache writes land. Skipped nodes never\n // start (no node_start / node_failed) — they stay pending for a resume.\n if (opts.signal?.aborted) {\n return Promise.reject(new RunAbortedError(\"signal\", \"run aborted before node dispatch\"));\n }\n this.emitProgress(opts, { kind: \"node_start\", node_id: nodeId });\n return this.executeOne(canvas, nodeId, outputs, runId, writer, opts, needsBytes.has(nodeId)).then((r) => {\n if (r.cached) counters.cachedNodes++;\n counters.totalCredits += r.credits;\n const node = canvas.nodes.find((n) => n.id === nodeId);\n if (node) {\n const run: NodeRunRecord = {\n node_id: nodeId,\n node_type: node.type,\n cached: r.cached,\n duration_ms: r.durationMs,\n credits: r.credits,\n };\n nodeRuns.push(run);\n this.emitProgress(opts, { kind: \"node_settled\", run, outputs: outputs[nodeId] ?? {} });\n }\n });\n });\n const failures: LayerFailure[] = [];\n settled.forEach((result, i) => {\n const nodeId = layer[i];\n if (result.status === \"rejected\" && nodeId) {\n // Abort-skips are not node failures — the node never dispatched.\n if (result.reason instanceof RunAbortedError) return;\n failures.push({ nodeId, reason: result.reason });\n this.emitProgress(opts, { kind: \"node_failed\", node_id: nodeId, error: failureDetail(result.reason) });\n }\n });\n // An abort outranks same-layer failures: the signal is why the run ended,\n // and per-node outcomes are already captured in the progress snapshot.\n if (opts.signal?.aborted) throw new RunAbortedError(\"signal\", \"run aborted by signal\");\n if (failures.length === 1 && failures[0]) throw failures[0].reason;\n if (failures.length > 1) throw new LayerExecutionError(failures);\n }\n }\n\n /** Progress consumers are observers only — an exception there must never fail the run. */\n private emitProgress(opts: RunOptions, event: RunProgressEvent): void {\n if (!opts.onProgress) return;\n try {\n opts.onProgress(event);\n } catch {\n // deliberately swallowed\n }\n }\n\n /**\n * Dead-node elimination: when the canvas declares an `output`, execute only the\n * nodes that output transitively depends on. Orphaned nodes (left by an edit or\n * a rewire) otherwise still run, bill, and abort the run on their own failure.\n * No declared output ⇒ intent is ambiguous, so run everything (back-compat).\n */\n private pruneToOutput(canvas: Canvas, graph: Map<string, Set<string>>): Map<string, Set<string>> {\n const rootId = canvas.output?.node;\n if (!rootId || !graph.has(rootId)) return graph;\n const keep = reachableNodes(graph, [rootId]);\n if (keep.size === graph.size) return graph;\n const dropped = [...graph.keys()].filter((id) => !keep.has(id));\n this.log(`[prune ] skipping ${dropped.length} node(s) unreachable from output: ${dropped.join(\", \")}`);\n return subgraph(graph, keep);\n }\n\n private async writeFinal(writer: OutputWriter, output: unknown): Promise<void> {\n if (output === undefined) return;\n if (isAssetRefArray(output)) {\n for (let i = 0; i < output.length; i++) {\n const ref = output[i];\n if (ref) await writer.writeAsset(`_final__${i}`, ref);\n }\n return;\n }\n if (isAssetRef(output)) {\n await writer.writeAsset(\"_final\", output);\n return;\n }\n await writer.writeManifest(\"_final\", output);\n }\n\n private async executeOne(\n canvas: Canvas,\n nodeId: string,\n outputs: AllOutputs,\n runId: string,\n writer: OutputWriter,\n opts: RunOptions,\n downloadOutputs: boolean,\n ): Promise<{ cached: boolean; credits: number; durationMs: number }> {\n const node = canvas.nodes.find((n) => n.id === nodeId);\n if (!node) throw new Error(`executor: missing node ${nodeId}`);\n const def = this.registry.get(node.type);\n if (!def) throw new Error(`executor: missing registry entry for type ${node.type}`);\n\n const regenerateToken = resolveRegenerateToken(node, opts.regenerate, runId);\n const prepared = await prepareForExecution(node, outputs, def, canvas.cache_salt, regenerateToken, this.assets);\n const policy = opts.cache_policy ?? \"read_write\";\n\n if (policy !== \"bypass\") {\n const hit = await this.replayCached(node, prepared.cacheKey, outputs, writer);\n if (hit) return hit;\n }\n assertPromptsFitModel(node, prepared.renderedParams);\n\n const t0 = Date.now();\n let charged: number | undefined;\n const ctx: ExecCtx = {\n canvasRunId: runId,\n nodeId: node.id,\n nodeType: node.type,\n cacheKey: prepared.cacheKey,\n downloadOutputs,\n client: this.client,\n assets: this.assets,\n log: this.log,\n signal: opts.signal,\n reportCharge: (c) => {\n charged = (charged ?? 0) + c;\n },\n };\n // Fail-safe: a local node needs its input bytes on disk. `computeNeedsLocalBytes`\n // guarantees its producers were downloaded, but if an edge was missed\n // (e.g. an unrecognized ref shape), materialize any URL-only input on demand\n // rather than letting the local runner throw on the missing path. A\n // passthrough-ref node (collect) never reads bytes, so it stays URL-only.\n const preparedForExec = needsLocalMaterialization(def)\n ? { ...prepared, resolvedInputs: await this.materializeLocalInputs(prepared.resolvedInputs) }\n : prepared;\n const { parsedInputs, parsedParams } = parseNodeArgs(def, preparedForExec, node.id, node.type);\n const result = await invokeExecute(def, parsedInputs, parsedParams, ctx, node.id, node.type);\n const elapsed = Date.now() - t0;\n // What the backend charged is the spend; a node it never billed (a local one,\n // or an old backend that does not report) falls back to its own estimate.\n const credits = charged ?? estimateNodeCredits(def, preparedForExec.renderedParams);\n const outputsObj = result as Record<string, unknown>;\n outputs[node.id] = outputsObj;\n\n // Stamp durable URLs BEFORE the cache write so the entry (and the run\n // record built from outputs_by_node) is rehydratable in any sandbox.\n // URL-only stamping never changes cache keys (inputs hash by sha256,\n // params are normalized to content identity).\n if (this.persistAssets) await this.persistOutputs(node.id, outputsObj, ctx);\n\n if (policy === \"read_write\") {\n await this.cache.put({\n cacheKey: prepared.cacheKey,\n nodeType: node.type,\n nodeVersion: def.version,\n outputs: outputsObj,\n refs: collectShas(outputsObj),\n createdAt: Date.now(),\n });\n }\n await this.materializeNodeOutputs(node.id, outputsObj, writer);\n this.log(`[exec ] ${node.id.padEnd(20)} ${node.type.padEnd(20)} fresh (${(elapsed / 1000).toFixed(1)}s)`);\n this.logOutputWarnings(node.id, outputsObj);\n return { cached: false, credits, durationMs: elapsed };\n }\n\n /**\n * Every reason a layer must not dispatch, checked before any of its nodes starts, so a stop\n * never leaves a layer half billed: a signal, the cost cap and a checkpoint.\n */\n private assertLayerMayRun(canvas: Canvas, layer: string[], opts: RunOptions): void {\n if (opts.signal?.aborted) throw new RunAbortedError(\"signal\", \"run aborted before layer dispatch\");\n // Stop on the doorstep of the expensive half, before anything in this layer\n // dispatches. Checked per LAYER rather than per node so the stop is clean: a\n // half-dispatched layer would bill some of exactly what the caller asked not to run.\n const held = heldByCheckpoint(canvas, layer, opts.stop_before_kinds);\n if (held.length > 0) {\n throw new RunAbortedError(\n \"checkpoint\",\n `stopped before ${held.join(\", \")} — everything up to here is done and cached, so continuing this run id pays for none of it again`,\n );\n }\n }\n\n /** A cache hit replayed as this node's result: its outputs written, and free. */\n private async replayCached(\n node: NodeDecl,\n cacheKey: string,\n outputs: AllOutputs,\n writer: OutputWriter,\n ): Promise<{ cached: boolean; credits: number; durationMs: number } | null> {\n const cacheT0 = Date.now();\n const hit = await this.cache.get(cacheKey);\n if (!hit) return null;\n outputs[node.id] = hit.outputs;\n await this.materializeNodeOutputs(node.id, hit.outputs, writer);\n this.log(`[exec ] ${node.id.padEnd(20)} ${node.type.padEnd(20)} cached`);\n this.logOutputWarnings(node.id, hit.outputs);\n return { cached: true, credits: 0, durationMs: Date.now() - cacheT0 };\n }\n\n /** Best-effort: a failed upload keeps the outputs local-only, never fails the node. */\n private async persistOutputs(nodeId: string, outputs: Record<string, unknown>, ctx: ExecCtx): Promise<void> {\n try {\n await persistOutputAssetUrls(outputs, ctx);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n this.log(`[warn ] ${nodeId}: asset persistence failed (${msg}) — outputs stay local-only`);\n }\n }\n\n /**\n * Print the warnings a node left on its outputs' metadata — a `voice_select` that\n * could not honor the requested gender, say. Left in the metadata alone they were\n * read only after the render was paid for and somebody listened. Printed on a\n * cached replay too: the output still carries the compromise.\n */\n private logOutputWarnings(nodeId: string, outputs: Record<string, unknown>): void {\n for (const warning of outputWarnings(outputs)) this.log(`[warn ] ${nodeId}: ${warning}`);\n }\n\n private async materializeNodeOutputs(\n nodeId: string,\n outputs: Record<string, unknown>,\n writer: OutputWriter,\n ): Promise<void> {\n for (const [name, value] of Object.entries(outputs)) {\n if (isAssetRef(value)) {\n await writer.writeAsset(`${nodeId}__${name}`, value);\n } else if (isAssetRefArray(value)) {\n for (let i = 0; i < value.length; i++) {\n const ref = value[i];\n if (ref) await writer.writeAsset(`${nodeId}__${name}__${i}`, ref);\n }\n } else {\n await writer.writeManifest(`${nodeId}__${name}`, value);\n }\n }\n }\n\n /**\n * Download any URL-only asset ref reachable in a local node's inputs so the\n * bytes are on disk before the local runner stages them. Returns a copy —\n * refs are replaced, never mutated in place, so the producer's cached output\n * (shared object) keeps its URL-only shape.\n */\n private async materializeLocalInputs(inputs: Record<string, unknown>): Promise<Record<string, unknown>> {\n return (await mapObjectsAsync(inputs, (v) => {\n if (!isUrlOnlyRef(v)) return undefined;\n this.log(`[warn ] materializing URL-only input on demand (${v.kind}/${v.mime}) — missed graph edge`);\n return this.assets.ingestRemote({\n kind: v.kind,\n url: v.url,\n sha256: v.sha256,\n mime: v.mime,\n metadata: v.metadata as Record<string, unknown> | undefined,\n });\n })) as Record<string, unknown>;\n }\n}\n\n/** The `metadata.warnings` strings on one asset ref. */\nfunction refWarnings(ref: unknown): string[] {\n if (!isAssetRef(ref)) return [];\n const warnings = ref.metadata?.warnings;\n if (!Array.isArray(warnings)) return [];\n return warnings.filter((w): w is string => typeof w === \"string\" && w.length > 0);\n}\n\n/** The distinct `metadata.warnings` strings on a node's output asset refs. */\nfunction outputWarnings(outputs: Record<string, unknown>): string[] {\n const refs = Object.values(outputs).flatMap((value) => (Array.isArray(value) ? value : [value]));\n return [...new Set(refs.flatMap(refWarnings))];\n}\n\ntype Prepared = {\n resolvedInputs: Record<string, unknown>;\n renderedParams: unknown;\n cacheKey: string;\n};\n\n/**\n * The cache-busting token for a node, or undefined to leave its content-addressed\n * key untouched. The one-shot `--regenerate` flag wins and folds the run id (fresh\n * every run); otherwise the persistent per-node `regenerate` field is used. Both\n * flow into the cache key as an `extras` entry, so ONLY targeted nodes change keys\n * — every other node keeps its exact prior key and cache hit.\n */\nexport function resolveRegenerateToken(\n node: NodeDecl,\n forced: ReadonlySet<string> | undefined,\n runId: string,\n): string | undefined {\n if (forced?.has(node.id)) return `run:${runId}`;\n if (node.regenerate !== undefined) return `node:${String(node.regenerate)}`;\n return undefined;\n}\n\nasync function prepareForExecution(\n node: NodeDecl,\n outputs: AllOutputs,\n def: AnyNodeDefinition,\n cacheSalt: string | undefined,\n regenerateToken: string | undefined,\n assets: AssetStore,\n): Promise<Prepared> {\n const resolvedInputs = (resolveRefs(node.inputs ?? {}, { outputs }) ?? {}) as Record<string, unknown>;\n const resolvedParams = (resolveRefs(node.params ?? {}, { outputs }) ?? {}) as Record<string, unknown>;\n const slotValues = await hydrateTextSlots(resolvedInputs, assets, node.id, node.type);\n const renderedParams = renderPromptSlotsInValue(resolvedParams, slotValues);\n // Nodes may declare a cacheKeyExtras hook to fold external content into the\n // cache key (e.g. hyperframe_render hashes the composition directory so\n // composition edits invalidate cached renders automatically). A failing hook\n // fails the node: falling back to a params-only key would cache results that\n // ignore external content and silently serve stale outputs later.\n let extras: Record<string, string> | undefined;\n if (def.cacheKeyExtras) {\n try {\n extras = await def.cacheKeyExtras({ params: renderedParams });\n } catch (e) {\n throw new NodeExecutionError(node.id, node.type, { kind: \"local\", cause: e });\n }\n }\n // Fold the regenerate token in as an extra ONLY when present, so nodes without\n // one keep byte-identical cache keys (no mass invalidation on rollout).\n if (regenerateToken !== undefined) {\n extras = { ...(extras ?? {}), __regenerate__: regenerateToken };\n }\n const cacheKey = computeCacheKey({\n node_id: node.type,\n node_version: def.version,\n params: normalizeParamsForCacheKey(renderedParams),\n input_hashes: hashInputs(resolvedInputs),\n cache_salt: cacheSalt,\n extras,\n });\n return { resolvedInputs, renderedParams, cacheKey };\n}\n\n/**\n * Slots are filled only at run time, from what upstream nodes produced, so this is\n * the first moment the prompt the model will receive exists. Refuse it here rather\n * than send it for the backend to refuse.\n */\nfunction assertPromptsFitModel(node: NodeDecl, renderedParams: unknown): void {\n const overLimit = renderedPromptOverLimit(node.id, node.type, renderedParams);\n if (overLimit.length > 0) {\n throw new NodeExecutionError(node.id, node.type, { kind: \"local\", cause: new Error(overLimit.join(\"; \")) });\n }\n}\n\nfunction parseNodeArgs(\n def: AnyNodeDefinition,\n prepared: Prepared,\n nodeId: string,\n nodeType: string,\n): { parsedInputs: unknown; parsedParams: unknown } {\n try {\n return {\n parsedInputs: def.inputs.parse(prepared.resolvedInputs),\n parsedParams: def.params.parse(prepared.renderedParams),\n };\n } catch (e) {\n throw new NodeExecutionError(nodeId, nodeType, { kind: \"local\", cause: e });\n }\n}\n\nasync function invokeExecute(\n def: AnyNodeDefinition,\n parsedInputs: unknown,\n parsedParams: unknown,\n ctx: ExecCtx,\n nodeId: string,\n nodeType: string,\n): Promise<unknown> {\n try {\n return await def.execute({ inputs: parsedInputs, params: parsedParams, ctx });\n } catch (e) {\n if (e instanceof NodeExecutionError) throw e;\n throw new NodeExecutionError(nodeId, nodeType, { kind: \"local\", cause: e });\n }\n}\n\n/** A local node needs its input bytes on disk unless it only forwards refs (collect). */\nfunction needsLocalMaterialization(def: AnyNodeDefinition): boolean {\n return def.location === \"local\" && !def.passthroughRefs;\n}\n\nfunction pickFinalOutput(canvas: Canvas, outputs: AllOutputs): unknown {\n if (canvas.output) {\n const node = outputs[canvas.output.node];\n return node ? node[canvas.output.output] : undefined;\n }\n const last = canvas.nodes[canvas.nodes.length - 1];\n if (!last) return undefined;\n const lastOut = outputs[last.id];\n return lastOut ? Object.values(lastOut)[0] : undefined;\n}\n\n/**\n * Node ids whose outputs a LOCAL (ffmpeg/magick) node consumes and therefore\n * needs on disk. A node NOT in this set can stay URL-only after remote exec —\n * the backend already persisted its bytes to R2 with a durable sha256 URL, so\n * the redundant R2→sandbox download is skipped. `graph` maps node → its upstream\n * deps, so a local node's deps are exactly its byte-needing producers. The run's\n * final output (canvas.output) is a ref pointer, not a graph consumer, so being\n * the final output never forces a download.\n */\nexport function computeNeedsLocalBytes(\n canvas: Canvas,\n graph: Map<string, Set<string>>,\n registry: NodeRegistry,\n): Set<string> {\n const typeById = new Map(canvas.nodes.map((n) => [n.id, n.type]));\n const needs = new Set<string>();\n for (const [consumerId, deps] of graph) {\n const def = registry.get(typeById.get(consumerId) ?? \"\");\n if (def?.location !== \"local\" || def.passthroughRefs) continue;\n for (const dep of deps) needs.add(dep);\n }\n return needs;\n}\n\n/**\n * Reduce each resolved input to a stable identity for the cache key: a ref → its\n * sha256; anything else → a canonical literal with any NESTED refs reduced to\n * `{sha256,kind,mime}` first. Without that normalization, a ref buried inside an\n * object/array input folds its volatile `url`/`path`/`metadata.ingested_at` into\n * the key — the node either never caches (key churns every run) or keeps a stale\n * key when content changes under an unchanged url. Flat inputs (top-level refs,\n * scalars, ref-free literals) key byte-identically to before. Exported for tests.\n */\nexport function hashInputs(inputs: Record<string, unknown>): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [k, v] of Object.entries(inputs)) {\n if (Array.isArray(v)) {\n // Preserve every element (and its position): dropping non-asset literals\n // would let two canvases differing only in an array literal share a key.\n out[k] = v.map((el) => contentShaOf(el) ?? canonicalLiteral(normalizeParamsForCacheKey(el)));\n } else {\n const s = contentShaOf(v);\n if (s !== null) out[k] = s;\n else out[k] = canonicalLiteral(normalizeParamsForCacheKey(v));\n }\n }\n return out;\n}\n\n// Params may carry AssetRefs resolved from `$ref:` (e.g. a composition `image`\n// param wired to an upstream asset). Unlike inputs — which `hashInputs` reduces\n// to sha256 — params are folded into the cache key whole, so the AssetRef's\n// volatile fields (`url`, `path`, `metadata.ingested_at`) would change the key\n// every run and defeat caching. Reduce any embedded AssetRef to its stable\n// content identity before keying; the full object is kept for execution.\nexport function normalizeParamsForCacheKey(v: unknown): unknown {\n return mapObjects(v, (o) => {\n if (contentShaOf(o) === null) return undefined;\n return { sha256: o.sha256, kind: o.kind, mime: o.mime };\n });\n}\n\nfunction canonicalLiteral(v: unknown): string {\n if (v === undefined) return \"u\";\n if (v === null) return \"n\";\n if (typeof v === \"string\") return `s:${v}`;\n if (typeof v === \"number\") return `f:${v}`;\n if (typeof v === \"boolean\") return `b:${v}`;\n // Objects/arrays go through canonicalize (sorted keys, NFC) so the key is\n // stable regardless of property order — raw JSON.stringify is order-dependent.\n return `j:${canonicalize(v)}`;\n}\n\nfunction collectShas(outputs: Record<string, unknown>): string[] {\n const out: string[] = [];\n forEachObject(outputs, (o) => {\n const sha = contentShaOf(o);\n if (sha !== null) out.push(sha);\n });\n return [...new Set(out)];\n}\n\nfunction renderPromptSlotsInValue(value: unknown, slots: Record<string, unknown>): unknown {\n const values = stringifySlots(slots);\n return mapStrings(value, (s) => renderTemplate(s, values));\n}\n\n/**\n * Hard cap on text/json content inlined into a prompt slot. Provider prompts\n * comfortably fit hundreds of KB; anything larger is almost certainly a wiring\n * mistake (e.g. pointing a slot at a transcript dump) and would silently eat\n * the model's context, so the node fails instead of truncating.\n */\nconst MAX_INLINE_TEXT_BYTES = 256 * 1024;\n\n/**\n * Replace text/json asset refs in prompt slots with their actual content so\n * `{{slot}}` injects what the upstream node produced. Without this, refs\n * render as compact `[text: <sha8>]` placeholders — fine for binary kinds\n * (image/video/audio), useless for text meant to feed the next prompt.\n */\nasync function hydrateTextSlots(\n slots: Record<string, unknown>,\n assets: AssetStore,\n nodeId: string,\n nodeType: string,\n): Promise<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(slots)) {\n out[k] = await hydrateSlotValue(v, assets, nodeId, nodeType);\n }\n return out;\n}\n\nasync function hydrateSlotValue(\n value: unknown,\n assets: AssetStore,\n nodeId: string,\n nodeType: string,\n): Promise<unknown> {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const v of value) out.push(await hydrateSlotValue(v, assets, nodeId, nodeType));\n return out;\n }\n if (!isAssetRef(value) || (value.kind !== \"text\" && value.kind !== \"json\")) return value;\n let bytes: Buffer;\n try {\n bytes = await assets.readBytes(value.sha256, value.mime);\n } catch (e) {\n // A text/json prompt slot is hydrated by reading its bytes locally, so its\n // producer needs them on disk even when the consumer is a remote node (the\n // `needsLocalBytes` graph rule only tracks local ffmpeg/magick consumers).\n // If the producer was kept URL-only, fetch it on demand — these assets are\n // tiny, so the transfer is cheap.\n if (value.url) {\n try {\n await assets.ingestRemote({ kind: value.kind, url: value.url, sha256: value.sha256, mime: value.mime });\n bytes = await assets.readBytes(value.sha256, value.mime);\n } catch (e2) {\n throw new NodeExecutionError(nodeId, nodeType, { kind: \"local\", cause: e2 });\n }\n } else {\n throw new NodeExecutionError(nodeId, nodeType, { kind: \"local\", cause: e });\n }\n }\n if (bytes.length > MAX_INLINE_TEXT_BYTES) {\n throw new NodeExecutionError(nodeId, nodeType, {\n kind: \"local\",\n cause: new Error(\n `slot asset ${value.sha256.slice(0, 8)} is ${bytes.length} bytes, over the ${MAX_INLINE_TEXT_BYTES}-byte inline prompt limit`,\n ),\n });\n }\n return bytes.toString(\"utf8\");\n}\n\nfunction stringifySlots(slots: Record<string, unknown>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(slots)) {\n out[k] = stringForSlot(v);\n }\n return out;\n}\n\nfunction stringForSlot(v: unknown): string {\n if (typeof v === \"string\") return v;\n if (isAssetRef(v)) {\n const ref = v;\n if (ref.kind === \"image\" && \"width\" in ref && \"height\" in ref && ref.width && ref.height) {\n return `[image: ${ref.width}x${ref.height}]`;\n }\n return `[${ref.kind}: ${ref.sha256.slice(0, 8)}]`;\n }\n if (Array.isArray(v)) return v.map(stringForSlot).join(\", \");\n return JSON.stringify(v);\n}\n","import type { AnyNodeDefinition } from \"./define.ts\";\n\nexport class NodeRegistry {\n private readonly byId = new Map<string, AnyNodeDefinition>();\n\n register(def: AnyNodeDefinition): void {\n if (this.byId.has(def.id)) {\n throw new Error(`NodeRegistry: duplicate node id \"${def.id}\"`);\n }\n this.byId.set(def.id, def);\n }\n\n registerAll(defs: readonly AnyNodeDefinition[]): void {\n for (const d of defs) this.register(d);\n }\n\n get(id: string): AnyNodeDefinition | undefined {\n return this.byId.get(id);\n }\n\n has(id: string): boolean {\n return this.byId.has(id);\n }\n\n ids(): string[] {\n return [...this.byId.keys()].sort();\n }\n\n all(): AnyNodeDefinition[] {\n return [...this.byId.values()];\n }\n}\n","import { execFile as execFileCb } from \"node:child_process\";\nimport type { Stats } from \"node:fs\";\nimport { mkdtemp, readFile, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { z } from \"zod\";\nimport { fetchExternalBytes } from \"../../lib/externalFetch.ts\";\nimport { runYtDlp, YT_DLP_BIN, YtDlpError, type YtDlpInfo } from \"../../lib/ytDlp.ts\";\nimport type { ExecCtx } from \"../engine/context.ts\";\nimport { defineNode } from \"../engine/define.ts\";\nimport { NodeExecutionError } from \"../engine/errors.ts\";\nimport { looksLikeHttpUrl } from \"../lib/url.ts\";\nimport type { AssetKind, AssetRef } from \"../schema/refs.ts\";\nimport { AssetRef as AssetRefSchema } from \"../schema/refs.ts\";\nimport { MAX_ASSET_BYTES } from \"../storage/asset-store.ts\";\nimport { isCanvasAssetOrigin, parseCanvasAssetUrl } from \"../storage/remote-cache-store.ts\";\nimport { sha256Hex } from \"../storage/sha256.ts\";\nimport { callBackendExec } from \"./remote/delegate.ts\";\nimport { presignAndPut } from \"./remote/upload.ts\";\n\nconst execFile = promisify(execFileCb);\n\n// ---------------------------------------------------------------------------\n// Schema — discriminated union on `source`\n// ---------------------------------------------------------------------------\n\nconst ExpectEnum = z.enum([\"image\", \"video\", \"audio\", \"text\", \"json\", \"font\"]);\n\n/**\n * Heal a `source:\"path\"` ingest whose `path` is actually an http(s) URL into a `source:\"url\"` ingest.\n *\n * The scaffold emits `source:\"path\"` asset slots (it expects a locally-dropped file); an operator\n * routinely fills one with a durable asset URL (`media.withbaker.com/…`) but forgets to flip `source`.\n * Left alone, `path` resolves as a relative filename and the run dies deep at execute with\n * `file_not_found` — AFTER billing has started, and after `validate` reported `ok` (the `path` branch\n * accepts any non-empty string, and validate never stats the disk). The distinction is decidable from\n * the value, so we coerce it here. Runs as a `z.preprocess` in front of the discriminated union, so\n * every caller — `validate`, the deep validator, and `execute` — sees the corrected shape.\n * `baker canvas validate`/`run` also write this same heal back to the canvas file (normalize-paths.ts),\n * so what is committed is what ran — the commit hook reads the file, not this schema.\n */\nexport function coercePathUrlToUrl(input: unknown): unknown {\n if (!input || typeof input !== \"object\") return input;\n const obj = input as Record<string, unknown>;\n if (obj.source === \"path\" && looksLikeHttpUrl(obj.path)) {\n const { path: _path, ...rest } = obj;\n return { ...rest, source: \"url\", url: (obj.path as string).trim() };\n }\n return input;\n}\n\nconst IngestParams = z.preprocess(\n coercePathUrlToUrl,\n z.discriminatedUnion(\"source\", [\n z\n .object({\n source: z.literal(\"url\"),\n url: z.string().url(),\n expect: ExpectEnum,\n })\n .strict(),\n z\n .object({\n source: z.literal(\"path\"),\n path: z.string().min(1),\n expect: ExpectEnum,\n })\n .strict(),\n ]),\n);\n\nconst IngestInputs = z.object({}).strict();\nconst IngestOutputs = z.object({ asset: AssetRefSchema }).strict();\n\ntype IngestParamsT = z.infer<typeof IngestParams>;\ntype UrlParams = Extract<IngestParamsT, { source: \"url\" }>;\ntype PathParams = Extract<IngestParamsT, { source: \"path\" }>;\n\n// ---------------------------------------------------------------------------\n// Strategy resolution — one branch on source, one switch on expect for URLs\n// ---------------------------------------------------------------------------\n\ntype Strategy = \"canvas_asset\" | \"direct_fetch\" | \"firecrawl\" | \"yt_dlp\" | \"local_file\";\n\nconst RAW_TEXT_EXTENSIONS = [\".txt\", \".md\"] as const;\n\nfunction resolveStrategy(params: IngestParamsT): Strategy {\n if (params.source === \"path\") return \"local_file\";\n if (takesCanvasAssetVerbatim(params.url, params.expect)) return \"canvas_asset\";\n switch (params.expect) {\n case \"video\":\n case \"audio\":\n return \"yt_dlp\";\n case \"image\":\n case \"json\":\n case \"font\":\n return \"direct_fetch\";\n case \"text\": {\n const p = safePathname(params.url);\n const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));\n return isRawText ? \"direct_fetch\" : \"firecrawl\";\n }\n default: {\n params.expect satisfies never;\n throw new Error(`ingest: unhandled expect=${String(params.expect)}`);\n }\n }\n}\n\nfunction safePathname(rawUrl: string): string {\n try {\n return new URL(rawUrl).pathname.toLowerCase();\n } catch {\n return \"\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Node definition\n// ---------------------------------------------------------------------------\n\nexport const ingestNode = defineNode({\n id: \"ingest\",\n version: \"1.3.0\",\n category: \"io\",\n location: \"local\",\n summary:\n \"Ingest an external URL or a local file into the asset store. Declare the kind you expect (image/video/audio/text/json/font); the node picks the strategy. For source=url: a canvas-assets URL (`…/canvas-assets/<xx>/<sha>.<ext>`, the durable copy of an earlier output) is taken back byte-for-byte with its sha unchanged; yt-dlp for other video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs), Firecrawl for HTML/PDF pages → markdown, direct HTTP fetch for binary URLs (images, fonts) and raw .txt/.md. For source=path: read a committed file from the workspace and upload to R2.\",\n when_to_use:\n 'Use as the on-ramp for any external asset a canvas needs. `expect` makes the output port statically typed so downstream wiring is checked before any byte is fetched. Pick `source: \"url\"` for portable canvas; pick `source: \"path\"` only for a file committed to the workspace (`src/brand/…`, `src/creatives/<slug>/assets/…`) — the canvas is rendered again in other Sessions, which have only what is committed. To reuse an earlier output (a voice take, a clip, a frame), ingest its canvas-assets URL from the run outputs: it comes back with the same sha, so nothing downstream re-renders. Never point `path` into `canvas/` (`canvas/.cache/`, `canvas/r_*`): that folder is not committed, and validate rewrites a cache path to its canvas-assets URL. Compose with other nodes for richer pipelines (e.g. `ingest expect=video` → `video_transcribe`).',\n inputs: IngestInputs,\n params: IngestParams,\n outputs: IngestOutputs,\n cost: () => ({ credits: 0, seconds_estimate: 5 }),\n\n // Output kind is declared in params, so validators/UI can type the port\n // statically without running the node.\n outputKinds: (params: IngestParamsT) => ({ asset: params.expect }),\n\n async execute({ params, ctx }) {\n const strategy = resolveStrategy(params);\n const source = params.source === \"url\" ? params.url : params.path;\n ctx.log(`ingest: ${strategy} (${params.expect}) ${source}`);\n const asset = await runStrategy(strategy, params, ctx);\n return { asset };\n },\n});\n\nfunction runStrategy(strategy: Strategy, params: IngestParamsT, ctx: ExecCtx): Promise<AssetRef> {\n if (params.source === \"path\") {\n if (strategy !== \"local_file\") {\n throw new Error(`ingest: invariant — source=path but strategy=${strategy}`);\n }\n return execLocalFile(params, ctx);\n }\n // params.source === \"url\" — narrowed\n switch (strategy) {\n case \"canvas_asset\":\n return execCanvasAsset(params, ctx);\n case \"direct_fetch\":\n return execDirectFetch(params, ctx);\n case \"firecrawl\":\n return execFirecrawl(params, ctx);\n case \"yt_dlp\":\n return execYtDlp(params, ctx);\n case \"local_file\":\n throw new Error(`ingest: invariant — source=url but strategy=local_file`);\n default: {\n strategy satisfies never;\n throw new Error(`ingest: unhandled strategy=${String(strategy)}`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// canvas_asset — an output our own store already holds, taken back verbatim\n// ---------------------------------------------------------------------------\n\n/**\n * Re-use an earlier output by its durable URL: the same bytes, the same sha.\n *\n * Any other route changes the asset. yt-dlp re-encodes audio to m4a (a new sha, and a\n * voice cut word by word drifts), and the image route re-rasterizes. An agent that\n * needed a take byte-identical had no route that gave it one, and reached for the\n * local cache instead — a gitignored path that broke the next Session's publish. The\n * download is checked against the sha the URL names, so a truncated or substituted\n * file is refused rather than quietly re-addressed.\n */\n/**\n * The mime a canvas-assets URL reads back as, when it reads back as `expect` — else null. The kind\n * comes from the URL's extension, and the backend names a few mimes it has no extension for `.bin`\n * (`text/markdown`), which reads back as octet-stream: such a URL is not one to rewrite a path to.\n */\nexport function canvasAssetMime(url: string, expect: AssetKind): string | null {\n const address = parseCanvasAssetUrl(url);\n const mime = address ? EXT_TO_MIME[address.ext] : undefined;\n return mime !== undefined && inferKindFromMime(mime) === expect ? mime : null;\n}\n\n/** Our own store's URL for an asset of this kind: fetched directly by content address, nothing else is. */\nfunction takesCanvasAssetVerbatim(url: string, expect: AssetKind): boolean {\n return isCanvasAssetOrigin(url) && canvasAssetMime(url, expect) !== null;\n}\n\nasync function execCanvasAsset(params: UrlParams, ctx: ExecCtx): Promise<AssetRef> {\n const address = parseCanvasAssetUrl(params.url);\n const mime = canvasAssetMime(params.url, params.expect);\n if (!address || mime === null) throw new Error(`ingest: invariant — canvas_asset strategy for ${params.url}`);\n try {\n return await ctx.assets.ingestRemote({\n kind: params.expect,\n url: params.url,\n sha256: address.sha256,\n mime,\n metadata: { strategy: \"canvas_asset\", source_url: params.url, ingested_at: new Date().toISOString() },\n });\n } catch (e) {\n throw localExecError(ctx, (e as Error).message);\n }\n}\n\n// ---------------------------------------------------------------------------\n// direct_fetch / firecrawl — both go through the backend\n// ---------------------------------------------------------------------------\n\nasync function execDirectFetch(params: UrlParams, ctx: ExecCtx): Promise<AssetRef> {\n // Image URLs are fetched and normalized locally rather than delegated to the backend: image-\n // generation models only read jpeg/png/gif/webp and 400 the WHOLE generation on anything else\n // (SVG, AVIF, HEIC, TIFF, BMP). Normalization needs sharp, which can't load in the Convex runtime,\n // so it has to happen here — the same treatment execLocalFile gives a `source: \"path\"` image. Text/\n // json/font URLs still go through the backend (firecrawl for HTML/PDF, direct fetch otherwise).\n if (params.expect === \"image\") {\n return ingestImageUrl(params.url, ctx);\n }\n const result = await callBackendExec({\n nodeType: \"ingest\",\n nodeVersion: ingestNode.version,\n params: { strategy: \"direct_fetch\", url: params.url, expect: params.expect },\n inputs: {},\n outputKinds: { asset: params.expect },\n ctx,\n });\n return assertAssetOutput(result, params.expect);\n}\n\n/**\n * Fetch an image URL, normalize it to a model-safe raster, and upload. Mirrors execLocalFile so an\n * asset behaves identically whether it's sourced by URL or by path. A non-decodable body (e.g. an\n * HTML error page returned with a 200) surfaces as a clear NodeExecutionError instead of poisoning a\n * downstream generation.\n */\nasync function ingestImageUrl(url: string, ctx: ExecCtx): Promise<AssetRef> {\n let bytes: Buffer;\n try {\n // `maxBytes` covers both halves — the body that declares itself oversize\n // and the one that says nothing and then arrives too big. There used to be\n // a second check here for the latter, on the belief that `fetchExternalBytes`\n // only handled the former. It did not handle either: an oversize declared\n // body came back as a 200 with an *empty* buffer, which sailed through a\n // `byteLength > MAX_ASSET_BYTES` test and reached the image decoder, so a\n // file that was merely too large was reported as a corrupt one.\n ({ buffer: bytes } = await fetchExternalBytes(url, { maxBytes: MAX_ASSET_BYTES }));\n } catch (e) {\n throw localExecError(ctx, `fetch ${url} → ${(e as Error).message}`);\n }\n let normalized: ModelSafeImage;\n try {\n normalized = await toModelSafeImage(bytes);\n } catch (e) {\n throw localExecError(ctx, `${url}: ${(e as Error).message}`);\n }\n const note = normalizationNote(normalized);\n if (note) ctx.log(`ingest: ${url} ${note}`);\n return uploadAndIngest({\n bytes: normalized.bytes,\n kind: \"image\",\n mime: normalized.mime,\n metadata: {\n source_url: url,\n strategy: \"direct_fetch\",\n ingested_at: new Date().toISOString(),\n ...(normalized.rasterizedFrom ? { rasterized_from: normalized.rasterizedFrom } : {}),\n },\n ctx,\n });\n}\n\nasync function execFirecrawl(params: UrlParams, ctx: ExecCtx): Promise<AssetRef> {\n const result = await callBackendExec({\n nodeType: \"ingest\",\n nodeVersion: ingestNode.version,\n params: { strategy: \"firecrawl\", url: params.url },\n inputs: {},\n outputKinds: { asset: \"text\" },\n ctx,\n });\n return assertAssetOutput(result, \"text\");\n}\n\nfunction assertAssetOutput(result: Record<string, unknown>, expected: AssetKind): AssetRef {\n const asset = result.asset as AssetRef | undefined;\n if (!asset || typeof asset !== \"object\" || !(\"kind\" in asset)) {\n throw new Error(`ingest: backend returned no asset (got ${JSON.stringify(result)})`);\n }\n if (asset.kind !== expected) {\n throw new Error(`ingest: backend returned kind=${asset.kind} but expected ${expected}`);\n }\n return asset;\n}\n\n// ---------------------------------------------------------------------------\n// local_file — read from disk, sniff mime, upload to R2 via presign\n// ---------------------------------------------------------------------------\n\nfunction resolveLocalPath(input: string): string {\n if (input.startsWith(\"~\")) {\n throw new Error(\n `ingest: ~ path expansion is not supported (got \"${input}\"). Use an absolute path or a cwd-relative path.`,\n );\n }\n if (path.isAbsolute(input)) return input;\n return path.resolve(process.cwd(), input);\n}\n\nconst EXT_TO_MIME: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n // Non-model-safe rasters `toModelSafeImage` transcodes to PNG at ingest — they\n // must resolve to an image mime here or the kind-check rejects the local file\n // before normalization ever runs.\n avif: \"image/avif\",\n heic: \"image/heic\",\n heif: \"image/heif\",\n tif: \"image/tiff\",\n tiff: \"image/tiff\",\n bmp: \"image/bmp\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n m4v: \"video/mp4\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n m4a: \"audio/mp4\",\n ogg: \"audio/ogg\",\n flac: \"audio/flac\",\n json: \"application/json\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n markdown: \"text/markdown\",\n html: \"text/html\",\n htm: \"text/html\",\n csv: \"text/csv\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n woff: \"font/woff\",\n woff2: \"font/woff2\",\n svg: \"image/svg+xml\",\n};\n\nconst SVG_MIME = \"image/svg+xml\";\n\n// Crispness target for rasterized SVGs. SVGs (brand logos especially) are often authored at small\n// intrinsic sizes — sharp rasterizes at the intrinsic size unless we raise the render density, which\n// would leave a tiny, blurry logo. We measure the intrinsic size and scale the density so the longest\n// edge lands near SVG_TARGET_LONGEST_PX, clamped so a pathological viewBox can't blow up memory.\nconst SVG_TARGET_LONGEST_PX = 2048;\nconst SVG_BASE_DENSITY = 72;\nconst SVG_MAX_DENSITY = 2400;\n\nfunction sniffSvg(buf: Buffer): boolean {\n const head = buf.subarray(0, Math.min(buf.length, 512)).toString(\"utf8\").trimStart();\n return head.startsWith(\"<?xml\") ? head.includes(\"<svg\") : head.startsWith(\"<svg\");\n}\n\nexport function inferMimeFromPath(absPath: string, sniffBytes: Buffer): string {\n const ext = path.extname(absPath).slice(1).toLowerCase();\n const fromExt = EXT_TO_MIME[ext];\n if (fromExt) return fromExt;\n const fromBytes = sniffImageMime(sniffBytes);\n if (fromBytes) return fromBytes;\n if (sniffSvg(sniffBytes)) return SVG_MIME;\n return \"application/octet-stream\";\n}\n\n// Image-generation models can't consume raw SVG markup, so an ingested SVG (e.g. a brand logo) is\n// rasterized to a transparent PNG here. Transparency is kept so the wordmark composites cleanly onto\n// any background downstream.\nexport async function rasterizeSvgToPng(bytes: Buffer): Promise<Buffer> {\n // `sharp` is a native module that can't load in the Convex runtime. This function only runs in the\n // local execLocalFile path (the ingest node is location: \"local\"), so import it lazily — a top-level\n // import would drag the native binary into the Convex bundle and break `convex deploy`.\n const { default: sharp } = await import(\"sharp\");\n const base = await sharp(bytes).metadata();\n const longest = Math.max(base.width ?? 0, base.height ?? 0);\n let density = SVG_BASE_DENSITY;\n if (longest > 0) {\n const scaled = Math.round((SVG_BASE_DENSITY * SVG_TARGET_LONGEST_PX) / longest);\n density = Math.min(SVG_MAX_DENSITY, Math.max(SVG_BASE_DENSITY, scaled));\n }\n return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();\n}\n\n// Image-generation providers (OpenAI, Google, Recraft via OpenRouter) only accept these input\n// formats; a reference in any other format 400s the entire generation with a non-retryable\n// \"not a valid image format\" error.\nconst MODEL_SAFE_IMAGE_MIMES = new Set([\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"]);\n\nexport type ModelSafeImage = {\n bytes: Buffer;\n mime: string;\n rasterizedFrom?: string;\n /** Set when the reference had to be re-encoded or scaled down to stay downloadable. */\n shrunkFromBytes?: number;\n};\n\n/**\n * The largest reference a generation provider will fetch. OpenRouter refuses anything above this\n * with `413 Downloaded image content cannot exceed 30MB` — and it refuses it at *generation* time,\n * so the cost of exceeding it is a dead node in the middle of a render, not a failed upload.\n *\n * The ceiling is easy to cross by accident because the transcode below is lossless: a 2.7MB AVIF\n * photo out of a media library is a 4480x6720 frame, and as PNG that is 57MB. Two clients lost\n * `image_reference_sheet` nodes to exactly that on consecutive days.\n */\nexport const MAX_MODEL_REFERENCE_BYTES = 30 * 1024 * 1024;\n\n/**\n * Bring an already-model-safe raster under `maxBytes`, re-encoding as little as possible.\n *\n * Order matters. The first pass re-encodes at full resolution into a lossy format, because lossless\n * PNG is what made the image oversized and q90 is visually indistinguishable to a model that\n * downsamples the reference anyway — a 57MB PNG lands around 5MB with every pixel of detail still\n * there. Opaque frames go to JPEG; anything with alpha or more than one frame goes to WebP, which is\n * already model-safe and keeps both. Only when that is still not enough does it scale the frame\n * down, which is the step that actually loses information.\n */\nasync function fitWithinReferenceLimit(\n bytes: Buffer,\n mime: string,\n maxBytes: number,\n): Promise<{ bytes: Buffer; mime: string }> {\n if (bytes.byteLength <= maxBytes) return { bytes, mime };\n\n const { default: sharp } = await import(\"sharp\");\n // Always decode every page: an oversized GIF or WebP is almost certainly animated, and reading it\n // frame-at-a-time would silently turn the sequence into a still. A single-page image reads the\n // same either way.\n const read = () => sharp(bytes, { animated: true });\n const meta = await read().metadata();\n const animated = (meta.pages ?? 1) > 1;\n const keepAlpha = meta.hasAlpha === true;\n const width = meta.width ?? 0;\n\n const encode = async (scale: number): Promise<{ bytes: Buffer; mime: string }> => {\n let pipeline = read();\n if (scale < 1 && width > 0) pipeline = pipeline.resize({ width: Math.max(256, Math.round(width * scale)) });\n if (animated && mime === \"image/gif\") return { bytes: await pipeline.gif().toBuffer(), mime: \"image/gif\" };\n if (animated || keepAlpha) return { bytes: await pipeline.webp({ quality: 90 }).toBuffer(), mime: \"image/webp\" };\n return { bytes: await pipeline.jpeg({ quality: 90, mozjpeg: true }).toBuffer(), mime: \"image/jpeg\" };\n };\n\n let out = await encode(1);\n let scale = 1;\n // Each pass aims straight at the limit (bytes scale with area, so the side scales with its square\n // root) but is capped at 0.9 so a badly-predicted pass still makes progress. Four passes take any\n // real photograph under 30MB; the bound is there so a pathological frame can't loop.\n for (let pass = 0; pass < 4 && out.bytes.byteLength > maxBytes; pass++) {\n scale *= Math.min(0.9, Math.sqrt(maxBytes / out.bytes.byteLength));\n out = await encode(scale);\n }\n return out;\n}\n\n/**\n * Normalize image bytes into something every image-generation model accepts AND every provider can\n * actually download. Model-safe rasters (jpeg/png/gif/webp) pass through untouched — no re-encode,\n * so quality and animation are preserved. Everything else (SVG, AVIF, HEIC, TIFF, BMP, …) is\n * transcoded to PNG so a downstream reference can never poison a generation. SVG gets the\n * density-aware rasterization (small logos upscaled, alpha kept); other formats go straight through\n * sharp. Whatever comes out of that, oversized bytes are then fitted under `maxBytes`. Throws when\n * the bytes aren't a decodable image at all, so a mis-`expect`ed ingest fails clearly rather than\n * uploading garbage.\n *\n * This lives in the CLI, not the backend, because sharp can't load in the Convex runtime — so the\n * `source: \"url\"` path (backend `direct_fetch`) can't normalize and must route images through here.\n */\nexport async function toModelSafeImage(bytes: Buffer, opts?: { maxBytes?: number }): Promise<ModelSafeImage> {\n const maxBytes = opts?.maxBytes ?? MAX_MODEL_REFERENCE_BYTES;\n const fit = async (candidate: Buffer, mime: string, rasterizedFrom?: string): Promise<ModelSafeImage> => {\n const fitted = await fitWithinReferenceLimit(candidate, mime, maxBytes);\n return {\n bytes: fitted.bytes,\n mime: fitted.mime,\n ...(rasterizedFrom ? { rasterizedFrom } : {}),\n ...(fitted.bytes === candidate ? {} : { shrunkFromBytes: candidate.byteLength }),\n };\n };\n\n const safe = sniffImageMime(bytes);\n if (safe && MODEL_SAFE_IMAGE_MIMES.has(safe)) {\n return await fit(bytes, safe);\n }\n if (sniffSvg(bytes)) {\n return await fit(await rasterizeSvgToPng(bytes), \"image/png\", \"svg\");\n }\n const { default: sharp } = await import(\"sharp\");\n let png: Buffer;\n let format: string;\n try {\n const img = sharp(bytes);\n format = (await img.metadata()).format ?? \"unknown\";\n png = await img.png({ force: true }).toBuffer();\n } catch (e) {\n throw new Error(`bytes are not a decodable image (${(e as Error).message})`);\n }\n return await fit(png, \"image/png\", format);\n}\n\n/**\n * One run-log line when normalization changed the bytes — format, size, or both. A silent re-encode\n * is the kind of thing someone later has to explain, so both halves say what they did and why.\n */\nfunction normalizationNote(image: ModelSafeImage): string | null {\n const parts: string[] = [];\n if (image.rasterizedFrom) parts.push(`normalized ${image.rasterizedFrom} -> ${image.mime}`);\n if (image.shrunkFromBytes !== undefined) {\n parts.push(\n `fitted ${image.shrunkFromBytes}B -> ${image.bytes.length}B as ${image.mime} (providers refuse a reference over ${MAX_MODEL_REFERENCE_BYTES}B)`,\n );\n }\n return parts.length > 0 ? parts.join(\"; \") : null;\n}\n\n/** Whether `buf` carries the exact ASCII string `sig` starting at `offset`. */\nfunction hasAscii(buf: Buffer, offset: number, sig: string): boolean {\n return buf.length >= offset + sig.length && buf.toString(\"ascii\", offset, offset + sig.length) === sig;\n}\n\nconst HEIC_BRANDS = new Set([\"heic\", \"heix\", \"heim\", \"heis\", \"hevc\", \"hevx\", \"mif1\", \"msf1\", \"heif\"]);\n\n/** ISO-BMFF `ftyp` box (bytes 4..8) — the brand at 8..12 splits AVIF from HEIC/HEIF. */\nfunction sniffIsoBmff(buf: Buffer): string | null {\n if (!hasAscii(buf, 4, \"ftyp\")) return null;\n const brand = buf.subarray(8, 12).toString(\"ascii\");\n if (brand === \"avif\" || brand === \"avis\") return \"image/avif\";\n if (HEIC_BRANDS.has(brand)) return \"image/heic\";\n return null;\n}\n\nfunction sniffImageMime(buf: Buffer): string | null {\n if (buf.length < 4) return null;\n if (buf[0] === 0x89 && hasAscii(buf, 1, \"PNG\")) return \"image/png\";\n if (buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) return \"image/jpeg\";\n if (hasAscii(buf, 0, \"GIF\")) return \"image/gif\";\n if (hasAscii(buf, 0, \"RIFF\") && hasAscii(buf, 8, \"WEBP\")) return \"image/webp\";\n // TIFF: \"II*\\0\" (little-endian) or \"MM\\0*\" (big-endian).\n if (hasAscii(buf, 0, \"II*\\0\") || hasAscii(buf, 0, \"MM\\0*\")) return \"image/tiff\";\n if (hasAscii(buf, 0, \"BM\")) return \"image/bmp\";\n // Covers HEIC/HEIF/AVIF whose extension is wrong or missing.\n return sniffIsoBmff(buf);\n}\n\n// ISO-BMFF (mp4 / mov / m4v) duration probe. Pure, dependency-free, and runs on\n// the bytes already in memory — so a video ingest can stamp the canonical\n// `VideoRef.duration_ms` without ffprobe (which the engine does not require on\n// PATH). Walks top-level boxes for `moov`, then `mvhd` inside it, and reads the\n// timescale + duration. Returns undefined for anything it can't read (webm, a\n// fragmented/duration-less moov, a truncated header) so the caller degrades to\n// \"duration unknown\" rather than failing the ingest.\n//\n// Box layout: [size:u32 BE][type:ascii4][payload]. size===1 → a u64 `largesize`\n// follows the type; size===0 → the box runs to the end of its parent.\nfunction findBoxPayload(buf: Buffer, start: number, end: number, type: string): { start: number; end: number } | null {\n let offset = start;\n while (offset + 8 <= end) {\n let size = buf.readUInt32BE(offset);\n const boxType = buf.toString(\"ascii\", offset + 4, offset + 8);\n let headerLen = 8;\n if (size === 1) {\n if (offset + 16 > end) return null;\n // largesize is u64; durations here never need >2^53, so Number is safe.\n size = Number(buf.readBigUInt64BE(offset + 8));\n headerLen = 16;\n } else if (size === 0) {\n size = end - offset;\n }\n if (size < headerLen || offset + size > end) return null;\n if (boxType === type) return { start: offset + headerLen, end: offset + size };\n offset += size;\n }\n return null;\n}\n\nconst MVHD_DURATION_UNKNOWN = 0xffffffff;\n\nfunction mp4DurationMs(bytes: Buffer | Uint8Array): number | undefined {\n const buf = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes);\n const moov = findBoxPayload(buf, 0, buf.length, \"moov\");\n if (!moov) return undefined;\n const mvhd = findBoxPayload(buf, moov.start, moov.end, \"mvhd\");\n if (!mvhd) return undefined;\n const version = buf[mvhd.start];\n // version byte (1) + flags (3), then v0: creation(4)+modification(4); v1: (8)+(8).\n const tsOffset = mvhd.start + 4 + (version === 1 ? 16 : 8);\n if (version === 1) {\n if (tsOffset + 12 > mvhd.end) return undefined;\n const timescale = buf.readUInt32BE(tsOffset);\n const duration = buf.readBigUInt64BE(tsOffset + 4);\n if (timescale === 0 || duration === 0n) return undefined;\n return Math.round((Number(duration) / timescale) * 1000);\n }\n if (version !== 0) return undefined;\n if (tsOffset + 8 > mvhd.end) return undefined;\n const timescale = buf.readUInt32BE(tsOffset);\n const duration = buf.readUInt32BE(tsOffset + 4);\n if (timescale === 0 || duration === 0 || duration === MVHD_DURATION_UNKNOWN) return undefined;\n return Math.round((duration / timescale) * 1000);\n}\n\nfunction inferKindFromMime(mime: string): AssetKind | null {\n if (mime.startsWith(\"image/\")) return \"image\";\n if (mime.startsWith(\"video/\")) return \"video\";\n if (mime.startsWith(\"audio/\")) return \"audio\";\n if (mime === \"application/json\") return \"json\";\n if (mime.startsWith(\"text/\")) return \"text\";\n if (mime.startsWith(\"font/\")) return \"font\";\n return null;\n}\n\nfunction localExecError(ctx: ExecCtx, message: string): NodeExecutionError {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"local\",\n cause: new Error(`ingest: ${message}`),\n });\n}\n\nasync function execLocalFile(params: PathParams, ctx: ExecCtx): Promise<AssetRef> {\n const absPath = resolveLocalPath(params.path);\n\n let stats: Stats;\n try {\n stats = await stat(absPath);\n } catch (e) {\n const code = (e as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n throw localExecError(ctx, `file_not_found: ${absPath}`);\n }\n if (code === \"EACCES\") {\n throw localExecError(ctx, `permission_denied: ${absPath}`);\n }\n throw localExecError(ctx, `stat failed for ${absPath}: ${(e as Error).message}`);\n }\n\n if (!stats.isFile()) {\n throw localExecError(ctx, `not_a_file: ${absPath} (isDirectory=${stats.isDirectory()})`);\n }\n if (stats.size > MAX_ASSET_BYTES) {\n throw localExecError(ctx, `file_too_large: ${absPath} is ${stats.size} bytes (limit ${MAX_ASSET_BYTES})`);\n }\n\n let bytes: Buffer;\n try {\n bytes = await readFile(absPath);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"EACCES\") {\n throw localExecError(ctx, `permission_denied: ${absPath}`);\n }\n throw localExecError(ctx, `read failed for ${absPath}: ${(e as Error).message}`);\n }\n\n const mime = inferMimeFromPath(absPath, bytes);\n const kind = inferKindFromMime(mime);\n\n if (kind === null) {\n throw localExecError(\n ctx,\n `kind_mismatch: ${absPath} has mime=${mime} which doesn't map to any ingest kind (image/video/audio/text/json/font). If this is a PDF and you want text extraction, host it via URL and use source=\"url\".`,\n );\n }\n if (kind !== params.expect) {\n throw localExecError(ctx, `kind_mismatch: expect=${params.expect} but ${absPath} has mime=${mime} (kind=${kind})`);\n }\n\n ctx.log(`ingest: local file ${stats.size}B mime=${mime}`);\n\n // Normalize any image to a model-safe raster before upload — image-generation models reject SVG,\n // AVIF, HEIC, TIFF, BMP (they read only jpeg/png/gif/webp). The kind check above guarantees\n // expect === \"image\" here; non-image kinds upload verbatim.\n let outBytes = bytes;\n let outMime = mime;\n let rasterizedFrom: string | undefined;\n if (kind === \"image\") {\n const normalized = await toModelSafeImage(bytes);\n outBytes = normalized.bytes;\n outMime = normalized.mime;\n rasterizedFrom = normalized.rasterizedFrom;\n const note = normalizationNote(normalized);\n if (note) ctx.log(`ingest: ${note}`);\n }\n\n const durationMs = probeVideoDurationMs(params.expect, outBytes, ctx);\n const ref = await uploadAndIngest({\n bytes: outBytes,\n kind: params.expect,\n mime: outMime,\n metadata: localFileMetadata({ absPath, fileSize: stats.size, mime, durationMs, rasterizedFrom }),\n ctx,\n });\n return withProbedDuration(ref, durationMs);\n}\n\n/**\n * Probe a video's duration from its ISO-BMFF header so downstream nodes (notably\n * video_deconstruct, which sizes its Mux ingest-poll timeout and preflight off\n * the declared duration) don't fall back to their worst-case budgets. Best-effort:\n * undefined for non-video kinds and non-mp4 containers (e.g. webm).\n */\nfunction probeVideoDurationMs(expect: AssetKind, bytes: Buffer, ctx: ExecCtx): number | undefined {\n if (expect !== \"video\") return undefined;\n const durationMs = mp4DurationMs(bytes);\n if (durationMs !== undefined) ctx.log(`ingest: probed video duration ${durationMs}ms`);\n return durationMs;\n}\n\nfunction localFileMetadata(args: {\n absPath: string;\n fileSize: number;\n mime: string;\n durationMs: number | undefined;\n rasterizedFrom: string | undefined;\n}): Record<string, unknown> {\n return {\n source_path: args.absPath,\n strategy: \"local_file\",\n ingested_at: new Date().toISOString(),\n file_size: args.fileSize,\n original_filename: path.basename(args.absPath),\n ...(args.rasterizedFrom ? { rasterized_from: args.rasterizedFrom } : {}),\n ...(args.durationMs !== undefined ? { duration_ms: args.durationMs } : {}),\n };\n}\n\n/**\n * Stamp the canonical top-level VideoRef.duration_ms when known. Consumers read\n * either the field or metadata.duration_ms, so populating both leaves neither\n * convention missing it.\n */\nfunction withProbedDuration(ref: AssetRef, durationMs: number | undefined): AssetRef {\n if (durationMs === undefined || ref.kind !== \"video\") return ref;\n return { ...ref, duration_ms: durationMs };\n}\n\n// ---------------------------------------------------------------------------\n// yt_dlp — runs locally, then uploads to R2 via presign so remote downstream\n// nodes can fetch the asset by URL.\n// ---------------------------------------------------------------------------\n\nconst YT_DLP_MIME: Record<AssetKind, string> = {\n image: \"image/png\",\n video: \"video/mp4\",\n audio: \"audio/mp4\",\n text: \"text/plain\",\n json: \"application/json\",\n font: \"font/ttf\", // unreachable — resolveStrategy never routes font to yt_dlp\n};\n\nconst YT_DLP_EXT: Record<AssetKind, string> = {\n image: \"png\",\n video: \"mp4\",\n audio: \"m4a\",\n text: \"txt\",\n json: \"json\",\n font: \"ttf\", // unreachable — resolveStrategy never routes font to yt_dlp\n};\n\nasync function execYtDlp(params: UrlParams, ctx: ExecCtx): Promise<AssetRef> {\n if (params.expect !== \"video\" && params.expect !== \"audio\") {\n // resolveStrategy guarantees this, but the runtime branch makes it explicit.\n throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);\n }\n\n const workDir = await mkdtemp(path.join(tmpdir(), \"ingest-yt-\"));\n try {\n const { filePath, info, reducedQuality } = await runYtDlpForNode({\n url: params.url,\n audioOnly: params.expect === \"audio\",\n workDir,\n ctx,\n });\n\n const downloadedStats = await stat(filePath);\n if (downloadedStats.size > MAX_ASSET_BYTES) {\n throw localExecError(\n ctx,\n `file_too_large: yt-dlp output for ${params.url} is ${downloadedStats.size} bytes (limit ${MAX_ASSET_BYTES})`,\n );\n }\n const bytes = await readFile(filePath);\n const kind = params.expect;\n const mime = YT_DLP_MIME[kind];\n const metadata = buildYtDlpMetadata(params.url, info);\n\n // The shared runner can only get some sources by asking a fallback player\n // for a lower-resolution stream. That is worth having and must not be\n // silent: a canvas built on a 360p asset looks like a canvas built on a\n // master until it is rendered, so the fact travels on the asset itself and\n // through the run log, as it does for `baker videos ingest`.\n if (reducedQuality) {\n metadata.reduced_quality = true;\n ctx.log(`ingest: ${params.url} was only available at reduced resolution`);\n }\n\n return await uploadAndIngest({ bytes, kind, mime, metadata, ctx });\n } finally {\n await rm(workDir, { recursive: true, force: true }).catch((e) => {\n ctx.log(`ingest: tmp cleanup failed (${(e as Error).message})`);\n });\n }\n}\n\n/**\n * The shared runner, with failures re-expressed as this node's error type.\n *\n * Everything about *how* yt-dlp is invoked — the ladder, the proxy environment,\n * the block signal — lives in `lib/ytDlp.ts` and is shared with\n * `baker videos ingest`, so the two cannot drift on what \"blocked\" means or on\n * how a credential is written into a proxy URL.\n */\nasync function runYtDlpForNode(args: {\n url: string;\n audioOnly: boolean;\n workDir: string;\n ctx: ExecCtx;\n}): Promise<{ filePath: string; info: YtDlpInfo; reducedQuality: boolean }> {\n try {\n return await runYtDlp({\n url: args.url,\n audioOnly: args.audioOnly,\n workDir: args.workDir,\n onLog: (message) => args.ctx.log(`ingest: ${message}`),\n });\n } catch (error) {\n if (error instanceof YtDlpError) {\n throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {\n kind: \"local\",\n cause: new Error(describeYtDlpFailure(error.message, error.stderrTail)),\n });\n }\n throw error;\n }\n}\n\n/**\n * yt-dlp's failure, plus what to do when it is TLS. A trust failure is this machine's, not the\n * file's, and the one copy within reach — the engine's cache under `canvas/` — is the wrong one: a\n * canvas wired to it renders here and fails every later Session's publish.\n */\nexport function describeYtDlpFailure(message: string, stderrTail: string): string {\n const base = stderrTail ? `${message}\\n${stderrTail}` : message;\n if (!/CERTIFICATE_VERIFY_FAILED|SSL certificate problem/i.test(stderrTail)) return base;\n return `${base}\\nThis machine could not verify the download's TLS certificate; the file itself is fine. Report it and leave this ingest as it is. Never copy the file out of canvas/ (the render cache or a run folder) to work around it: canvas/ is not committed, so the creative would fail every later publish. To reuse one of our own outputs, ingest its canvas-assets URL, which does not go through this downloader.`;\n}\n\nfunction buildYtDlpMetadata(sourceUrl: string, info: YtDlpInfo): Record<string, unknown> {\n const out: Record<string, unknown> = {\n source_url: sourceUrl,\n strategy: \"yt_dlp\",\n ingested_at: new Date().toISOString(),\n };\n if (typeof info.title === \"string\") out.original_title = info.title;\n if (typeof info.uploader === \"string\") out.original_uploader = info.uploader;\n if (typeof info.duration === \"number\") out.original_duration_s = info.duration;\n if (typeof info.webpage_url === \"string\") out.webpage_url = info.webpage_url;\n if (typeof info.width === \"number\") out.width = info.width;\n if (typeof info.height === \"number\") out.height = info.height;\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// R2 upload + local ingest\n// ---------------------------------------------------------------------------\n\nexport async function uploadAndIngest(args: {\n bytes: Buffer;\n kind: AssetKind;\n mime: string;\n metadata: Record<string, unknown>;\n ctx: ExecCtx;\n}): Promise<AssetRef> {\n const sha = sha256Hex(args.bytes);\n const publicUrl = await presignAndPut({ bytes: args.bytes, sha256: sha, mime: args.mime, ctx: args.ctx });\n\n const localRef = await args.ctx.assets.ingestBytes({\n bytes: args.bytes,\n kind: args.kind,\n mime: args.mime,\n metadata: args.metadata,\n });\n // ingestBytes leaves url undefined (LocalAssetStore is local-only). Overlay\n // the public R2 URL so remote downstream nodes can pull this asset.\n return { ...localRef, url: publicUrl } as AssetRef;\n}\n\n// ---------------------------------------------------------------------------\n// Spawn helper (local copy — cli-runner.ts rejects URL args by policy, which\n// directly conflicts with yt-dlp's job)\n// ---------------------------------------------------------------------------\n\n// Fold yt-dlp version into the cache key so upgrades bust cache automatically.\nasync function ytDlpVersion(): Promise<string> {\n try {\n const { stdout } = await execFile(YT_DLP_BIN, [\"--version\"], { encoding: \"utf-8\", maxBuffer: 64 * 1024 });\n return stdout.trim();\n } catch {\n return \"yt-dlp:unknown\";\n }\n}\n\n// Attach cacheKeyExtras after definition so the helper can be referenced.\ningestNode.cacheKeyExtras = async ({ params }): Promise<Record<string, string>> => {\n const p = params as { source?: unknown; url?: unknown; path?: unknown; expect?: unknown };\n const extras: Record<string, string> = {};\n\n // For source=path: fold mtime+size into the cache key so editing the file\n // invalidates the cache automatically. Failures (missing file, perms) are\n // silently swallowed — execute will surface them with full context.\n if (p.source === \"path\" && typeof p.path === \"string\") {\n try {\n const abs = resolveLocalPath(p.path);\n const s = await stat(abs);\n extras.local_file_signature = `${s.mtimeMs}:${s.size}`;\n } catch {\n // ignore — execute will fail clearly\n }\n }\n\n // For source=url with yt_dlp strategy: fold tool version so upgrades bust cache.\n if (\n p.source === \"url\" &&\n (p.expect === \"video\" || p.expect === \"audio\") &&\n !(typeof p.url === \"string\" && takesCanvasAssetVerbatim(p.url, p.expect))\n ) {\n extras.yt_dlp_version = await ytDlpVersion();\n }\n\n return extras;\n};\n\n// Exported for unit tests.\nexport const _internal = {\n resolveStrategy,\n resolveLocalPath,\n inferMimeFromPath,\n inferKindFromMime,\n mp4DurationMs,\n buildYtDlpMetadata,\n YT_DLP_EXT,\n YT_DLP_MIME,\n EXT_TO_MIME,\n};\n","/**\n * `fetch()` that climbs the ladder when — and only when — it is refused.\n *\n * This is the `undici` half of the pair; `preflight.ts` is the Playwright half's\n * safety net. It is a **subpath export** and is deliberately NOT re-exported\n * from `src/index.ts`, for the same reason `./preflight` is not: the barrel is\n * reachable from Convex's V8 runtime through `lib/env.ts`, and `undici` pulls in\n * Node built-ins that fail that bundle outright. Only a `\"use node\"` module may\n * import `@baker/proxy/fetch`.\n *\n * Three properties are worth stating up front, because each is a decision:\n *\n * - **The dispatcher is per request.** Installing a global one would silently\n * route every unrelated `fetch` in the process — provider APIs included —\n * through a metered exit, which is both a bill and a good way to trip a WAF\n * that has never seen us arrive from Brazil before.\n * - **`undici`'s own `fetch` is used, not the global one.** Node's global fetch\n * is powered by Node's *internal* copy of undici; handing it a `ProxyAgent`\n * built by the userland copy hands it a dispatcher speaking a different\n * handler protocol. Same package name, two module registries.\n * - **The body is read here and re-wrapped.** A proxied response is only alive\n * while its agent is, and the agent has to be closed or the sockets leak; and\n * a wall page cannot be recognised without reading it. Buffering once solves\n * both, at the cost of not supporting streaming callers — none of which exist\n * on the paths this serves, all of which call `arrayBuffer()` immediately.\n */\n\nimport { lookup as dnsLookup } from \"node:dns\";\nimport type { RequestInit } from \"undici\";\nimport { Agent, ProxyAgent, Response, fetch as undiciFetch } from \"undici\";\nimport { isChallengeBody } from \"./challenge.ts\";\nimport type { ProxyCredentials } from \"./credentials.ts\";\nimport type { FetchAttempt } from \"./fetchDecision.ts\";\nimport { classifyFetchOutcome, errorBlockSignal, shouldSniffChallenge, shouldTryNextRoute } from \"./fetchDecision.ts\";\nimport type { TriedRoute } from \"./ladder.ts\";\nimport { nextPlannedRoute, plannedRoutes } from \"./ladder.ts\";\nimport { isPrivateAddress } from \"./privateAddress.ts\";\nimport { refuseNonPublicUrl } from \"./publicAddress.ts\";\nimport type { ProxyRoute, RouteOptions } from \"./route.ts\";\nimport type { ProxyTier } from \"./tiers.ts\";\n\nexport type { FetchAttempt, FetchOutcome } from \"./fetchDecision.ts\";\n\nexport interface EscalatedFetchResult {\n /** The origin's answer, from whichever rung produced one. */\n readonly response: Response | null;\n /** Every rung tried, in order, so the caller can log the whole ladder. */\n readonly attempts: FetchAttempt[];\n /** Which rung produced `response`, or the last one attempted. */\n readonly finalTier: \"direct\" | ProxyTier;\n /** The last thrown error, when no rung produced a response at all. */\n readonly error?: unknown;\n}\n\nexport interface EscalatedFetchOptions {\n readonly credentials: ProxyCredentials;\n /** Country / sticky-session targeting, passed through to the route builder. */\n readonly routeOptions?: RouteOptions;\n /** Per-attempt budget. Each rung gets its own; the ladder is not one clock. */\n readonly timeoutMs?: number;\n /**\n * Read small text bodies looking for a bot wall behind a 200. On by default\n * because that block is invisible from the status line. Turn it off only\n * where a 200 is definitionally the payload.\n */\n readonly sniffChallenge?: boolean;\n /**\n * Refuse to buffer a body that *declares* itself larger than this.\n *\n * Because the body is read here, a caller's own size cap would otherwise fire\n * only after the whole file had already been pulled into memory — the exact\n * download the cap exists to avoid. The response still carries the original\n * `content-length`, so the caller's check is the one that reports it.\n */\n readonly maxBytes?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 60_000;\n/**\n * Above this, a body is the thing we asked for, not a wall. Generous next to\n * the 2 KB ceiling the detector itself applies, so a padded challenge page is\n * still caught while a real download is never decoded into a string.\n */\nconst MAX_SNIFF_BYTES = 64 * 1024;\n\nfunction tierOf(route: ProxyRoute): \"direct\" | ProxyTier {\n return route.kind === \"proxy\" ? route.tier : \"direct\";\n}\n\n/**\n * Credentials go in a header we build once, never into the URL.\n *\n * `new ProxyAgent(\"http://user:pass@host\")` would work and is how most examples\n * write it. It also means the password exists as a substring of a URL that any\n * error, log line or stack frame is free to print.\n */\nfunction agentFor(route: ProxyRoute): ProxyAgent | Agent | null {\n if (route.kind !== \"proxy\") return directAgent();\n const token = Buffer.from(`${route.username}:${route.password}`).toString(\"base64\");\n return new ProxyAgent({ uri: route.server, token: `Basic ${token}` });\n}\n\n/**\n * The direct rung, with DNS held to the same rule as the hostname was.\n *\n * `refuseNonPublicUrl` reads the hostname, and a hostname is not an address:\n * `localtest.me` is a public name with a public DNS record pointing at\n * `127.0.0.1`, and nothing stops anyone pointing a domain they own at\n * `169.254.169.254`. Before this, that walked straight past the guard and\n * fetched from loopback.\n *\n * Doing it in `lookup` rather than by resolving first and re-checking closes the\n * gap between the two: undici connects to the address this callback returned, so\n * there is no window in which the record can change to a private one after being\n * approved.\n *\n * Only the direct rung needs it. A proxied request is resolved by the proxy, at\n * the far end of the tunnel — and a private address never gets a proxy route\n * built for it in the first place.\n */\nfunction directAgent(): Agent {\n return new Agent({\n connect: {\n lookup: (hostname, options, callback) => {\n dnsLookup(hostname, options, (error, address, family) => {\n if (error) {\n callback(error, address as string, family as number);\n return;\n }\n const found = Array.isArray(address) ? address.map((entry) => entry.address) : [address];\n const blocked = found.find((entry) => isPrivateAddress(entry));\n if (blocked !== undefined) {\n callback(new Error(`${PRIVATE_ADDRESS_CODE}: ${hostname} resolves to ${blocked}`), \"\", 4);\n return;\n }\n callback(null, address as string, family as number);\n });\n },\n },\n });\n}\n\n/**\n * Marker in the thrown message, so a refusal is not read as a dead host.\n *\n * It travels as text because undici surfaces a `lookup` failure as an ordinary\n * connection error, and there is no field on it we own.\n */\nexport const PRIVATE_ADDRESS_CODE = \"EPRIVATEADDR\";\n\n/** Hops to allow before calling it a loop. Matches what browsers do. */\nconst MAX_REDIRECTS = 20;\nconst REDIRECT_STATUS = new Set([301, 302, 303, 307, 308]);\n\nfunction originOf(url: string): string | null {\n try {\n return new URL(url).origin;\n } catch {\n return null;\n }\n}\n\n/**\n * Whether this hop is a host walking us somewhere we would not have gone.\n *\n * The rule is: a redirect may not land anywhere we would have refused to start,\n * with one exception — the origin we deliberately chose. `entry` is the URL the\n * caller handed in, not the previous hop, so a chain cannot launder itself one\n * step at a time.\n *\n * The previous version compared each hop against the one before it and refused\n * only the *public → private* transition. That is sound as an induction and it\n * was still the wrong rule, for a reason worth keeping: every socket test in\n * this package serves from `127.0.0.1`, so the clause that asks whether we\n * started public was false in all of them and the guard could not fire once. The\n * two fixtures written for it sat in the file unreferenced. A guard whose only\n * possible test cannot reach it is indistinguishable from no guard, and this one\n * had already shipped once returning the metadata body with `outcome: \"ok\"`.\n *\n * Anchoring on the entry origin costs nothing in production — all four consumers\n * refuse a private entry URL, so the two rules never disagree there — and it\n * buys the loopback harness the ability to prove the refusal end to end.\n */\nexport function refuseRedirect(entry: string, to: string): boolean {\n if (refuseNonPublicUrl(to) === null) return false;\n const destination = originOf(to);\n return destination === null || destination !== originOf(entry);\n}\n\n/**\n * Follow redirects ourselves, so every hop is checked and not just the first.\n *\n * `undici` follows them for us by default, and that is the problem: the callers\n * validate the URL they were handed and have no way to see where it went. A\n * public origin answering `302 Location: http://127.0.0.1:…` therefore produced\n * `outcome: \"ok\"` and the loopback body — confirmed against a real public\n * redirector. This is also the hole the `lookup` guard cannot cover, since a\n * hop to an address *literal* never asks DNS anything.\n *\n * Per the fetch spec, 303 becomes a GET, and so does a 301/302 that was a POST.\n */\nasync function followRedirects(url: string, init: RequestInit): Promise<Response> {\n let current = url;\n let request: RequestInit = { ...init, redirect: \"manual\" };\n\n for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {\n const response = await undiciFetch(current, request);\n const location = response.headers.get(\"location\");\n if (!REDIRECT_STATUS.has(response.status) || location === null) return response;\n\n // Nobody will ever read this body, but it is still on the wire, and the\n // dispatcher is not done with the connection until it is drained. The cost\n // lands somewhere unobvious: the response arrives and is correct, and then\n // `agent.close()` in `attemptRoute`'s `finally` sits on the undrained socket\n // until the rung's own AbortSignal fires and releases it. Measured through\n // `fetchWithEscalation` with a 3 s budget — a 64 KB redirect body took\n // 3007 ms, and 5 ms once cancelled. With the 60 s default that is a minute\n // of an image fetch spent inside a Convex action on a body we discarded.\n //\n // Cancel before the refusal check below, so a refused hop releases it too.\n await response.body?.cancel();\n\n // Resolved against the URL that issued it, so a relative `Location` works.\n const next = new URL(location, current).toString();\n if (refuseRedirect(url, next)) {\n throw new Error(`${PRIVATE_ADDRESS_CODE}: redirected to ${next}`);\n }\n const method = (request.method ?? \"GET\").toUpperCase();\n const downgrade =\n response.status === 303 || ((response.status === 301 || response.status === 302) && method === \"POST\");\n request = downgrade ? { ...request, method: \"GET\", body: undefined } : request;\n current = next;\n }\n throw new Error(`too many redirects following ${url}`);\n}\n\n/** Statuses the `Response` constructor refuses to attach a body to. */\nconst NULL_BODY_STATUS = new Set([101, 103, 204, 205, 304]);\n\nfunction rebuild(response: Response, body: ArrayBuffer): Response {\n if (response.status < 200 || response.status > 599 || NULL_BODY_STATUS.has(response.status)) {\n return new Response(null, { status: response.status, headers: response.headers });\n }\n return new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n}\n\nfunction declaredLength(response: Response): number | null {\n const raw = response.headers.get(\"content-length\");\n if (raw === null) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\nasync function readOnce(\n response: Response,\n sniff: boolean,\n maxBytes: number | undefined,\n): Promise<{ response: Response; challenge: boolean }> {\n const declared = declaredLength(response);\n if (maxBytes !== undefined && declared !== null && declared > maxBytes) {\n await response.body?.cancel();\n return { response: rebuild(response, new ArrayBuffer(0)), challenge: false };\n }\n const body = await response.arrayBuffer();\n const wantsSniff =\n sniff && shouldSniffChallenge(response.headers.get(\"content-type\"), body.byteLength, MAX_SNIFF_BYTES);\n const challenge = wantsSniff ? isChallengeBody(new TextDecoder().decode(body)) : false;\n return { response: rebuild(response, body), challenge };\n}\n\ninterface RungResult {\n readonly attempt: FetchAttempt;\n /** Present only when the origin answered — a proxy's own 407 never is. */\n readonly response: Response | null;\n readonly error?: unknown;\n}\n\n/** One rung: open it, read it, close it. Never throws — a rung is a verdict. */\nasync function attemptRoute(\n url: string,\n init: RequestInit,\n route: ProxyRoute,\n options: EscalatedFetchOptions,\n): Promise<RungResult> {\n const tier = tierOf(route);\n const agent = agentFor(route);\n const startedAt = Date.now();\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n // Each rung gets its own budget, and the caller's own signal still cancels\n // the whole thing — a ladder that ignored it would keep paying for a job\n // that has already been abandoned.\n const signal = init.signal\n ? AbortSignal.any([init.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs);\n\n try {\n const raw = await followRedirects(url, { ...init, dispatcher: agent ?? undefined, signal });\n const read = await readOnce(raw, options.sniffChallenge ?? true, options.maxBytes);\n const outcome = classifyFetchOutcome({ status: raw.status, challenge: read.challenge });\n return {\n attempt: { tier, status: raw.status, ms: Date.now() - startedAt, outcome },\n response: outcome === \"proxy_unavailable\" ? null : read.response,\n };\n } catch (error) {\n const blockSignal = errorBlockSignal(error, route.kind === \"proxy\");\n return {\n attempt: {\n tier,\n status: blockSignal.status ?? undefined,\n ms: Date.now() - startedAt,\n outcome: classifyFetchOutcome(blockSignal),\n },\n response: null,\n error,\n };\n } finally {\n await agent?.close();\n }\n}\n\n/**\n * Fetch `url`, escalating through the configured proxy tiers on a classified\n * block and on nothing else.\n *\n * The returned `response` is the origin's, from whichever rung answered — never\n * a proxy's 407, which would read as the site refusing us. When every rung\n * failed for our own reasons, `response` is null and `attempts` says so, which\n * is the difference between \"this page is unreachable\" and \"our credentials\n * are wrong\" that every caller downstream needs.\n */\nexport async function fetchWithEscalation(\n url: string,\n init: RequestInit,\n options: EscalatedFetchOptions,\n): Promise<EscalatedFetchResult> {\n const attempts: FetchAttempt[] = [];\n let originResponse: Response | null = null;\n let originTier: \"direct\" | ProxyTier = \"direct\";\n let lastError: unknown;\n\n const routes = plannedRoutes(url, options.credentials, options.routeOptions);\n const tried: TriedRoute[] = [];\n for (let route: ProxyRoute | null = routes[0] ?? null; route !== null; route = nextPlannedRoute(routes, tried)) {\n const rung = await attemptRoute(url, init, route, options);\n attempts.push(rung.attempt);\n tried.push({ tier: rung.attempt.tier, proxyFailure: rung.attempt.outcome === \"proxy_unavailable\" });\n if (rung.response !== null) {\n originResponse = rung.response;\n originTier = rung.attempt.tier;\n }\n if (rung.error !== undefined) lastError = rung.error;\n if (!shouldTryNextRoute(rung.attempt.outcome)) break;\n }\n\n return {\n response: originResponse,\n attempts,\n finalTier: originResponse !== null ? originTier : (lastAttempt(attempts)?.tier ?? \"direct\"),\n ...(originResponse === null && lastError !== undefined ? { error: lastError } : {}),\n };\n}\n\n/** `Array.prototype.at` is past the lib Convex's own tsconfig compiles against. */\nfunction lastAttempt(attempts: readonly FetchAttempt[]): FetchAttempt | undefined {\n return attempts[attempts.length - 1];\n}\n\n/** One line per operation, with no credential anywhere in it. */\nexport function describeLadder(result: EscalatedFetchResult): string {\n const rungs = result.attempts.map((a) => `${a.tier}:${a.outcome}${a.status ? `(${a.status})` : \"\"}@${a.ms}ms`);\n return `${rungs.join(\" → \")} | final=${result.finalTier}`;\n}\n\n/** A rung that answered cleanly settles the question; no earlier rung outvotes it. */\nfunction rescued(result: EscalatedFetchResult): boolean {\n return result.attempts.some((attempt) => attempt.outcome === \"ok\");\n}\n\n/**\n * Whether the ladder ran out of rungs while the site was still refusing us.\n *\n * Two mistakes are available here and this function has made both. Reading only\n * the *last* rung reports \"not blocked\" for `[blocked, proxy_unavailable]` — the\n * state a rotated password puts every deployment into — and hands the caller the\n * wall page from rung one as if it were the payload, which `mediaCache` then\n * writes into the S3 cache permanently. Reading *any* rung reports \"blocked\" for\n * `[blocked, ok]`, which is the success case: the caller pays for the paid rung,\n * receives the bytes, and throws them away. That one makes the whole ladder cost\n * money for nothing.\n *\n * So: a clean answer wins outright, and only in its absence does a refusal count.\n */\nexport function endedBlocked(result: EscalatedFetchResult): boolean {\n if (rescued(result)) return false;\n return result.attempts.some((attempt) => attempt.outcome === \"blocked\");\n}\n\n/**\n * Whether the ladder ended on our own fault with no site verdict to report.\n *\n * Narrower than \"a rung was a proxy failure\" twice over: a rung that answered\n * makes the dead rung irrelevant, and a rung that saw the site refuse us gives\n * `endedBlocked` the honest verdict. This is only for the case where we never\n * got far enough to learn anything about the page — which must never be charged\n * to it.\n */\nexport function endedProxyUnavailable(result: EscalatedFetchResult): boolean {\n if (rescued(result) || endedBlocked(result)) return false;\n return result.attempts.some((attempt) => attempt.outcome === \"proxy_unavailable\");\n}\n\n/**\n * The host resolved to an address inside our own network, so we refused it.\n *\n * Worth telling apart from \"that address did not answer\": one is a dead site\n * and the other is a decision we made, and only the second is worth saying\n * plainly to whoever handed us the URL.\n */\nexport function endedPrivateAddress(result: EscalatedFetchResult): boolean {\n if (rescued(result)) return false;\n // The chain, not the top: undici reports this as a bare `TypeError: fetch\n // failed` and hangs the real reason off `cause`. Reading only the outer\n // message found nothing and reported a refusal as a dead host.\n for (let error: unknown = result.error, depth = 0; error instanceof Error && depth < 5; depth++) {\n if (error.message.includes(PRIVATE_ADDRESS_CODE)) return true;\n // Read rather than dereferenced: `Error.cause` is ES2022, and this module is\n // also typechecked by Convex's bundler against an older lib, where the\n // property does not exist on the type even though it does at runtime.\n error = (error as { cause?: unknown }).cause;\n }\n return false;\n}\n","/**\n * Every judgement the escalating fetch makes, with no socket in sight.\n *\n * `fetch.ts` is deliberately thin: it opens connections and hands what came\n * back to the functions here. Everything that decides *money* — is this a\n * block, is this our own broken route, is it worth another rung — lives in this\n * file so it can be pinned by tests rather than by a live request to a site\n * that happens to be refusing us today.\n */\n\nimport type { BlockSignal } from \"./escalate.ts\";\nimport { isProxyFailure, shouldEscalate } from \"./escalate.ts\";\nimport type { ProxyTier } from \"./tiers.ts\";\n\n/** What one rung of the ladder did. */\nexport type FetchOutcome = \"ok\" | \"blocked\" | \"proxy_unavailable\" | \"failed\";\n\n/** One rung, as it happened. Returned to the caller so the ladder is loggable. */\nexport interface FetchAttempt {\n readonly tier: \"direct\" | ProxyTier;\n readonly status?: number;\n readonly ms: number;\n readonly outcome: FetchOutcome;\n}\n\n/**\n * Name what happened, from the signal alone.\n *\n * The order matters and is the whole point:\n *\n * 1. **Our route first.** A 407 or a tunnel that would not open is a fault in\n * the credentials we sent, never the site's opinion of us. Reading it as a\n * block would spend the expensive rung reproducing our own misconfiguration\n * and — worse — would file a perfectly readable page as refused.\n * 2. **Then the block**, delegated to `shouldEscalate`, which is the single\n * closed allow-list every surface shares.\n * 3. **Then success**, which a challenge body can veto even on a 200.\n */\nexport function classifyFetchOutcome(signal: BlockSignal): FetchOutcome {\n if (isProxyFailure(signal)) return \"proxy_unavailable\";\n if (shouldEscalate(signal)) return \"blocked\";\n if (signal.status != null && signal.status >= 200 && signal.status < 400 && !signal.challenge) return \"ok\";\n return \"failed\";\n}\n\n/**\n * Whether a failed rung earns another one.\n *\n * Both reasons move to the next route, and they are not the same reason:\n *\n * - `blocked` climbs — the site refused *us*, and a different exit IP is a\n * different us.\n * - `proxy_unavailable` does not climb, it *degrades*: this rung is unusable,\n * so it is dropped and the ladder carries on without it. A mis-pasted\n * datacenter password must cost the use of the datacenter rung, never the\n * residential one that would have worked.\n *\n * Everything else stops: a 404, a dead domain, a certificate that will not\n * negotiate and a timeout all fail identically from every IP on earth, so a\n * second attempt buys a second identical failure and a bill.\n */\nexport function shouldTryNextRoute(outcome: FetchOutcome): boolean {\n return outcome === \"blocked\" || outcome === \"proxy_unavailable\";\n}\n\n/** Node/undici error codes that mean the attempt ran out of time. */\nconst TIMEOUT_CODES = new Set([\n \"UND_ERR_CONNECT_TIMEOUT\",\n \"UND_ERR_HEADERS_TIMEOUT\",\n \"UND_ERR_BODY_TIMEOUT\",\n \"ETIMEDOUT\",\n]);\n\n/**\n * Node codes that mean the peer hung up mid-conversation.\n *\n * Mapped onto the Chromium names the shared classifier speaks, because a WAF\n * null-routing a datacenter range looks like this from `undici` and like\n * `net::ERR_CONNECTION_RESET` from Playwright, and they have to reach the same\n * verdict.\n */\nconst RESET_CODES = new Set([\"ECONNRESET\", \"UND_ERR_SOCKET\", \"EPIPE\"]);\n\n/**\n * Anything speaking proxy answered the CONNECT with something other than 200.\n *\n * Kept, but do not rely on it: measured against undici 7.29 and a local proxy\n * that answers every CONNECT with 407, `fetch` surfaces only\n * `Error(\"Request was cancelled.\")` with `code: 0` — the status never reaches\n * the caller. The `proxied` fallback below is what actually carries that case,\n * which is why it is a fallback and not a last resort.\n */\nconst PROXY_RESPONSE = /Proxy response \\((\\d+)\\)/;\n\nfunction errorCode(error: unknown): string | null {\n if (typeof error !== \"object\" || error === null) return null;\n const code = (error as { code?: unknown }).code;\n if (typeof code === \"string\") return code;\n const name = (error as { name?: unknown }).name;\n return typeof name === \"string\" ? name : null;\n}\n\nfunction errorMessage(error: unknown): string {\n if (typeof error === \"object\" && error !== null) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === \"string\") return message;\n }\n return String(error);\n}\n\n/**\n * Turn a thrown `fetch` error into the signal the shared classifier reads.\n *\n * `proxied` is not a nicety. Through a tunnel, the target's DNS is resolved at\n * the far end and every connect-level failure we can see belongs to the proxy —\n * so the same `ENOTFOUND` means \"your domain is a typo\" on the direct rung and\n * \"our exit node is down\" on a paid one. Collapsing the two would let one bad\n * credential mark healthy pages as unreachable, which is precisely the failure\n * this ladder exists to avoid.\n */\nexport function errorBlockSignal(error: unknown, proxied: boolean): BlockSignal {\n const proxyResponse = PROXY_RESPONSE.exec(errorMessage(error));\n if (proxyResponse) {\n const status = Number(proxyResponse[1]);\n // Only something speaking proxy produces these, so they are always ours.\n return status === 407 ? { status: 407 } : { netError: \"ERR_TUNNEL_CONNECTION_FAILED\" };\n }\n\n const code = errorCode(error);\n if (code !== null && TIMEOUT_CODES.has(code)) return { timedOut: true };\n // `AbortError` is what a caller's own signal and `AbortSignal.timeout` both\n // raise; either way the attempt ran out of time rather than being refused.\n if (code === \"AbortError\" || code === \"TimeoutError\") return { timedOut: true };\n\n if (proxied) {\n // The tunnel never carried us. Whatever the code says about hostnames, the\n // only host we spoke to was the proxy.\n return { netError: \"ERR_PROXY_CONNECTION_FAILED\" };\n }\n\n if (code !== null && RESET_CODES.has(code)) return { netError: \"ERR_CONNECTION_RESET\" };\n return { netError: code ?? \"ERR_FAILED\" };\n}\n\n/**\n * Is this response worth reading as text to look for a wall behind a 200?\n *\n * Two guards, both about not paying for the check. A CDN handing back the JPEG\n * we asked for is never a challenge, so a body that declares an image, a video\n * or any other binary type is not read. And a wall page is small — a body that\n * announces megabytes is the thing we wanted, so it is left as bytes rather\n * than decoded into a string that would be thrown away.\n */\nexport function shouldSniffChallenge(\n contentType: string | null,\n contentLength: number | null,\n maxSniffBytes: number,\n): boolean {\n if (contentLength !== null && contentLength > maxSniffBytes) return false;\n if (contentType === null || contentType.trim() === \"\") return true;\n const type = contentType.toLowerCase();\n // Media is never a wall, and `image/svg+xml` would otherwise be dragged in by\n // the `xml` test below — refusing an SVG because it says \"captcha\" is a worse\n // failure than missing the wall that never arrives dressed as an image.\n if (type.startsWith(\"image/\") || type.startsWith(\"video/\") || type.startsWith(\"audio/\")) return false;\n return type.includes(\"text/\") || type.includes(\"html\") || type.includes(\"json\") || type.includes(\"xml\");\n}\n","/**\n * Fetching bytes from somebody else's server, with the escalation ladder on.\n *\n * Six commands download from an arbitrary host — canvas ingest, `images\n * download`, the shared image reader, and the two canvas scaffolders — and every\n * one of them was a bare `fetch(url)`. That is the request shape a CDN hotlink\n * filter is tuned to refuse first: no User-Agent, no Referer, and an exit IP\n * that belongs to a datacenter. The agent's instruction for the resulting 403\n * was to give up and re-source the asset.\n *\n * This is where that stops being the answer. One place reads the credentials,\n * one place decides what a refusal looks like, and one place writes the ladder\n * to stderr — so a command that gains a download later inherits all three\n * instead of growing a seventh copy of the mistake.\n *\n * **Nothing here reaches the model.** No hint, no error message and no command\n * description mentions a proxy; an agent that learned it could ask for one would\n * start asking. The escalation is ours to decide and its bill is ours to carry.\n */\n\nimport { refuseNonPublicUrl } from \"@baker/proxy\";\nimport {\n describeLadder,\n endedBlocked,\n endedPrivateAddress,\n endedProxyUnavailable,\n fetchWithEscalation,\n} from \"@baker/proxy/fetch\";\nimport type { Response } from \"undici\";\nimport { captureProxyCredentials } from \"../env.ts\";\n\n/**\n * Browser-shaped headers — the *first* answer to a hotlink filter, and the free\n * one. The ladder is the second, for hosts that refuse the address rather than\n * the request. Mirrors `convex/lib/imageFetch.ts`, which learned it the same way.\n */\nconst BROWSER_HEADERS: Record<string, string> = {\n \"User-Agent\":\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36\",\n Accept: \"image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n};\n\n/** One sentence for both halves of the rule — the name and the address it resolves to. */\nconst NOT_PUBLIC_MESSAGE = \"That address is not reachable from the public web, so there is nothing to read.\";\n\n/**\n * The ceiling for anything that is an image rather than a video.\n *\n * The same number the Convex side uses (`MAX_INGEST_BYTES`), and deliberately so\n * — the dashboard and the agent should not disagree about how big an image is\n * allowed to be, and a URL handed to `images download` is frequently the same\n * URL the backend will be asked to ingest afterwards.\n */\nexport const MAX_REMOTE_IMAGE_BYTES = 25_000_000;\n\nexport interface ExternalFetchOptions {\n /** Per-rung budget. The ladder is not one clock; each rung gets its own. */\n readonly timeoutMs?: number;\n /** Refuse to buffer a body that declares itself larger than this. */\n readonly maxBytes?: number;\n /** Extra or overriding request headers. */\n readonly headers?: Record<string, string>;\n /**\n * Off for a download that is definitionally bytes. A challenge sniff decodes\n * a small body as text looking for a wall behind a 200; a video file is not\n * that, and reading one as a string is waste rather than a mistake.\n */\n readonly sniffChallenge?: boolean;\n}\n\nexport interface ExternalFetchResult {\n /** The origin's answer, from whichever rung produced one. */\n readonly response: Response;\n /**\n * Every rung refused us and none produced a clean answer.\n *\n * Distinct from `!response.ok`: a bot wall answers **200** with a page, so the\n * status line alone cannot tell a payload from a refusal.\n */\n readonly blocked: boolean;\n}\n\n/** No rung reached the origin. Named so a caller can tell it from a 404. */\nexport class ExternalFetchError extends Error {\n /** True when the fault was ours — a route we could not open, not a dead host. */\n readonly ourFault: boolean;\n\n constructor(message: string, ourFault: boolean) {\n super(message);\n this.name = \"ExternalFetchError\";\n this.ourFault = ourFault;\n }\n}\n\n/**\n * Fetch `url`, climbing to a paid exit only if the host refuses this one.\n *\n * Dormant on the happy path by construction: a 200 costs one request, and so do\n * a 404, a dead domain and a certificate that will not negotiate — none of which\n * a different IP would change. Only a classified refusal opens a rung.\n */\nexport async function fetchExternal(url: string, options: ExternalFetchOptions = {}): Promise<ExternalFetchResult> {\n // The ladder refuses to *bill* a private address, and its comment says one is\n // \"refused upstream\" — which was true of the landing capture, the only caller\n // it had when it was written. It is not true here: every command that reaches\n // the web through this function takes a URL straight from an agent or a\n // stored row, and the direct rung would still go out. In a Runtime that means\n // `169.254.169.254` answers with cloud credentials, and a caller writes them\n // to a file it then reads back. The refusal has to happen before the request,\n // and this is the only place all six download surfaces pass through.\n if (refuseNonPublicUrl(url) !== null) {\n throw new ExternalFetchError(NOT_PUBLIC_MESSAGE, false);\n }\n\n const result = await fetchWithEscalation(\n url,\n { headers: { ...BROWSER_HEADERS, ...options.headers } },\n {\n credentials: captureProxyCredentials(),\n ...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),\n ...(options.maxBytes === undefined ? {} : { maxBytes: options.maxBytes }),\n ...(options.sniffChallenge === undefined ? {} : { sniffChallenge: options.sniffChallenge }),\n },\n );\n\n // stderr, and only when it actually climbed. stdout is the JSON envelope the\n // agent parses, and a ladder line on the happy path would be noise on every\n // download in the repo.\n if (result.attempts.length > 1) {\n process.stderr.write(`fetch ladder ${url} — ${describeLadder(result)}\\n`);\n }\n\n if (result.response === null) {\n // A name that resolved into our own network is a refusal, not an outage.\n // The hostname guard above cannot catch it — `localtest.me` is a public name\n // with a public record pointing at `127.0.0.1` — so it is caught at the\n // resolved address, and reported as the decision it is.\n if (endedPrivateAddress(result)) {\n throw new ExternalFetchError(NOT_PUBLIC_MESSAGE, false);\n }\n const ourFault = endedProxyUnavailable(result);\n throw new ExternalFetchError(\n ourFault ? \"Could not open a route to that address.\" : \"That address did not answer.\",\n ourFault,\n );\n }\n\n return { response: result.response, blocked: endedBlocked(result) };\n}\n\n/**\n * Whether `bytes` busts the ceiling, as the sentence to say about it.\n *\n * `null` bytes means the host never said, which must not read as zero — a\n * chunked response has no `content-length` and is not thereby empty.\n *\n * A file, not an image: `scaffold-video` downloads through here too, and it is\n * the surface most likely to meet a limit.\n */\nexport function refuseOversize(bytes: number | null, maxBytes: number): string | null {\n if (bytes === null || bytes <= maxBytes) return null;\n return `That file is ${humanBytes(bytes)} — the limit is ${humanBytes(maxBytes)}.`;\n}\n\n/**\n * A size a person can read, at whatever scale it happens to be.\n *\n * Fixed `MB` looked fine against the two real ceilings and fell over the moment\n * it met anything else: a 13 KB file refused by a 1 KB cap read \"That file is\n * 0.0 MB — the limit is 0.0 MB\", which names no problem at all. The ceilings\n * here span 25 MB to 2 GB, so no single unit was ever going to carry them.\n */\nfunction humanBytes(n: number): string {\n if (n >= 1024 ** 3) return `${(n / 1024 ** 3).toFixed(1)} GB`;\n if (n >= 1024 ** 2) return `${(n / 1024 ** 2).toFixed(1)} MB`;\n return `${Math.max(1, Math.round(n / 1024))} KB`;\n}\n\n/** What the response says it weighs, or `null` when it did not say. */\nfunction declaredBytes(response: Response): number | null {\n const raw = response.headers.get(\"content-length\");\n if (raw === null) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * The common case: fetch and hand back the bytes.\n *\n * Throws on a refusal rather than returning a wall page, because every caller\n * writes what it gets to disk or uploads it — and an HTML \"just a moment\" page\n * saved as `logo.png` is the failure nothing downstream can detect.\n *\n * `maxBytes` is **required**, and that is the point of it. Four of the five\n * callers here passed nothing, so `images download`, `readImageBuffer` and the\n * static-ad scaffolder would each buffer whatever a URL from agent argv pointed\n * at, with no ceiling anywhere — while every neighbouring surface in the repo\n * caps (`MAX_ASSET_BYTES` streaming in `asset-store`, a required `maxBytes` in\n * `fetchImageBytes`). A default would have closed it today and reopened it for\n * the next caller; a required argument makes each surface say its own number.\n */\nexport async function fetchExternalBytes(\n url: string,\n options: ExternalFetchOptions & { readonly maxBytes: number },\n): Promise<{ buffer: Buffer; contentType: string | null }> {\n const { response, blocked } = await fetchExternal(url, options);\n if (!response.ok) {\n throw new ExternalFetchError(`${response.status} ${response.statusText}`, false);\n }\n if (blocked) {\n throw new ExternalFetchError(\"That host served a security check instead of the file.\", false);\n }\n\n // The declared size first, and it is not belt-and-braces. `@baker/proxy`\n // answers an oversize body by cancelling it and handing back a 200 with an\n // *empty* buffer, keeping `content-length` so the caller can see what\n // happened. Without this read, that arrives as a successful download of\n // nothing — and the caller's own byte-length check passes, because zero is\n // not too large.\n const declared = refuseOversize(declaredBytes(response), options.maxBytes);\n if (declared !== null) {\n throw new ExternalFetchError(declared, false);\n }\n\n const buffer = Buffer.from(await response.arrayBuffer());\n // And again on what actually arrived, for the host that said nothing or lied.\n const actual = refuseOversize(buffer.byteLength, options.maxBytes);\n if (actual !== null) {\n throw new ExternalFetchError(actual, false);\n }\n\n return { buffer, contentType: response.headers.get(\"content-type\") };\n}\n","import { spawn } from \"node:child_process\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ProxyCredentials, ProxyRoute, ProxyTier } from \"@baker/proxy\";\nimport { DIRECT_ROUTE, plannedRoutes, shouldEscalate } from \"@baker/proxy\";\nimport { captureProxyCredentials, childEnvWith } from \"../env.ts\";\nimport { writeVimeoCookieFile } from \"./vimeoCookie.ts\";\nimport { videoFormatArgs } from \"./ytDlpFormat.ts\";\nimport { needsPlayerClientRetry, YOUTUBE_FALLBACK_CLIENT_ARGS } from \"./ytDlpPlayerClient.ts\";\nimport { ytDlpBlockSignal } from \"./ytDlpSignal.ts\";\n\n/**\n * yt-dlp, run through the egress ladder, shared by the canvas `ingest` node and\n * `baker videos ingest`.\n *\n * Both download the same kinds of URL from the same sandbox and must agree about\n * what \"blocked\" means and how a credential is written down — and the second of\n * those is a decision that silently means two different passwords if it is made\n * twice (see `proxyEnvFor`). One copy is the point.\n *\n * Callers own how a failure is reported: the node wraps `YtDlpError` in a\n * `NodeExecutionError`, the command turns it into a `{ ok: false }` envelope.\n */\n\nexport const YT_DLP_BIN = \"yt-dlp\";\nexport const YT_DLP_TIMEOUT_MS = 10 * 60 * 1000;\n/** A probe fetches no media, so it gets a much shorter leash than a download. */\nexport const YT_DLP_PROBE_TIMEOUT_MS = 60 * 1000;\n\nexport type YtDlpInfo = Record<string, unknown>;\n\n/**\n * Verify TLS against the system bundle, never certifi's.\n *\n * The Runtime reads HTTPS through the credential broker, and the broker's CA is\n * installed in the system bundle (`SSL_CERT_FILE`) and nowhere else. yt-dlp is\n * installed with certifi and, left alone, verifies against certifi's own copy —\n * so once the broker shipped, every direct download in a Runtime failed with\n * `CERTIFICATE_VERIFY_FAILED` while `curl` to the same host answered. The agent\n * that hit it on our own CDN worked around it by pointing a committed canvas at\n * its gitignored local cache, which broke the next Session's publish. The\n * impersonating (curl_cffi) path reads `CURL_CA_BUNDLE`, which the broker also\n * sets, so it needs nothing here.\n */\nconst TRUST_SYSTEM_CERTS = [\"--compat-options\", \"no-certifi\"] as const;\n\nexport class YtDlpError extends Error {\n constructor(\n message: string,\n readonly stderrTail: string,\n ) {\n super(message);\n this.name = \"YtDlpError\";\n }\n}\n\nexport function tail(text: string, maxLines: number): string {\n return text.split(\"\\n\").slice(-maxLines).join(\"\\n\");\n}\n\n/**\n * Download `url` into `workDir`, climbing to a paid exit only if the host\n * refused the cheaper one. Returns the media file and yt-dlp's info.json\n * (best-effort — yt-dlp omits it for some direct file URLs).\n *\n * By default the best the site will release: `bv*+ba/b`. There used to be an\n * optional height cap here, on the reasoning that a 4K master is bytes we\n * re-encode anyway — but both callers now want the master, because capping\n * discards source pixels before the systems that make renditions from them ever\n * see them, and that loss is permanent while the saving is one download.\n * The one exception is opt-in: a bulk import may pass `maxHeight`.\n */\nexport async function runYtDlp(args: {\n url: string;\n audioOnly: boolean;\n workDir: string;\n timeoutMs?: number;\n onLog?: (message: string) => void;\n country?: string;\n /** Vimeo session cookie value; written to a jar in `workDir` when present. */\n vimeoCookie?: string;\n /** Opt-in resolution cap (shorter side) for bulk imports — see `videoFormatArgs`. */\n maxHeight?: number;\n}): Promise<{ filePath: string; info: YtDlpInfo; tier: EgressTier; reducedQuality: boolean }> {\n const outTemplate = path.join(args.workDir, \"out.%(ext)s\");\n const infoPath = path.join(args.workDir, \"out.info.json\");\n\n const argv: string[] = [\n args.url,\n \"--no-playlist\",\n \"--no-progress\",\n \"--no-warnings\",\n \"--quiet\",\n \"--write-info-json\",\n ...TRUST_SYSTEM_CERTS,\n \"-o\",\n outTemplate,\n ];\n\n if (args.audioOnly) {\n argv.push(\"-x\", \"--audio-format\", \"m4a\");\n } else {\n argv.push(...videoFormatArgs(args.maxHeight));\n }\n\n const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.workDir);\n if (cookieFile) argv.push(\"--cookies\", cookieFile);\n\n args.onLog?.(`yt-dlp: downloading ${args.audioOnly ? \"audio\" : \"video\"}`);\n const timeoutMs = args.timeoutMs ?? YT_DLP_TIMEOUT_MS;\n const { code, stderr, tier, reducedQuality } = await downloadWithClientFallback({\n url: args.url,\n argv,\n onLog: args.onLog,\n ladder: (attemptArgv) =>\n runViaLadder({ url: args.url, argv: attemptArgv, timeoutMs, onLog: args.onLog, country: args.country }),\n repeat: (attemptArgv, route) => spawnAndWait(YT_DLP_BIN, attemptArgv, timeoutMs, { extraEnv: proxyEnvFor(route) }),\n });\n\n if (code !== 0) {\n throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));\n }\n\n const files = await readdir(args.workDir);\n const wanted = args.audioOnly ? \".m4a\" : \".mp4\";\n const downloaded = files.find((f) => f.startsWith(\"out.\") && f.endsWith(wanted));\n if (!downloaded) {\n throw new YtDlpError(`yt-dlp finished but produced no ${wanted} file (saw: ${files.join(\", \")})`, \"\");\n }\n\n let info: YtDlpInfo = {};\n try {\n info = JSON.parse(await readFile(infoPath, \"utf-8\")) as YtDlpInfo;\n } catch {\n // info.json is best-effort — yt-dlp sometimes omits it for direct file URLs.\n }\n\n return { filePath: path.join(args.workDir, downloaded), info, tier, reducedQuality };\n}\n\n/**\n * Read a URL's metadata without fetching any media.\n *\n * Worth its own round trip because it settles the one thing that has to be known\n * before the expensive part starts: how long the video is. Measured, a ten-hour\n * link is now refused in ~3s, where the byte ceiling it replaced could only\n * refuse it after the download it was meant to avoid. It also carries the\n * provider id, which used to be readable only from the download's own info.json.\n *\n * Climbs the same egress ladder as the download, so a host that answers only a\n * paid exit is probed through one too.\n */\nexport async function probeYtDlp(args: {\n url: string;\n timeoutMs?: number;\n onLog?: (message: string) => void;\n country?: string;\n /** Vimeo session cookie value. `cookieDir` must be set for it to be used. */\n vimeoCookie?: string;\n cookieDir?: string;\n}): Promise<{ info: YtDlpInfo; tier: EgressTier }> {\n const argv = [\n args.url,\n \"--no-playlist\",\n \"--no-warnings\",\n \"--dump-single-json\",\n \"--skip-download\",\n // Without this the probe fails wherever the *download* would fail, which\n // defeats the point of probing: yt-dlp still runs format selection for\n // `--dump-single-json`, so a binary too old to read a site's format list\n // exits \"Requested format is not available\" and we learn nothing — measured\n // against a stale build, which returned full metadata the moment this flag\n // was added. Duration and id come from the page, not from a format.\n \"--ignore-no-formats-error\",\n ...TRUST_SYSTEM_CERTS,\n ];\n\n // The probe needs the same credential as the download: Vimeo refuses metadata\n // to an anonymous caller too, so probing without it fails before the duration\n // gate ever runs.\n if (args.cookieDir) {\n const cookieFile = await writeVimeoCookieFile(args.vimeoCookie, args.cookieDir);\n if (cookieFile) argv.push(\"--cookies\", cookieFile);\n }\n\n let stdout = \"\";\n const { code, stderr, tier } = await walkYtDlpLadder({\n url: args.url,\n credentials: captureProxyCredentials(),\n attempt: async (route) => {\n const result = await spawnAndWait(YT_DLP_BIN, argv, args.timeoutMs ?? YT_DLP_PROBE_TIMEOUT_MS, {\n extraEnv: proxyEnvFor(route),\n captureStdout: true,\n });\n stdout = result.stdout ?? \"\";\n return result;\n },\n onLog: args.onLog,\n country: args.country,\n });\n\n if (code !== 0) {\n throw new YtDlpError(`yt-dlp exited ${code}`, tail(stderr, 40));\n }\n\n // Parsed, not trusted: yt-dlp prints a bare `null` for a page it recognised\n // but could not extract, and on at least one site it pairs that with a\n // non-zero exit — so the exit code alone is not enough to promise an object.\n let parsed: unknown;\n try {\n parsed = JSON.parse(stdout);\n } catch {\n throw new YtDlpError(\"yt-dlp returned metadata that could not be read\", tail(stdout, 5));\n }\n if (parsed === null || typeof parsed !== \"object\") {\n throw new YtDlpError(\"yt-dlp returned no metadata for that link\", tail(stderr, 40));\n }\n return { info: parsed as YtDlpInfo, tier };\n}\n\n/**\n * True only when the source explicitly says it carries no video.\n *\n * `vcodec: \"none\"` is the assertion; a *missing* vcodec is not, and that\n * distinction is the whole function. Measured: SoundCloud reports `\"none\"`,\n * while Streamable reports `null` and is perfectly good video. The obvious\n * version of this check — \"no format has a video codec\" — matches both and\n * would refuse Streamable, which ingests fine today.\n *\n * Caught at probe time because the alternative is what it replaces: the\n * download succeeds, produces an `.m4a`, and the caller fails looking for an\n * `.mp4` — surfaced as \"yt-dlp finished but produced no .mp4 file\", which names\n * an internal expectation rather than the fact that someone linked to audio.\n */\nexport function isAudioOnly(info: YtDlpInfo): boolean {\n return info.vcodec === \"none\";\n}\n\n/** The probed duration in seconds, when the site reported one that can be read. */\nexport function durationSeconds(info: YtDlpInfo): number | undefined {\n const raw = info.duration;\n return typeof raw === \"number\" && Number.isFinite(raw) && raw > 0 ? raw : undefined;\n}\n\n/**\n * The canonical page URL for what was probed, when the extractor resolved one.\n *\n * Worth preferring over the URL a person pasted, because yt-dlp has already done\n * the normalising: measured, it turns `youtu.be/<id>?si=…&t=42`,\n * `/shorts/<id>` and `m.youtube.com/watch?v=<id>&feature=share` all into the one\n * `www.youtube.com/watch?v=<id>`. Storing the raw paste instead files the same\n * video under a different `externalUrl` depending on where the link was copied\n * from, which is exactly the provenance question this field exists to answer.\n */\nexport function canonicalUrl(info: YtDlpInfo): string | undefined {\n const raw = info.webpage_url;\n return typeof raw === \"string\" && raw.length > 0 ? raw : undefined;\n}\n\n/**\n * Walk the ladder: direct first, then each configured tier, stopping the moment\n * the failure is one a different exit IP cannot fix.\n *\n * The escalation decision is `shouldEscalate`'s, not ours — the same closed\n * allow-list every other egress surface uses. A downloader that decided for\n * itself would be the one surface where a 404 could cost money.\n */\nexport type EgressTier = \"direct\" | ProxyTier;\n\n/**\n * Which rung produced this result — so a paid exit is never spent invisibly.\n *\n * The `route` is here, and not just the tier's name, because one caller needs to\n * *repeat* the rung rather than merely report it: the player-client fallback\n * re-runs on whichever exit already got far enough to be refused at the media\n * stage. A name cannot be handed back to `proxyEnvFor`; the route can.\n */\nexport type LadderResult = SpawnResult & { tier: EgressTier; route: ProxyRoute };\n\nexport async function walkYtDlpLadder(args: {\n url: string;\n credentials: ProxyCredentials;\n attempt: (route: ProxyRoute) => Promise<SpawnResult>;\n onLog?: (message: string) => void;\n /**\n * ISO-3166 alpha-2 exit country for the paid rungs. Omit for \"wherever\".\n *\n * Not inferred and never automatic: a country is only right when someone\n * knows *which* one opens the video, and guessing would multiply the cost of\n * every failure by the length of the guess list. `@baker/proxy` owns the\n * per-product syntax — datacenter and residential spell geo differently and\n * each rejects the other's token.\n */\n country?: string;\n}): Promise<LadderResult> {\n const routes = plannedRoutes(args.url, args.credentials, args.country ? { country: args.country } : undefined);\n let last: LadderResult = { code: -1, stderr: \"\", tier: \"direct\", route: DIRECT_ROUTE };\n\n for (const route of routes) {\n const tier: EgressTier = route.kind === \"proxy\" ? route.tier : \"direct\";\n if (route.kind === \"proxy\") args.onLog?.(`yt-dlp: retrying via ${tier}`);\n last = { ...(await args.attempt(route)), tier, route };\n if (last.code === 0) return last;\n if (!shouldEscalate(ytDlpBlockSignal(last.stderr))) return last;\n }\n return last;\n}\n\n/**\n * The download, plus the one retry that changes the player instead of the exit.\n *\n * Split out from `runYtDlp` with its two runners injected because the decision\n * this makes is the expensive one to get wrong and the hardest to observe: it\n * spends egress. A test can drive both branches here; it cannot drive them\n * through a function that spawns a binary.\n *\n * **The retry repeats one rung and opens no new one.** It re-runs on\n * `first.route` — whichever exit already got far enough to be refused at the\n * media stage — rather than walking the ladder again. Walking it again was the\n * first version of this and it was wrong in exactly the way this whole change\n * exists to stop: the trigger is a 403, `shouldEscalate` treats a 403 as\n * escalatable, so a fallback client that was also refused would climb through\n * datacenter and residential a second time, paying twice more for a verdict\n * that has already been shown not to depend on the address. Re-using the rung\n * is also what makes the retry correct when the *only* rung that reached the\n * media stage was a paid one — a direct-only retry would land back on the bot\n * wall that made us leave direct in the first place.\n */\nexport async function downloadWithClientFallback(args: {\n url: string;\n argv: readonly string[];\n ladder: (argv: readonly string[]) => Promise<LadderResult>;\n repeat: (argv: readonly string[], route: ProxyRoute) => Promise<SpawnResult>;\n onLog?: (message: string) => void;\n}): Promise<LadderResult & { reducedQuality: boolean }> {\n const first = await args.ladder(args.argv);\n if (first.code === 0 || !needsPlayerClientRetry(args.url, first.stderr)) {\n return { ...first, reducedQuality: false };\n }\n\n args.onLog?.(\"yt-dlp: retrying with the fallback player client\");\n const again = await args.repeat([...args.argv, ...YOUTUBE_FALLBACK_CLIENT_ARGS], first.route);\n\n // `tier` and `route` stay the first attempt's: the retry deliberately did not\n // move, so reporting a different rung would misstate what was spent.\n return { ...again, tier: first.tier, route: first.route, reducedQuality: again.code === 0 };\n}\n\nfunction runViaLadder(args: {\n url: string;\n argv: readonly string[];\n timeoutMs: number;\n onLog?: (message: string) => void;\n country?: string;\n}): Promise<LadderResult> {\n return walkYtDlpLadder({\n url: args.url,\n credentials: captureProxyCredentials(),\n attempt: (route) => spawnAndWait(YT_DLP_BIN, args.argv, args.timeoutMs, { extraEnv: proxyEnvFor(route) }),\n onLog: args.onLog,\n country: args.country,\n });\n}\n\n/**\n * A route as the environment a child process reads it from.\n *\n * Env, never `--proxy`: the flag would put the password in `argv`, readable by\n * any `ps` sharing the sandbox and printed by yt-dlp into the very stderr this\n * module reads back and attaches to an error. Both spellings, because \"which\n * case does this tool read\" is not worth being wrong about once — and `NO_PROXY`\n * is deliberately unset, since `plannedRoutes` has already refused to build a\n * proxy route for a private address.\n *\n * The percent-encoding is correct here and is deliberately **not** the same\n * decision as `browserProxyUrl` in `@baker/proxy`. Measured against both readers\n * with a proxy that logs what it receives: given `ab%3Dcd`, yt-dlp (Python)\n * authenticates as `ab=cd` — it decodes — while Chromium authenticates as the\n * literal `ab%3Dcd`. The same URL means two different passwords depending on who\n * reads it, so each side is written for its own reader. Do not harmonise them.\n */\nexport function proxyEnvFor(route: ProxyRoute): Record<string, string> | undefined {\n if (route.kind !== \"proxy\") return undefined;\n const uri = new URL(route.server);\n uri.username = encodeURIComponent(route.username);\n uri.password = encodeURIComponent(route.password);\n return {\n HTTP_PROXY: uri.toString(),\n HTTPS_PROXY: uri.toString(),\n http_proxy: uri.toString(),\n https_proxy: uri.toString(),\n };\n}\n\nexport type SpawnResult = { code: number; stderr: string; stdout?: string };\n\n/**\n * Spawn helper local to yt-dlp: the engine's `cli-runner` rejects URL arguments\n * by policy, which is directly at odds with yt-dlp's job.\n *\n * stdout is discarded unless asked for, because on a download it is the media\n * progress firehose; the probe is the one caller that actually wants it.\n */\nexport function spawnAndWait(\n bin: string,\n argv: readonly string[],\n timeoutMs: number,\n options: { extraEnv?: Record<string, string>; captureStdout?: boolean } = {},\n): Promise<SpawnResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(bin, argv, {\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n // `childEnvWith` because `spawn`'s `env` REPLACES the environment rather\n // than extending it — passing only the additions would strip PATH.\n ...(options.extraEnv ? { env: childEnvWith(options.extraEnv) } : {}),\n });\n const stderrChunks: Buffer[] = [];\n const stdoutChunks: Buffer[] = [];\n let stderrLen = 0;\n const MAX_STDERR = 256 * 1024;\n\n child.stdout.on(\"data\", (chunk: Buffer) => {\n if (options.captureStdout) stdoutChunks.push(chunk);\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n if (stderrLen < MAX_STDERR) {\n stderrChunks.push(chunk);\n stderrLen += chunk.length;\n }\n });\n\n const timer = setTimeout(() => {\n child.kill(\"SIGKILL\");\n reject(new Error(`${bin} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`failed to spawn \"${bin}\" — ${err.message}`));\n });\n\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n resolve({\n code: code ?? -1,\n stderr: Buffer.concat(stderrChunks).toString(\"utf-8\"),\n ...(options.captureStdout ? { stdout: Buffer.concat(stdoutChunks).toString(\"utf-8\") } : {}),\n });\n });\n });\n}\n","import { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Vimeo's session cookie, as a file yt-dlp can read.\n *\n * Vimeo is the only platform with no anonymous route left: every yt-dlp client\n * either cannot mint a token any more or refuses without a login, and the\n * failure is identical through direct, datacenter and residential exits, so it\n * is neither our IP nor our TLS fingerprint. A logged-in cookie is the whole\n * fix — measured, `vimeo` alone is sufficient and `vuid`/`auth_xsrft` without it\n * are worthless.\n *\n * The cookie arrives as a value, never as a path: the backend owns the secret\n * and the sandbox owns its filesystem, so nothing outside writes into the\n * Runtime. `--cookies <path>` then puts only the path in argv, keeping the\n * credential out of `ps` — the same rule that governs proxy passwords.\n */\n\n/** The single cookie yt-dlp's `_is_logged_in` actually checks. */\nconst COOKIE_NAME = \"vimeo\";\n\n/**\n * Write the cookie jar, returning its path — or undefined when unconfigured.\n *\n * Undefined rather than an empty file, because an empty jar is not the same as\n * no jar: yt-dlp would read it, find no session, and fail with the anonymous\n * error while looking as though credentials had been supplied.\n */\nexport async function writeVimeoCookieFile(cookie: string | undefined, workDir: string): Promise<string | undefined> {\n const value = cookie?.trim();\n if (!value) return undefined;\n\n // A year out. The jar's own expiry is a formality — Vimeo expires the session\n // server-side on its own schedule, and `isVimeoAuthFailure` is what notices.\n const expires = Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60;\n const jar = [\n \"# Netscape HTTP Cookie File\",\n [\".vimeo.com\", \"TRUE\", \"/\", \"TRUE\", String(expires), COOKIE_NAME, value].join(\"\\t\"),\n \"\",\n ].join(\"\\n\");\n\n const filePath = path.join(workDir, \"vimeo-cookies.txt\");\n // 0o600 at creation, not chmod-after: the sandbox runs the agent's own shell,\n // and a world-readable window between write and chmod is a window.\n await writeFile(filePath, jar, { mode: 0o600 });\n return filePath;\n}\n\n/**\n * Whether a Vimeo failure is about authentication rather than the video.\n *\n * Every one of these phrasings means the same thing to whoever pasted the link —\n * Vimeo would not talk to us — but Vimeo's own wording (\"Failed to fetch macos\n * OAuth token\") names an internal detail that sends the reader nowhere. The\n * distinction earns its place because this credential *expires*: without it, the\n * day the cookie lapses looks like a brand-new mystery instead of a five-minute\n * re-export.\n *\n * Scoped to `[vimeo]` so another platform's 401 cannot inherit advice about a\n * cookie that has nothing to do with it.\n */\nconst VIMEO_AUTH_SIGNATURES = [\"oauth token\", \"only works when logged-in\", \"unable to fetch new oauth tokens\"] as const;\n\nexport function isVimeoAuthFailure(detail: string): boolean {\n const haystack = detail.toLowerCase();\n if (!haystack.includes(\"[vimeo]\")) return false;\n return VIMEO_AUTH_SIGNATURES.some((signature) => haystack.includes(signature));\n}\n","/**\n * Which video yt-dlp keeps. By default the best the site releases — see\n * `runYtDlp` for why the master matters. A caller importing in bulk may cap\n * the resolution instead: a whole channel of 4K masters is five times the bytes\n * of 1080p, and those bytes are what overruns a download's time budget.\n *\n * The cap is on the shorter side (yt-dlp's `res` sort field), not the height: a\n * vertical Short's 1080p is 1080×1920, and a height cap would keep its 608×1080\n * rendition instead. A sort rather than a filter, so when nothing fits the cap\n * the closest there is still beats no video.\n */\nexport function videoFormatArgs(maxResolution?: number): string[] {\n const sort = maxResolution ? [\"-S\", `res:${maxResolution}`] : [];\n return [\"--format\", \"bv*+ba/b\", ...sort, \"--merge-output-format\", \"mp4\"];\n}\n","/**\n * The second thing to try when YouTube hands us a video and then refuses it.\n *\n * Distinct from the egress ladder, and the distinction is the whole point. The\n * ladder answers \"would a different IP help\"; this answers \"would a different\n * *client* help\" — and measured inside a Runtime, the second question is the one\n * that was failing. yt-dlp's default client selection lands on `android_vr`,\n * whose media URLs YouTube now signs and then rejects, so extraction succeeds,\n * a format is chosen, and the byte fetch comes back\n * `unable to download video data: HTTP Error 403: Forbidden`. That failure is\n * identical from every exit: direct, datacenter and residential all produced it,\n * so climbing the ladder spent both paid rungs to reproduce it.\n *\n * The `android` client resolves a progressive format whose URL is honoured from\n * the same address — verified on two unrelated videos from the Runtime's own IP\n * after the default clients had 403'd. It is a *fallback*, not the default: it\n * currently offers only 360p, so it is worth reaching for once the good formats\n * have already been refused and worth nothing before that.\n */\n\n/** yt-dlp's argv for the fallback client. Appended, never substituted. */\nexport const YOUTUBE_FALLBACK_CLIENT_ARGS = [\"--extractor-args\", \"youtube:player_client=android\"] as const;\n\n/**\n * The refusal that happens *after* a format was chosen.\n *\n * Deliberately this exact phrase rather than a bare 403: a 403 raised while\n * fetching the page is a block on us, which the egress ladder already owns and\n * can actually fix. This one is raised on the media URL yt-dlp was just given,\n * which no exit IP changes.\n */\nconst MEDIA_FETCH_REFUSED = \"unable to download video data\";\n\nconst YOUTUBE_HOSTS = [\"youtube.com\", \"youtu.be\"];\n\nfunction isYouTube(url: string): boolean {\n let host: string;\n try {\n host = new URL(url).hostname.toLowerCase();\n } catch {\n return false;\n }\n return YOUTUBE_HOSTS.some((known) => host === known || host.endsWith(`.${known}`));\n}\n\n/** True when re-running with the fallback client is worth one more attempt. */\nexport function needsPlayerClientRetry(url: string, stderr: string): boolean {\n return isYouTube(url) && stderr.toLowerCase().includes(MEDIA_FETCH_REFUSED);\n}\n","import type { BlockSignal } from \"@baker/proxy\";\n\n/**\n * yt-dlp's stderr, read as the same signal every other surface produces.\n *\n * Deliberately only extraction — `shouldEscalate` still makes the decision, so\n * yt-dlp cannot drift into its own private idea of what a block is. The two\n * halves have to stay separate: a downloader that decided for itself would be\n * the one surface where a 404 could cost money.\n *\n * The parsing exists because yt-dlp gives us prose and an exit code, not a\n * response. There is no status line to read, so the status has to be recovered\n * from the sentence it was printed into.\n */\n\n/** `HTTP Error 403: Forbidden`, in any of the shapes yt-dlp writes it. */\nconst HTTP_ERROR = /HTTP Error (\\d{3})/i;\n\n/**\n * Refusals that arrive as prose with no status at all.\n *\n * YouTube's bot check is the one that matters: it is the single most common\n * reason a datacenter IP fails a download that a residential one completes, and\n * it looks like an ordinary successful page to everything except a human\n * reading the text.\n */\nconst CHALLENGE_PHRASES = [\n \"sign in to confirm you're not a bot\",\n \"sign in to confirm you’re not a bot\",\n \"confirm you are not a bot\",\n \"unusual traffic\",\n \"captcha\",\n];\n\n/**\n * Prose that must never escalate, checked *before* the phrases above.\n *\n * \"Private video. Sign in if you've been granted access\" contains a sign-in\n * demand and is not a block — no exit IP on earth opens it, and treating it as\n * one would spend the expensive rung on every private link an agent is handed.\n */\nconst NOT_A_BLOCK_PHRASES = [\"private video\", \"video unavailable\", \"members-only\", \"this video is available to\"];\n\n/**\n * Our own proxy refusing us, which yt-dlp writes without an `HTTP Error` prefix.\n *\n * Checked before everything else. A 407 cannot come from an origin server, so\n * recovering it is what lets `isProxyFailure` separate \"the platform's egress\n * credentials are wrong\" from \"this site blocked us\" — opposite remediations,\n * and the first is otherwise invisible: a rotated password reads downstream as\n * every site having simultaneously started refusing us.\n */\nconst PROXY_TUNNEL_FAILURE = /tunnel connection failed: 407|proxy authentication required|http error 407/i;\n\nexport function ytDlpBlockSignal(stderr: string): BlockSignal {\n const text = stderr.toLowerCase();\n\n if (PROXY_TUNNEL_FAILURE.test(stderr)) return { status: 407 };\n\n if (NOT_A_BLOCK_PHRASES.some((phrase) => text.includes(phrase))) return {};\n\n // First, not last. yt-dlp prints a line per retry, so the final line is\n // usually \"giving up after 3 retries\" — the verdict that started the sequence\n // is the one that says why.\n const status = HTTP_ERROR.exec(stderr);\n if (status) return { status: Number(status[1]) };\n\n if (CHALLENGE_PHRASES.some((phrase) => text.includes(phrase))) return { challenge: true };\n return {};\n}\n","import type { RawAssetOutput } from \"../../client/backend-client.ts\";\nimport { BackendHttpError } from \"../../client/http.ts\";\nimport type { ExecCtx } from \"../../engine/context.ts\";\nimport type { NodeDefinition } from \"../../engine/define.ts\";\nimport { resolveOutputKinds } from \"../../engine/define.ts\";\nimport { NodeExecutionError } from \"../../engine/errors.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { buildRef } from \"../../storage/asset-store.ts\";\nimport { ensureUploaded } from \"./upload.ts\";\n\ntype RemoteSpec<I, P, O> = Omit<NodeDefinition<I, P, O>, \"execute\" | \"location\">;\n\n/**\n * Build a `defineNode`-shaped remote node that POSTs to /api/canvas/nodes/exec\n * and ingests any returned asset bytes locally via ctx.assets.ingestRemote.\n */\nexport function delegated<I, P, O>(spec: RemoteSpec<I, P, O>): NodeDefinition<I, P, O> {\n return {\n ...spec,\n location: \"remote\",\n execute: async ({ inputs, params, ctx }) => {\n return (await callBackendExec({\n nodeType: spec.id,\n nodeVersion: spec.version,\n params,\n inputs,\n outputKinds: resolveOutputKinds(spec.outputKinds, params),\n ctx,\n })) as O;\n },\n };\n}\n\n/**\n * Shared \"POST to /api/canvas/nodes/exec and ingest the returned assets\"\n * pipeline. Used by `delegated()` for remote nodes, and by the `ingest` node\n * which dispatches its `direct_fetch` and `firecrawl` strategies through the\n * same backend channel even though the node itself is registered as local.\n */\nexport async function callBackendExec(args: {\n nodeType: string;\n nodeVersion: string;\n params: unknown;\n inputs: unknown;\n outputKinds: Record<string, AssetRef[\"kind\"]>;\n ctx: ExecCtx;\n}): Promise<Record<string, unknown>> {\n try {\n const serialized = await serializeInputs(args.inputs, args.ctx);\n // Scoped to the run so a deliberate re-run (e.g. cache bypass) generates\n // fresh, while HTTP-level retries within one execution replay the stored\n // backend response instead of dispatching (and charging) twice.\n const idempotencyKey = args.ctx.cacheKey ? `${args.ctx.canvasRunId}:${args.ctx.cacheKey}` : undefined;\n const res = await args.ctx.client.exec(\n {\n nodeType: args.nodeType,\n nodeVersion: args.nodeVersion,\n params: args.params,\n inputs: serialized,\n idempotency_key: idempotencyKey,\n canvas_run_id: args.ctx.canvasRunId,\n node_id: args.ctx.nodeId,\n },\n args.ctx.signal,\n );\n if (typeof res.credits_charged === \"number\") args.ctx.reportCharge?.(res.credits_charged);\n return await ingestOutputs(res.outputs, args.ctx, args.outputKinds);\n } catch (e) {\n throw mapClientError(args.ctx, e);\n }\n}\n\nasync function serializeInputs(inputs: unknown, ctx: ExecCtx): Promise<Record<string, unknown>> {\n if (!inputs || typeof inputs !== \"object\") return {};\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(inputs as Record<string, unknown>)) {\n out[k] = await stripLocalRefs(v, ctx);\n }\n return out;\n}\n\nasync function stripLocalRefs(value: unknown, ctx: ExecCtx): Promise<unknown> {\n if (Array.isArray(value)) return Promise.all(value.map((v) => stripLocalRefs(v, ctx)));\n if (value && typeof value === \"object\") {\n if (\"kind\" in value && \"sha256\" in value && \"mime\" in value) {\n // A local-node output (ffmpeg/magick via runCli) has `path` but no `url`.\n // Upload it to R2 first so the remote backend can fetch it — otherwise it\n // receives `url: undefined` and rejects the input. Idempotent for refs\n // that already carry a `url` (ingest/remote outputs).\n const ref = await ensureUploaded(value as AssetRef, ctx);\n // Strip local-only `path` and pass through what the backend can consume.\n return {\n kind: ref.kind,\n url: ref.url,\n sha256: ref.sha256,\n mime: ref.mime,\n metadata: ref.metadata,\n };\n }\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = await stripLocalRefs(v, ctx);\n return out;\n }\n return value;\n}\n\nasync function ingestOutputs(\n outputs: Record<string, unknown>,\n ctx: ExecCtx,\n kinds: Record<string, AssetRef[\"kind\"]>,\n): Promise<Record<string, unknown>> {\n const result: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(outputs)) {\n const declaredKind = kinds[name];\n result[name] = await ingestValue(value, ctx, declaredKind);\n }\n return result;\n}\n\nasync function ingestValue(value: unknown, ctx: ExecCtx, declaredKind?: AssetRef[\"kind\"]): Promise<unknown> {\n if (Array.isArray(value)) {\n const out: unknown[] = [];\n for (const v of value) out.push(await ingestValue(v, ctx, declaredKind));\n return out;\n }\n if (isRawAsset(value)) {\n const kind = value.kind ?? declaredKind ?? \"json\";\n // No local consumer needs the bytes on disk: the backend already computed\n // the sha256 and persisted a durable R2 URL, so skip the redundant\n // R2→sandbox download and keep the ref URL-only (no local `path`). The run\n // record still works — `isPersistedAssetRef` is true for a canvas-assets URL.\n if (ctx.downloadOutputs === false) {\n // Match the shape `ingestRemote` would have produced (top-level\n // width/height/duration_ms hoisted from metadata) so downstream consumers\n // that read those fields are unaffected — just without the local `path`.\n return buildRef({ kind, sha: value.sha256, mime: value.mime, url: value.url, metadata: value.metadata });\n }\n return ctx.assets.ingestRemote({\n kind,\n url: value.url,\n sha256: value.sha256,\n mime: value.mime,\n metadata: value.metadata,\n });\n }\n return value;\n}\n\nfunction isRawAsset(v: unknown): v is RawAssetOutput {\n return Boolean(\n v &&\n typeof v === \"object\" &&\n \"url\" in v &&\n \"sha256\" in v &&\n \"mime\" in v &&\n typeof (v as { url: unknown }).url === \"string\" &&\n typeof (v as { sha256: unknown }).sha256 === \"string\",\n );\n}\n\nfunction mapClientError(ctx: ExecCtx, e: unknown): Error {\n if (e instanceof BackendHttpError) {\n const d = e.detail;\n if (d.kind === \"provider\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"remote\",\n status: d.status,\n code: d.code,\n message: d.message,\n provider: d.provider,\n // The backend's real classification — providerErr pins the status to 502,\n // so this flag (not the status) is the source of truth for retryability.\n retryable: d.retryable,\n });\n }\n if (d.kind === \"content_policy\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"content_policy\",\n provider: d.provider,\n message: d.message,\n });\n }\n if (d.kind === \"timeout\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: \"timeout\", provider: d.provider });\n }\n if (d.kind === \"validation\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"remote\",\n status: d.status,\n // The backend's own code (`model_invalid_params`, `cost_limit_exceeded`,\n // `invalid_inputs`, …) says WHICH refusal; `invalid_params` only when it gave none.\n code: d.code ?? \"invalid_params\",\n message: d.message,\n // The request itself was refused; it fails the same way until the input changes.\n retryable: false,\n });\n }\n if (d.kind === \"unauthorized\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"remote\",\n status: d.status,\n code: \"unauthorized\",\n message: d.message,\n });\n }\n if (d.kind === \"network\") {\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: \"network\", cause: d.cause });\n }\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"remote\",\n status: d.status,\n code: \"server_error\",\n message: d.message,\n });\n }\n return new NodeExecutionError(ctx.nodeId, ctx.nodeType, { kind: \"local\", cause: e });\n}\n","import { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { getToolVersion, runCli } from \"./lib/cli-runner.ts\";\n\nconst FFMPEG_BIN = \"ffmpeg\";\n\nconst FORMAT_EXT = { mp3: \"mp3\", wav: \"wav\", m4a: \"m4a\" } as const;\nconst FORMAT_CODEC = { mp3: \"libmp3lame\", wav: \"pcm_s16le\", m4a: \"aac\" } as const;\n\nconst Track = z\n .object({\n /** Input slot name (must match a wired `inputs.<slot>` audio ref). */\n slot: z.string().min(1),\n /** When this track starts on the timeline, seconds from 0. */\n start_s: z.number().min(0),\n /**\n * Optional hard cap on this track's length, seconds. The clip is trimmed BEFORE\n * placement, so a source that runs long (an over-long voice extract, a converted\n * track that came back oversized) cannot bleed into the next track's window.\n */\n duration_s: z.number().positive().optional(),\n /**\n * Level adjustment in dB — negative attenuates. A MUSIC BED under speech\n * belongs at −20 dB (see the `describe` text: this number is a settled\n * decision, not a per-cut judgement).\n */\n gain_db: z\n .number()\n .optional()\n .describe(\n \"Level adjustment in dB (negative attenuates, 0 = untouched). A music bed under speech sits at −20 dB \" +\n \"and is sidechain-ducked as well; −12 dB crowded the voice and −13 dB was rejected by the client as \" +\n \"too loud. If a silent tail (an end card, a logo plate) sounds empty, do NOT raise the bed — wire the \" +\n \"same bed as a SECOND track windowed over the tail with its own higher gain. Measuring loudness across \" +\n \"a window that includes the silent tail is what makes a correct bed look quiet.\",\n ),\n })\n .strict();\n\n/**\n * Sidechain-duck one track (a music bed) under one or more key tracks (the voice):\n * whenever the key tracks carry signal, the ducked track's level drops, then recovers\n * in the gaps. Makes a music bed sit *dynamically* under speech instead of at a flat\n * level. `threshold` is linear 0–1 (lower = ducks more readily), `ratio` the\n * compression amount, `attack`/`release` in ms.\n */\nconst DuckSpec = z\n .object({\n track: z.string().min(1).describe(\"Slot to duck — the music bed.\"),\n against: z.array(z.string().min(1)).min(1).describe(\"Key slots whose signal ducks it — the voice tracks.\"),\n threshold: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe(\"Linear 0–1, not dB. Lower ducks more readily. Default 0.03.\"),\n ratio: z.number().min(1).max(20).optional().describe(\"How hard it ducks. Default 8; 12 for a firm hold.\"),\n // MILLISECONDS is spelled out because a canvas shipped `attack: 0.05,\n // release: 0.45` — read as seconds — which is a 0.45 ms release: the bed\n // snapped back to full level between syllables and the client heard the\n // music as \"too loud\" at a level that measured correct.\n attack: z\n .number()\n .min(0.01)\n .optional()\n .describe(\"MILLISECONDS (not seconds). Typical 5–50; default 5. A value below 0.5 is a unit mistake.\"),\n release: z\n .number()\n .min(0.01)\n .optional()\n .describe(\n \"MILLISECONDS (not seconds). Typical 200–800; default 300. Anything under ~10 makes the bed pump back \" +\n \"up between words and is a unit mistake.\",\n ),\n })\n .strict();\n\nconst AudioTimelineParams = z\n .object({\n tracks: z.array(Track).min(1),\n /** Final track length in ms — pads short / trims long. Defaults to the natural mix length. */\n total_ms: z.number().int().positive().optional(),\n output_format: z.enum([\"mp3\", \"wav\", \"m4a\"]).optional(),\n /** Optional sidechain duck of one track under others (e.g. music under voice). */\n duck: DuckSpec.optional(),\n })\n .strict();\n\ntype AudioTimelineParamsT = z.infer<typeof AudioTimelineParams>;\n\nconst AudioTimelineInputs = z.record(z.string(), z.unknown());\nconst AudioTimelineOutputs = z.object({ audio: z.custom<AssetRef>() }).strict();\n\n/**\n * Build the ffmpeg argv for an audio timeline: one `-i` per track (in order,\n * so track i is ffmpeg input i), an `adelay` (+ optional `volume`) per track\n * to place it, an `amix` to combine them (`normalize=0` preserves the relative\n * levels set via `gain_db`), and an optional `apad`+`atrim` to pin the final\n * length. Pure + deterministic so it unit-tests without ffmpeg.\n */\n/**\n * Sidechain-duck `duck.track` under `duck.against` IN PLACE: split each key label into a\n * mix-copy + key-copy, combine the keys, and compress the ducked track against them.\n * Rewrites the relevant `labelFor` entries (music → ducked, each key → its mix-copy) and\n * appends to `filterChains`. No-op when the named slots aren't present (validated upstream).\n */\nfunction applyDuck(params: AudioTimelineParamsT, labelFor: string[], filterChains: string[]): void {\n const duck = params.duck;\n if (!duck) return;\n const trackIdx = params.tracks.findIndex((t) => t.slot === duck.track);\n const keyIdxs = duck.against.map((s) => params.tracks.findIndex((t) => t.slot === s)).filter((i) => i >= 0);\n if (trackIdx < 0 || keyIdxs.length === 0) return;\n\n const keyLabels: string[] = [];\n for (const ki of keyIdxs) {\n const base = labelFor[ki];\n filterChains.push(`[${base}]asplit=2[${base}m][${base}k]`);\n labelFor[ki] = `${base}m`;\n keyLabels.push(`[${base}k]`);\n }\n let keyOut = keyLabels[0] as string;\n if (keyLabels.length > 1) {\n filterChains.push(`${keyLabels.join(\"\")}amix=inputs=${keyLabels.length}:normalize=0[duckkey]`);\n keyOut = \"[duckkey]\";\n }\n const th = duck.threshold ?? 0.03;\n const ra = duck.ratio ?? 8;\n const at = duck.attack ?? 5;\n const re = duck.release ?? 300;\n filterChains.push(\n `[${labelFor[trackIdx]}]${keyOut}sidechaincompress=threshold=${th}:ratio=${ra}:attack=${at}:release=${re}[ducked]`,\n );\n labelFor[trackIdx] = \"ducked\";\n}\n\nexport function buildAudioTimelineArgs(params: AudioTimelineParamsT): string[] {\n const fmt = params.output_format ?? \"mp3\";\n const inputArgs: string[] = [];\n const filterChains: string[] = [];\n // track index → the filter label that carries it into the final amix. Sidechain\n // ducking rewrites a couple of these (the music label → its ducked copy, each key\n // label → its split mix-copy) without changing the per-track placement chains.\n const labelFor: string[] = [];\n params.tracks.forEach((track, i) => {\n inputArgs.push(\"-i\", `{{in.${track.slot}}}`);\n const delayMs = Math.round(track.start_s * 1000);\n // The cap trims BEFORE the delay so it bounds the clip itself, not the timeline.\n const steps = [\n ...(track.duration_s !== undefined ? [`atrim=0:${track.duration_s}`] : []),\n `adelay=${delayMs}:all=1`,\n ];\n if (track.gain_db !== undefined) steps.push(`volume=${track.gain_db}dB`);\n const label = `a${i}`;\n filterChains.push(`[${i}:a]${steps.join(\",\")}[${label}]`);\n labelFor[i] = label;\n });\n\n applyDuck(params, labelFor, filterChains);\n\n const mixLabels = labelFor.map((l) => `[${l}]`);\n let graph = `${filterChains.join(\";\")};${mixLabels.join(\"\")}amix=inputs=${params.tracks.length}:normalize=0`;\n if (params.total_ms !== undefined) {\n const totalS = params.total_ms / 1000;\n graph += `[mixed];[mixed]apad,atrim=0:${totalS}`;\n }\n graph += \"[out]\";\n\n return [...inputArgs, \"-filter_complex\", graph, \"-map\", \"[out]\", \"-c:a\", FORMAT_CODEC[fmt], \"{{out.audio}}\"];\n}\n\nexport const audioTimelineNode = defineNode({\n id: \"audio_timeline\",\n version: \"1.2.0\",\n category: \"audio\",\n location: \"local\",\n summary:\n \"Place and mix several audio clips onto one timeline: each track starts at a given second (optionally level-adjusted in dB), then they're combined into a single track. Built for laying a music bed plus timed voiceover lines and sound effects under a video.\",\n when_to_use:\n \"Use to assemble a full audio bed from separately-generated clips — e.g. a `music` bed at 0 (ducked via `gain_db: -12`), each scene's `tts` voiceover at its scene start, and `sound_effect` hits at their timestamps. Wire each clip as `inputs.<slot>` (audio AssetRef) and list it in `params.tracks` with `{slot, start_s, duration_s?, gain_db?}` (`duration_s` hard-caps a clip so it can't bleed into the next track's window). Set `total_ms` to pin the final length to the video. Requires `ffmpeg` on PATH.\",\n inputs: AudioTimelineInputs,\n params: AudioTimelineParams,\n outputs: AudioTimelineOutputs,\n // The mixed timeline is an audio asset — declare it so strictly-typed consumers\n // (e.g. audio_voice_convert reading a merged per-speaker track) resolve its kind.\n outputKinds: { audio: \"audio\" },\n cost: () => ({ credits: 0, seconds_estimate: 10 }),\n\n validateExtra({ rawParams, rawInputs }) {\n const issues: Array<{ path: string; message: string }> = [];\n const parsed = AudioTimelineParams.safeParse(rawParams ?? {});\n if (!parsed.success) return issues; // shape errors already surfaced by the param validator\n const inputKeys = new Set(Object.keys((rawInputs as Record<string, unknown>) ?? {}));\n parsed.data.tracks.forEach((track, i) => {\n if (!inputKeys.has(track.slot)) {\n issues.push({\n path: `params.tracks[${i}].slot`,\n message: `track slot \"${track.slot}\" has no matching wired input — add inputs.${track.slot}`,\n });\n }\n });\n const duck = parsed.data.duck;\n if (duck) {\n const slots = new Set(parsed.data.tracks.map((t) => t.slot));\n if (!slots.has(duck.track)) {\n issues.push({ path: \"params.duck.track\", message: `duck.track \"${duck.track}\" is not one of the tracks` });\n }\n duck.against.forEach((s, k) => {\n if (!slots.has(s)) {\n issues.push({ path: `params.duck.against[${k}]`, message: `duck.against \"${s}\" is not one of the tracks` });\n }\n if (s === duck.track) {\n issues.push({ path: `params.duck.against[${k}]`, message: \"a track cannot duck against itself\" });\n }\n });\n }\n return issues;\n },\n\n async cacheKeyExtras() {\n return { ffmpeg_version: await getToolVersion(FFMPEG_BIN) };\n },\n\n async execute({ inputs, params, ctx }) {\n return await runCli({\n bin: FFMPEG_BIN,\n args: buildAudioTimelineArgs(params),\n inputs: inputs as Record<string, AssetRef | AssetRef[]>,\n outputs: { audio: { kind: \"audio\", ext: FORMAT_EXT[params.output_format ?? \"mp3\"] } },\n ctx,\n });\n },\n});\n","import { execFile as execFileCb, spawn } from \"node:child_process\";\nimport { copyFile, mkdtemp, readFile, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { ExecCtx } from \"../../../engine/context.ts\";\nimport type { AssetKind, AssetRef } from \"../../../schema/refs.ts\";\n\nconst execFile = promisify(execFileCb);\n\ntype CliOutputSpec = {\n kind: AssetKind;\n ext: string;\n};\n\ntype RunCliArgs = {\n /** Binary to spawn (must be on PATH). */\n bin: string;\n /** argv list with `{{in.<slot>}}` and `{{out.<name>}}` placeholders. */\n args: readonly string[];\n /** Input slots — each becomes `{{in.<slot>}}` (single asset) or `{{in.<slot>.<index>}}` (array). */\n inputs: Record<string, AssetRef | AssetRef[]>;\n /** Output declarations — each becomes `{{out.<name>}}`; engine allocates the path. */\n outputs: Record<string, CliOutputSpec>;\n ctx: ExecCtx;\n /** Process timeout. Default: 10 minutes. */\n timeoutMs?: number;\n};\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;\n\nconst URL_SCHEME_RE = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//;\n\nconst MIME_BY_EXT: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n avif: \"image/avif\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n m4a: \"audio/mp4\",\n ogg: \"audio/ogg\",\n flac: \"audio/flac\",\n};\n\nfunction mimeForExt(ext: string): string {\n const clean = ext.startsWith(\".\") ? ext.slice(1) : ext;\n return MIME_BY_EXT[clean.toLowerCase()] ?? \"application/octet-stream\";\n}\n\nfunction extForAssetRef(ref: AssetRef): string {\n // Prefer the on-disk extension if we have a local path; fall back to mime.\n if (ref.path) {\n const e = path.extname(ref.path);\n if (e) return e;\n }\n const reverse: Record<string, string> = {\n \"image/png\": \".png\",\n \"image/jpeg\": \".jpg\",\n \"image/webp\": \".webp\",\n \"video/mp4\": \".mp4\",\n \"video/webm\": \".webm\",\n \"video/quicktime\": \".mov\",\n \"audio/mpeg\": \".mp3\",\n \"audio/wav\": \".wav\",\n \"audio/mp4\": \".m4a\",\n \"audio/ogg\": \".ogg\",\n };\n return reverse[ref.mime] ?? \".bin\";\n}\n\n/**\n * Build the placeholder → absolute-path lookup table for one node invocation.\n * - Single AssetRef inputs register as `in.<slot>`.\n * - Array AssetRef inputs register each element as `in.<slot>.<index>`.\n * - Declared outputs register as `out.<name>`.\n *\n * Returns the lookup table plus the list of staged input paths and allocated\n * output paths so the caller can copy/read them.\n */\ntype Placeholders = {\n lookup: Map<string, string>;\n stagedInputs: { srcPath: string; destPath: string }[];\n outputPaths: { name: string; absPath: string; spec: CliOutputSpec }[];\n};\n\nfunction planArrayInputSlot(\n tmpDir: string,\n slot: string,\n values: AssetRef[],\n lookup: Map<string, string>,\n stagedInputs: Placeholders[\"stagedInputs\"],\n): void {\n for (let i = 0; i < values.length; i++) {\n const ref = values[i];\n if (!ref) continue;\n if (!ref.path) throw new Error(`cli-runner: inputs.${slot}[${i}] has no local path`);\n const dest = path.join(tmpDir, `in_${slot}_${i}${extForAssetRef(ref)}`);\n stagedInputs.push({ srcPath: ref.path, destPath: dest });\n lookup.set(`in.${slot}.${i}`, dest);\n }\n}\n\nfunction planSingleInputSlot(\n tmpDir: string,\n slot: string,\n ref: AssetRef,\n lookup: Map<string, string>,\n stagedInputs: Placeholders[\"stagedInputs\"],\n): void {\n if (!ref.path) throw new Error(`cli-runner: inputs.${slot} has no local path`);\n const dest = path.join(tmpDir, `in_${slot}${extForAssetRef(ref)}`);\n stagedInputs.push({ srcPath: ref.path, destPath: dest });\n lookup.set(`in.${slot}`, dest);\n}\n\nfunction planOutputs(\n tmpDir: string,\n outputs: Record<string, CliOutputSpec>,\n lookup: Map<string, string>,\n): Placeholders[\"outputPaths\"] {\n const outputPaths: Placeholders[\"outputPaths\"] = [];\n for (const [name, spec] of Object.entries(outputs)) {\n const ext = spec.ext.startsWith(\".\") ? spec.ext : `.${spec.ext}`;\n const absPath = path.join(tmpDir, `out_${name}${ext}`);\n outputPaths.push({ name, absPath, spec });\n lookup.set(`out.${name}`, absPath);\n }\n return outputPaths;\n}\n\nfunction planPlaceholders(\n tmpDir: string,\n inputs: Record<string, AssetRef | AssetRef[]>,\n outputs: Record<string, CliOutputSpec>,\n): Placeholders {\n const lookup = new Map<string, string>();\n const stagedInputs: Placeholders[\"stagedInputs\"] = [];\n\n for (const [slot, value] of Object.entries(inputs)) {\n if (Array.isArray(value)) {\n planArrayInputSlot(tmpDir, slot, value, lookup, stagedInputs);\n } else {\n planSingleInputSlot(tmpDir, slot, value, lookup, stagedInputs);\n }\n }\n\n const outputPaths = planOutputs(tmpDir, outputs, lookup);\n return { lookup, stagedInputs, outputPaths };\n}\n\nconst PLACEHOLDER_RE = /\\{\\{\\s*([a-zA-Z0-9_.]+)\\s*\\}\\}/g;\n\n/**\n * Substitute `{{in.X}}` / `{{out.Y}}` placeholders in a single arg string.\n * Returns the new string and the set of placeholder keys it consumed.\n * Throws if a placeholder isn't in the lookup.\n */\nfunction substituteArg(arg: string, lookup: Map<string, string>): string {\n return arg.replace(PLACEHOLDER_RE, (_match, key: string) => {\n const value = lookup.get(key);\n if (value === undefined) {\n throw new Error(\n `cli-runner: unknown placeholder \"{{${key}}}\" — declared inputs/outputs: ${[...lookup.keys()].join(\", \")}`,\n );\n }\n return value;\n });\n}\n\n/**\n * After substitution, reject arg strings that look like raw filesystem paths\n * outside our staging dir, or URL inputs. The threat model is \"trusted local\n * canvas shouldn't accidentally reach outside the engine's content-addressed\n * world.\" Args that resolve to staging-dir paths via placeholders pass cleanly.\n */\nfunction rejectRawPaths(substituted: string, original: string, stagingDir: string): void {\n // URL schemes — block ffmpeg/magick from fetching arbitrary http/file/data inputs.\n if (URL_SCHEME_RE.test(substituted)) {\n throw new Error(\n `cli-runner: URL-style arg \"${original}\" not allowed — ingest the asset upstream (e.g. via the ingest node) and wire it as an input slot.`,\n );\n }\n // Home-relative — never allow.\n if (substituted.startsWith(\"~/\") || substituted === \"~\") {\n throw new Error(`cli-runner: home-relative path \"${original}\" not allowed in args.`);\n }\n // Absolute paths must live under the staging dir.\n if (substituted.startsWith(\"/\")) {\n const resolved = path.resolve(substituted);\n if (!resolved.startsWith(`${stagingDir}${path.sep}`) && resolved !== stagingDir) {\n throw new Error(\n `cli-runner: raw filesystem path \"${original}\" not allowed — declare an input slot and use {{in.<slot>}} instead.`,\n );\n }\n }\n // Relative path tokens — `./foo`, `../foo` — block.\n if (substituted.startsWith(\"./\") || substituted.startsWith(\"../\")) {\n throw new Error(`cli-runner: relative path \"${original}\" not allowed in args.`);\n }\n}\n\nasync function stageInputs(staged: Placeholders[\"stagedInputs\"]): Promise<void> {\n for (const { srcPath, destPath } of staged) {\n await copyFile(srcPath, destPath);\n }\n}\n\ntype SpawnResult = { code: number; stderr: string };\n\nfunction spawnAndWait(bin: string, args: readonly string[], timeoutMs: number): Promise<SpawnResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(bin, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n const stderrChunks: Buffer[] = [];\n let stderrLen = 0;\n const MAX_STDERR = 256 * 1024;\n\n child.stdout.on(\"data\", () => {\n // Discard stdout — large CLI tools can flood it; we surface stderr on failure.\n });\n child.stderr.on(\"data\", (chunk: Buffer) => {\n if (stderrLen < MAX_STDERR) {\n stderrChunks.push(chunk);\n stderrLen += chunk.length;\n }\n });\n\n const timer = setTimeout(() => {\n child.kill(\"SIGKILL\");\n reject(new Error(`cli-runner: ${bin} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n child.on(\"error\", (err) => {\n clearTimeout(timer);\n reject(new Error(`cli-runner: failed to spawn \"${bin}\" — ${err.message}`));\n });\n\n child.on(\"close\", (code) => {\n clearTimeout(timer);\n const stderr = Buffer.concat(stderrChunks).toString(\"utf8\");\n resolve({ code: code ?? -1, stderr });\n });\n });\n}\n\nfunction tailLines(text: string, maxLines: number): string {\n const lines = text.split(\"\\n\");\n return lines.slice(-maxLines).join(\"\\n\");\n}\n\n/**\n * Run a local CLI binary with engine-staged inputs and engine-allocated outputs.\n *\n * Contract:\n * - `args` are passed as argv (no shell). `{{in.<slot>}}` / `{{out.<name>}}`\n * placeholders are substituted with absolute paths under a per-node tmp dir.\n * - After substitution, raw filesystem paths and URL-style inputs are rejected.\n * - Every declared `outputs` entry must exist and be non-empty after exit.\n * - Outputs are ingested into the asset store as the declared `kind`.\n * - Tmp dir is removed in `finally`.\n */\nexport async function runCli(opts: RunCliArgs): Promise<Record<string, AssetRef>> {\n const { bin, args, inputs, outputs, ctx, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;\n\n const tmpDir = await mkdtemp(path.join(tmpdir(), `cli-${bin.replace(/[^a-z0-9]/gi, \"\")}-`));\n try {\n const { lookup, stagedInputs, outputPaths } = planPlaceholders(tmpDir, inputs, outputs);\n await stageInputs(stagedInputs);\n ctx.log(`staged ${stagedInputs.length} inputs in ${tmpDir}`);\n\n const substituted = args.map((arg) => {\n const next = substituteArg(arg, lookup);\n rejectRawPaths(next, arg, tmpDir);\n return next;\n });\n\n ctx.log(`running ${bin} (${substituted.length} args)`);\n const { code, stderr } = await spawnAndWait(bin, substituted, timeoutMs);\n if (code !== 0) {\n throw new Error(`cli-runner: ${bin} exited ${code}\\n${tailLines(stderr, 40)}`);\n }\n\n const result: Record<string, AssetRef> = {};\n for (const { name, absPath, spec } of outputPaths) {\n const s = await stat(absPath).catch(() => null);\n if (!s?.isFile() || s.size === 0) {\n throw new Error(`cli-runner: declared output \"${name}\" missing or empty at ${absPath}`);\n }\n const bytes = await readFile(absPath);\n const ref = await ctx.assets.ingestBytes({\n bytes: Buffer.from(bytes),\n kind: spec.kind,\n mime: mimeForExt(spec.ext),\n });\n result[name] = ref;\n }\n return result;\n } finally {\n await rm(tmpDir, { recursive: true, force: true }).catch((e) => {\n ctx.log(`cli-runner: tmp cleanup failed (${(e as Error).message})`);\n });\n }\n}\n\n/**\n * Capture the binary's `-version` first line for cache-key inclusion.\n * Memoized per-binary per-process. Bumping the host CLI busts cache automatically.\n */\nconst VERSION_CACHE = new Map<string, string>();\n\nexport async function getToolVersion(bin: string): Promise<string> {\n const cached = VERSION_CACHE.get(bin);\n if (cached !== undefined) return cached;\n try {\n const { stdout } = await execFile(bin, [\"-version\"], { encoding: \"utf-8\", maxBuffer: 1024 * 1024 });\n const firstLine = stdout.split(\"\\n\")[0]?.trim() ?? \"\";\n VERSION_CACHE.set(bin, firstLine);\n return firstLine;\n } catch {\n // Don't fail the run on version probe issues; cache key folds in a stable marker.\n const fallback = `${bin}:unknown`;\n VERSION_CACHE.set(bin, fallback);\n return fallback;\n }\n}\n\n// Exported for tests.\nexport const _internal = {\n planPlaceholders,\n substituteArg,\n rejectRawPaths,\n mimeForExt,\n extForAssetRef,\n};\n","import { z } from \"zod\";\nimport { defineNode, type StaticTiming } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { getToolVersion, runCli } from \"./lib/cli-runner.ts\";\n\nconst FFMPEG_BIN = \"ffmpeg\";\n\nconst Loudnorm = z\n .object({\n i: z.number().default(-14).describe(\"Integrated loudness target, LUFS. -14 is the social/streaming target.\"),\n tp: z.number().default(-1.5).describe(\"True-peak ceiling, dBTP.\"),\n lra: z.number().default(11).describe(\"Loudness range target, LU.\"),\n })\n .strict();\n\nexport const AvMuxParams = z\n .object({\n loudnorm: z\n .union([z.literal(false), Loudnorm])\n .default({ i: -14, tp: -1.5, lra: 11 })\n .describe(\n \"Loudness normalization of the soundtrack (EBU R128). The raw mix is a quiet track that reads as \" +\n '\"no sound\" in casual players, so it is on by default at -14 LUFS / -1.5 dBTP. `false` keeps the mix level.',\n ),\n audio_bitrate: z\n .string()\n .regex(/^\\d+k$/, 'a bitrate like \"192k\"')\n .default(\"192k\")\n .describe(\"AAC bitrate of the delivered soundtrack.\"),\n sample_rate: z.number().int().positive().default(48000).describe(\"Delivered sample rate, Hz.\"),\n length: z\n .union([z.literal(\"shortest\"), z.object({ total_s: z.number().positive() }).strict()])\n .default(\"shortest\")\n .describe(\n \"`shortest` ends the file with the shorter stream, so a picture and mix pinned to the same timeline \" +\n \"come out exact (a longer one is silently cut: `canvas validate` checks both against the timeline). \" +\n \"`{ total_s }` cuts both streams at that second; they must each run at least that long.\",\n ),\n })\n .strict();\nexport type AvMuxParamsT = z.infer<typeof AvMuxParams>;\n\nconst AvMuxInputs = z\n .object({\n video: z.custom<AssetRef>().describe(\"The finished picture (its own audio, if any, is dropped).\"),\n audio: z.custom<AssetRef>().describe(\"The mixed soundtrack, usually an `audio_timeline`.\"),\n })\n .strict();\nconst AvMuxOutputs = z.object({ video: z.custom<AssetRef>() }).strict();\n\n/** The audio filter: optional loudness normalization, always an upmix to stereo. */\nfunction audioFilter(params: AvMuxParamsT): string {\n const stereo = \"aformat=channel_layouts=stereo\";\n if (params.loudnorm === false) return stereo;\n const { i, tp, lra } = params.loudnorm;\n return `loudnorm=I=${i}:TP=${tp}:LRA=${lra},${stereo}`;\n}\n\n/**\n * The ffmpeg argv the node runs: the picture stream-copied (no generational loss),\n * the soundtrack normalized and encoded, the length decided by `params.length`.\n */\nexport function compileAvMuxArgs(params: AvMuxParamsT): string[] {\n return [\n \"-i\",\n \"{{in.video}}\",\n \"-i\",\n \"{{in.audio}}\",\n \"-map\",\n \"0:v:0\",\n \"-map\",\n \"1:a:0\",\n \"-c:v\",\n \"copy\",\n \"-af\",\n audioFilter(params),\n \"-c:a\",\n \"aac\",\n \"-b:a\",\n params.audio_bitrate,\n \"-ar\",\n String(params.sample_rate),\n ...(params.length === \"shortest\" ? [\"-shortest\"] : [\"-t\", params.length.total_s.toFixed(3)]),\n \"{{out.video}}\",\n ];\n}\n\n/** The delivered length and raster, before anything runs. */\nexport function avMuxTiming(\n params: AvMuxParamsT,\n inputs: Readonly<Record<string, StaticTiming | undefined>>,\n): StaticTiming {\n const { width, height } = inputs.video ?? {};\n const raster = { ...(width !== undefined ? { width } : {}), ...(height !== undefined ? { height } : {}) };\n if (params.length !== \"shortest\") return { ...raster, duration_s: params.length.total_s };\n const video = inputs.video?.duration_s;\n const audio = inputs.audio?.duration_s;\n if (video === undefined || audio === undefined) return raster;\n return { ...raster, duration_s: Math.min(video, audio) };\n}\n\nexport const avMuxNode = defineNode({\n id: \"av_mux\",\n version: \"1.0.0\",\n category: \"video\",\n location: \"local\",\n summary:\n \"Put a soundtrack under a finished picture: the video is stream-copied, the audio loudness-normalized (-14 LUFS by default), upmixed to stereo and encoded to AAC, and the file's length follows an explicit policy (the shorter stream, or a pinned total).\",\n when_to_use:\n \"Use as the last step of a video canvas (the scaffold's `final`). Start here: \" +\n '`{ \"type\": \"av_mux\", \"inputs\": { \"video\": \"$ref:overlaid.video\", \"audio\": \"$ref:audio_mix.audio\" }, ' +\n '\"params\": { \"length\": \"shortest\" } }`. Keep the default loudness unless a client asks for a quieter ' +\n \"master. Use `length: { total_s }` only when the picture and mix are deliberately longer than the cut. \" +\n \"Reach for a raw `ffmpeg` node only for a mux this cannot express (several audio tracks, burned subtitles).\",\n inputs: AvMuxInputs,\n params: AvMuxParams,\n outputs: AvMuxOutputs,\n outputKinds: { video: \"video\" },\n cost: () => ({ credits: 0, seconds_estimate: 10 }),\n staticTiming: ({ params, inputs }) => avMuxTiming(params, inputs),\n\n async cacheKeyExtras() {\n return { ffmpeg_version: await getToolVersion(FFMPEG_BIN) };\n },\n\n async execute({ inputs, params, ctx }) {\n return (await runCli({\n bin: FFMPEG_BIN,\n args: compileAvMuxArgs(params),\n inputs: { video: inputs.video, audio: inputs.audio },\n outputs: { video: { kind: \"video\", ext: \"mp4\" } },\n ctx,\n })) as { video: AssetRef };\n },\n});\n","import { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { ImageRef } from \"../../schema/refs.ts\";\n\nexport const collectNode = defineNode({\n id: \"collect\",\n version: \"1.0.0\",\n category: \"data\",\n location: \"local\",\n passthroughRefs: true,\n summary:\n \"Gather images from multiple upstream nodes into one ordered array — the standard terminal for multi-variant canvases whose final output is several images.\",\n when_to_use:\n \"Point the canvas `output` at this node when several independent branches (e.g. one image_generate per scene/variant) must ALL be finals. Wire `inputs.images` as an array of refs like `$ref:gen_billboard_03.images#0` — each final inherits its producer node id as its label (or set `params.labels` to override), so variants stay identifiable in the dashboard and selection.\",\n inputs: z.object({ images: z.array(ImageRef).min(1) }).strict(),\n params: z.object({ labels: z.array(z.string().min(1)).min(1).optional() }).strict(),\n outputs: z.object({ images: z.array(ImageRef).min(1) }).strict(),\n outputKinds: { images: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 0 }),\n // Arity is only knowable at validate time when `images` is a literal array;\n // a single `$ref:` string to an upstream array output defers to runtime.\n validateExtra: ({ rawParams, rawInputs }) => {\n const labels = (rawParams as { labels?: unknown } | undefined)?.labels;\n if (!Array.isArray(labels)) return [];\n if (new Set(labels).size !== labels.length) {\n return [{ path: \"params.labels\", message: \"labels must be unique — each names one output variant\" }];\n }\n const images = (rawInputs as { images?: unknown } | undefined)?.images;\n if (Array.isArray(images) && labels.length !== images.length) {\n return [\n {\n path: \"params.labels\",\n message: `labels has ${labels.length} entries but ${images.length} images are wired — provide one label per image`,\n },\n ];\n }\n return [];\n },\n execute: ({ inputs }) => Promise.resolve({ images: inputs.images }),\n});\n","import { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { getToolVersion, runCli } from \"./lib/cli-runner.ts\";\n\nconst FFMPEG_BIN = \"ffmpeg\";\n\nconst OutputDecl = z\n .object({\n kind: z.enum([\"image\", \"video\", \"audio\"]),\n ext: z.string().min(1).max(8),\n })\n .strict();\n\nconst FfmpegParams = z\n .object({\n args: z.array(z.string()).min(1),\n outputs: z.record(z.string(), OutputDecl).default({}),\n })\n .strict();\n\n// Inputs are open — every canvas names its own slots. The cli-runner stages\n// any AssetRef-shaped value (or array of them) into the tmp dir keyed by slot.\nconst FfmpegInputs = z.record(z.string(), z.unknown());\n\n// Outputs are also open — keyed by the same names the canvas declared under\n// `params.outputs`. Each entry is an AssetRef.\nconst FfmpegOutputs = z.record(z.string(), z.custom<AssetRef>());\n\nexport const ffmpegNode = defineNode({\n id: \"ffmpeg\",\n version: \"2.0.0\",\n category: \"video\",\n location: \"local\",\n summary:\n \"Local ffmpeg passthrough. Declare inputs, declare outputs, write the argv you'd type on the command line. Inputs are staged into a tmp dir and reached via `{{in.<slot>}}`; declared outputs are allocated at `{{out.<name>}}` and ingested as assets when the run completes.\",\n when_to_use:\n \"Use whenever you'd reach for ffmpeg on the command line: trim, concat, scale, overlay, extract audio, generate thumbnails, mux/demux. Pass the full argv as `params.args` (no shell — argv array, no string interpolation). \" +\n \"Wire input files via `inputs.<slot>` (single AssetRef) or `inputs.<slot>: [...]` (array, addressed as `{{in.<slot>.0}}`, `{{in.<slot>.1}}`). \" +\n \"Declare every output file under `params.outputs` (name → {kind, ext}); the engine allocates the path, you reference it with `{{out.<name>}}`. \" +\n \"Requires `ffmpeg` on PATH. URL-style inputs (http://, file://) and raw filesystem paths in args are rejected — keep everything in the engine's asset model.\",\n inputs: FfmpegInputs,\n params: FfmpegParams,\n outputs: FfmpegOutputs,\n cost: () => ({ credits: 0, seconds_estimate: 15 }),\n\n async cacheKeyExtras() {\n return { ffmpeg_version: await getToolVersion(FFMPEG_BIN) };\n },\n\n async execute({ inputs, params, ctx }) {\n return await runCli({\n bin: FFMPEG_BIN,\n args: params.args,\n inputs: inputs as Record<string, AssetRef | AssetRef[]>,\n outputs: params.outputs,\n ctx,\n });\n },\n});\n","import { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { NodeExecutionError } from \"../../engine/errors.ts\";\nimport { FontRef, ImageRef } from \"../../schema/refs.ts\";\nimport { extForMime } from \"../../storage/asset-store.ts\";\nimport { uploadAndIngest } from \"../ingest.ts\";\nimport { stageAsset } from \"./lib/assets.ts\";\n\nconst require_ = createRequire(import.meta.url);\n\nexport const DEFAULT_SPECIMEN = [\n \"The quick brown fox jumps over the lazy dog\",\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\",\n \"abcdefghijklmnopqrstuvwxyz\",\n \"0123456789 !?&@#$%().,:;'\\\"-\",\n].join(\"\\n\");\n\nconst FontSpecimenParams = z\n .object({\n text: z.string().min(1).max(2000).optional().default(DEFAULT_SPECIMEN),\n font_size: z.number().int().min(8).max(512).optional().default(72),\n padding: z.number().int().min(0).max(512).optional().default(64),\n line_height: z.number().min(0.8).max(3).optional().default(1.35),\n max_width: z.number().int().min(256).max(4096).optional(),\n })\n .strict();\n\ntype Params = z.infer<typeof FontSpecimenParams>;\n\n// `.loose()` so the exact copy that will appear in the ad can be wired in from\n// an upstream node (e.g. text_generate) and referenced via `{{slot}}` in the\n// `text` param — render the specimen on the real headline, not placeholder text.\nconst FontSpecimenInputs = z.object({ font: FontRef }).loose();\nconst FontSpecimenOutputs = z.object({ image: ImageRef }).strict();\n\n// Retina export, consistent with hyperframe_snapshot.\nconst DEVICE_SCALE_FACTOR = 2;\nconst PAGE_TIMEOUT_MS = 30_000;\n\nfunction escapeHtml(text: string): string {\n return text\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#39;\");\n}\n\n/**\n * Self-contained specimen page: the staged font is the ONLY family declared,\n * so a load failure is detectable instead of silently falling back to a\n * system font (which would defeat the node's purpose).\n */\nfunction buildSpecimenHtml(params: Params, fontFilename: string): string {\n const widthRule = params.max_width === undefined ? \"width: max-content;\" : `max-width: ${params.max_width}px;`;\n return `<!doctype html>\n<html>\n<head>\n<meta charset=\"utf-8\">\n<style>\n @font-face {\n font-family: 'SpecimenFont';\n src: url('${fontFilename}');\n }\n html, body { margin: 0; background: #fff; }\n #specimen {\n font-family: 'SpecimenFont';\n color: #000;\n background: #fff;\n font-size: ${params.font_size}px;\n line-height: ${params.line_height};\n padding: ${params.padding}px;\n white-space: pre-wrap;\n overflow-wrap: break-word;\n ${widthRule}\n }\n</style>\n</head>\n<body><div id=\"specimen\">${escapeHtml(params.text)}</div></body>\n</html>\n`;\n}\n\nexport const fontSpecimenNode = defineNode({\n id: \"font_specimen\",\n version: \"1.1.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Render specimen text in a given font file to a PNG — black text on a white background — via headless Chromium. \" +\n \"Produces a typeface reference image an image model can replicate.\",\n when_to_use:\n \"Use when a generated image must contain text in a specific brand font: wire `image` into `image_generate`'s `reference` input so the model copies the typeface. \" +\n 'Input is a `font` asset (ttf/otf/woff/woff2) — get one via `ingest` with `expect: \"font\"`. ' +\n \"Default specimen covers a pangram, both alphabets, digits and punctuation; override with `params.text` (e.g. the exact headline to render).\",\n inputs: FontSpecimenInputs,\n params: FontSpecimenParams,\n outputs: FontSpecimenOutputs,\n outputKinds: { image: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 5 }),\n\n async execute({ inputs, params, ctx }) {\n const tmp = await mkdtemp(path.join(tmpdir(), \"font-specimen-\"));\n try {\n const fontFilename = `font.${extForMime(inputs.font.mime)}`;\n await stageAsset(inputs.font, tmp, fontFilename);\n\n const entryPath = path.join(tmp, \"index.html\");\n await writeFile(entryPath, buildSpecimenHtml(params, fontFilename), \"utf-8\");\n\n ctx.log(`rendering specimen (${params.font_size}px, ${params.text.split(\"\\n\").length} lines)`);\n\n const pwSpecifier = [\"play\", \"wright\"].join(\"\");\n const playwright = require_(pwSpecifier) as typeof import(\"playwright\");\n const browser = await playwright.chromium.launch({ headless: true });\n try {\n const context = await browser.newContext({\n viewport: { width: 1280, height: 720 },\n deviceScaleFactor: DEVICE_SCALE_FACTOR,\n });\n const page = await context.newPage();\n await page.goto(`file://${entryPath}`, { waitUntil: \"load\", timeout: PAGE_TIMEOUT_MS });\n\n // Fail loudly when Chromium can't parse the font — the page declares\n // no fallback family, but a broken file would otherwise still render\n // with the browser's last-resort font and silently lie downstream.\n const loaded = await page\n .evaluate(async () => {\n const faces = await document.fonts.load(\"16px 'SpecimenFont'\");\n await document.fonts.ready;\n return faces.length;\n })\n .catch(() => 0);\n if (loaded === 0) {\n throw new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"local\",\n cause: new Error(\n `font_specimen: Chromium could not load the font (${inputs.font.mime}) — is the file a valid ttf/otf/woff/woff2?`,\n ),\n });\n }\n\n // A font can load yet cover none of the specimen's characters (e.g. a\n // script-subset woff2 with no Latin glyphs) — Chromium then falls back\n // per character and the specimen would silently show a system font.\n // Detection: a covered char measures identically under both fallback\n // stacks (the font itself renders it); an uncovered char differs\n // (monospace vs serif fallback metrics diverge).\n const { covered, missing } = await page.evaluate((text: string) => {\n const chars = [...new Set(text.replace(/\\s/g, \"\"))];\n const c = document.createElement(\"canvas\").getContext(\"2d\");\n if (!c) return { covered: chars.length, missing: [] as string[] };\n const missingChars: string[] = [];\n for (const ch of chars) {\n c.font = \"64px 'SpecimenFont', monospace\";\n const a = c.measureText(ch).width;\n c.font = \"64px 'SpecimenFont', serif\";\n const b = c.measureText(ch).width;\n if (a !== b) missingChars.push(ch);\n }\n return { covered: chars.length - missingChars.length, missing: missingChars };\n }, params.text);\n if (covered === 0) {\n throw new NodeExecutionError(ctx.nodeId, ctx.nodeType, {\n kind: \"local\",\n cause: new Error(\n \"font_specimen: the font loaded but provides no glyphs for the specimen text — is it a subset for another script? Use a full (e.g. latin) build of the font, or change params.text.\",\n ),\n });\n }\n if (missing.length > 0) {\n ctx.log(`warning: font has no glyphs for ${missing.length} character(s): ${missing.join(\" \")}`);\n }\n\n const specimen = page.locator(\"#specimen\");\n const box = await specimen.boundingBox();\n const bytes = await specimen.screenshot({ type: \"png\", timeout: PAGE_TIMEOUT_MS });\n ctx.log(`captured ${bytes.length} bytes`);\n\n const metadata: Record<string, unknown> = {\n font_sha256: inputs.font.sha256,\n font_mime: inputs.font.mime,\n };\n if (box) {\n metadata.width = Math.round(box.width * DEVICE_SCALE_FACTOR);\n metadata.height = Math.round(box.height * DEVICE_SCALE_FACTOR);\n }\n\n // Upload to R2 (not just the local store): downstream remote nodes\n // (image_generate reference) receive only `url` — see stripLocalRefs.\n const ref = await uploadAndIngest({\n bytes: Buffer.from(bytes),\n kind: \"image\",\n mime: \"image/png\",\n metadata,\n ctx,\n });\n return { image: ref as z.infer<typeof ImageRef> };\n } finally {\n await browser.close();\n }\n } finally {\n await rm(tmp, { recursive: true, force: true }).catch((cleanupErr) => {\n ctx.log(`warning: tmp cleanup failed (${(cleanupErr as Error).message})`);\n });\n }\n },\n});\n\n// Exported for unit tests.\nexport const _internal = {\n FontSpecimenParams,\n buildSpecimenHtml,\n};\n","import { copyFile, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AssetRef } from \"../../../schema/refs.ts\";\n\n// Shape we accept: any AssetRef-ish value with at least a kind+mime and either\n// path or url. We don't import the Zod refs here so this helper stays usable\n// from anywhere without circulars.\nexport type StageableRef = { kind: string; mime: string; path?: string; url?: string };\n\n/**\n * Copy a local asset into `destDir` under `filename` (or, when `filename` is\n * omitted, `<key><ext>` derived from the ref's mime). Throws if the ref has no\n * local path — callers must materialise upstream assets before staging.\n */\nexport async function stageAsset(ref: StageableRef, destDir: string, filename: string): Promise<string> {\n if (!ref.path) {\n throw new Error(`stageAsset: ref (${ref.kind}/${ref.mime}) has no local path`);\n }\n const dest = path.join(destDir, filename);\n await copyFile(ref.path, dest);\n return dest;\n}\n\n/**\n * Returns an AssetRef as a URL string browser code can consume.\n * Prefers `ref.url`; falls back to a base64 `data:` URL built from `ref.path`.\n */\nexport async function refToUrl(ref: Pick<AssetRef, \"mime\" | \"path\" | \"url\">): Promise<string> {\n if (ref.url) return ref.url;\n if (!ref.path) {\n throw new Error(\"refToUrl: AssetRef has neither url nor path\");\n }\n const bytes = await readFile(ref.path);\n return `data:${ref.mime};base64,${bytes.toString(\"base64\")}`;\n}\n","import { execFile } from \"node:child_process\";\nimport { mkdtemp, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { cpus, tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { z } from \"zod\";\nimport { hashCompositionDir } from \"../../engine/composition-hash.ts\";\nimport {\n buildParamsSchema,\n type CompositionMeta,\n defaultFilenameForInput,\n loadCompositionMeta,\n} from \"../../engine/composition-meta.ts\";\nimport type { ExecCtx } from \"../../engine/context.ts\";\nimport { defineNode, type ValidateExtraFn } from \"../../engine/define.ts\";\nimport { type AssetRef, isStageableRef } from \"../../schema/refs.ts\";\nimport { refToUrl, type StageableRef, stageAsset } from \"./lib/assets.ts\";\nimport { copyComposition, vendorGsap } from \"./lib/composition-stage.ts\";\nimport { staticCompositionIssues } from \"./lib/composition-static-check.ts\";\nimport { runHyperframesCheck } from \"./lib/hyperframe-check.ts\";\nimport { mapKnownHyperframesError } from \"./lib/hyperframe-errors.ts\";\nimport { ensureHyperframesMetaJson } from \"./lib/hyperframe-meta.ts\";\nimport { substituteVariables } from \"./lib/templating.ts\";\n\nconst execFileAsync = promisify(execFile);\n\n// Runtime knobs are owned by the canvas-engine — we always render at max\n// quality, mp4, on the controlled E2B sandbox (see\n// `packages/e2b-template/src/template.ts`). The composition's own params\n// (`words_per_group`, `transcript`, etc.) ride alongside via `.catchall` and\n// are validated against the composition's meta.json by `validateExtra`.\nconst HyperframeParams = z\n .object({\n composition: z.string().min(1),\n // Output container. mp4 (default) for delivery; webm/mov render WITH\n // transparency (alpha) when the composition background is transparent —\n // use for motion-graphic overlays dropped into Premiere/AE/Nuke.\n format: z.enum([\"mp4\", \"webm\", \"mov\"]).optional().default(\"mp4\"),\n timeout_ms: z\n .number()\n .int()\n .positive()\n .optional()\n .default(10 * 60 * 1000),\n })\n .catchall(z.unknown());\n\n// Inputs are dynamic — meta.json declares which keys this composition\n// expects. The node accepts any keyed AssetRef and validates against meta\n// at execute time.\nconst HyperframeInputs = z.record(z.string(), z.custom<AssetRef>()).optional().default({});\n\nconst HyperframeOutputs = z\n .object({\n video: z.custom<AssetRef>(),\n })\n .strict();\n\nconst NODE_OWNED_PARAM_KEYS = new Set([\"composition\", \"format\", \"timeout_ms\"]);\n\nconst MIME_BY_FORMAT: Record<\"mp4\" | \"webm\" | \"mov\", string> = {\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n};\n\n// Engine-injected: the substitution pass fills these from input asset duration\n// (and the composition's default_duration). Authors must not declare them.\nconst ENGINE_INJECTED_TOKENS = new Set([\"duration\"]);\n\ntype Params = z.infer<typeof HyperframeParams>;\n\nexport const hyperframeRenderNode = defineNode({\n id: \"hyperframe_render\",\n version: \"6.1.0\",\n category: \"video\",\n location: \"local\",\n summary:\n \"Render an mp4 by composing an HTML/CSS/GSAP composition over upstream assets. Point `params.composition` at a directory containing `index.html` + `meta.json`. All variables are passed as primitives in `params` and substituted into the composition before render.\",\n when_to_use:\n \"Use to add captions, lower-thirds, branded overlays, title cards, or any HTML-driven graphic over a video. \" +\n \"Point `params.composition` at a directory containing `index.html` + `meta.json`. \" +\n \"Inputs are keyed by the composition's `meta.json.inputs` map — wire `inputs.<key> = $ref:<node>.<output>`. \" +\n \"Output resolution/fps come from the composition's `meta.json`; quality and worker count are fixed by the engine. \" +\n \"Set `params.format` to `webm` or `mov` for a transparent (alpha) overlay to composite in another editor; defaults to `mp4`. \" +\n \"Runs a pre-render `hyperframes lint`/`inspect` gate (overflow/structural errors block; contrast warns).\",\n inputs: HyperframeInputs,\n params: HyperframeParams,\n outputs: HyperframeOutputs,\n cost: () => ({ credits: 0, seconds_estimate: 30 }),\n\n validateExtra: (args) => validateCompositionNode(args, NODE_OWNED_PARAM_KEYS),\n\n cacheKeyExtras: async ({ params }) => {\n const out: Record<string, string> = {};\n const composition = (params as { composition?: unknown }).composition;\n if (typeof composition !== \"string\") return out;\n const dir = await resolveCompositionDir(composition).catch(() => null);\n if (!dir) return out;\n out.composition_content_sha = await hashCompositionDir(dir);\n return out;\n },\n\n async execute({ inputs, params, ctx }) {\n const compositionDir = await resolveCompositionDir(params.composition);\n const meta = await loadCompositionMeta(compositionDir);\n const compositionParams = validateAndParseDynamicParams(meta, params);\n\n const tmp = await mkdtemp(path.join(tmpdir(), \"hf-render-\"));\n try {\n await copyComposition(compositionDir, tmp);\n await vendorGsap(tmp, ctx);\n\n const stagedDuration = await stageInputs(tmp, inputs, meta, ctx);\n const duration = stagedDuration ?? meta.default_duration;\n\n const substitutionValues = await buildSubstitutionValues(compositionParams, meta, duration);\n await substituteCompositionFiles(tmp, substitutionValues);\n await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, duration);\n\n // Pre-render quality gate on the SUBSTITUTED composition: lint + inspect.\n // Overflow/structural errors block; contrast + deferred-asset advisories\n // only warn. Degrades gracefully if the tool is unavailable.\n await runHyperframesCheck({ dir: tmp, nodeId: \"hyperframe_render\", ctx, timeoutMs: params.timeout_ms });\n\n const outputPath = path.join(tmp, `output.${params.format}`);\n\n await runRender({ tmp, outputPath, params, meta, ctx });\n\n const bytes = await readFile(outputPath);\n ctx.log(`rendered ${bytes.length} bytes`);\n const ref = await ctx.assets.ingestBytes({\n bytes: Buffer.from(bytes),\n kind: \"video\",\n mime: MIME_BY_FORMAT[params.format],\n metadata: {\n width: meta.width,\n height: meta.height,\n fps: meta.fps,\n duration_ms: Math.round(duration * 1000),\n },\n });\n return { video: ref };\n } finally {\n await rm(tmp, { recursive: true, force: true }).catch((e) => {\n ctx.log(`warning: tmp cleanup failed (${(e as Error).message})`);\n });\n }\n },\n});\n\n// ---------------------------------------------------------------------------\n// Composition resolution + dynamic param validation\n// ---------------------------------------------------------------------------\n\nexport async function resolveCompositionDir(composition: string): Promise<string> {\n const compositionPath = path.isAbsolute(composition) ? composition : path.resolve(process.cwd(), composition);\n const s = await stat(compositionPath);\n if (s.isDirectory()) return compositionPath;\n return path.dirname(compositionPath);\n}\n\n/**\n * `validateExtra` hook shared by both hyperframe nodes: meta-param validation PLUS\n * every refusal the render would make that the canvas already determines: an\n * unfilled `{{token}}`, a required input left unwired or wired to the wrong kind,\n * and the pre-render gate's lint (degrades gracefully when the binary is absent).\n * Each of these aborted a real run after it had paid for every clip upstream.\n */\nexport async function validateCompositionNode(\n args: Parameters<ValidateExtraFn>[0],\n ownedKeys: ReadonlySet<string>,\n): Promise<{ path: string; message: string }[]> {\n const issues = await validateCompositionParams(args.rawParams, ownedKeys);\n const params = (args.rawParams ?? {}) as Record<string, unknown>;\n if (typeof params.composition !== \"string\") return issues;\n const dir = await resolveCompositionDir(params.composition).catch(() => null);\n const meta = dir ? await loadCompositionMeta(dir).catch(() => null) : null;\n if (!dir || !meta) return issues; // validateCompositionParams reported why\n const compositionParams = Object.fromEntries(Object.entries(params).filter(([k]) => !ownedKeys.has(k)));\n issues.push(\n ...(await staticCompositionIssues({\n nodeId: args.nodeId,\n dir,\n meta,\n compositionParams,\n rawInputs: args.rawInputs,\n inputKindOf: args.inputKindOf,\n inputSourceOf: args.inputSourceOf,\n lintTimeoutMs: 20_000,\n })),\n );\n return issues;\n}\n\n/**\n * Validate raw params against the composition's meta.json.\n * Returns issue list (empty = ok). Used by `validateExtra` so canvas\n * validate catches bad params before any render runs.\n */\nexport async function validateCompositionParams(\n rawParams: unknown,\n ownedKeys: ReadonlySet<string> = NODE_OWNED_PARAM_KEYS,\n): Promise<{ path: string; message: string }[]> {\n const issues: { path: string; message: string }[] = [];\n if (!rawParams || typeof rawParams !== \"object\") return issues;\n const params = rawParams as Record<string, unknown>;\n const composition = params.composition;\n if (typeof composition !== \"string\") return issues; // static schema will have flagged it\n\n let dir: string;\n try {\n dir = await resolveCompositionDir(composition);\n } catch (e) {\n issues.push({ path: \"params.composition\", message: (e as Error).message });\n return issues;\n }\n\n let meta: CompositionMeta;\n try {\n meta = await loadCompositionMeta(dir);\n } catch (e) {\n issues.push({ path: \"params.composition\", message: (e as Error).message });\n return issues;\n }\n\n // Pick out only the dynamic (composition-declared) params for the meta-schema check.\n const dynamic: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(params)) {\n if (ownedKeys.has(k)) continue;\n dynamic[k] = v;\n }\n for (const key of Object.keys(dynamic)) {\n if (ENGINE_INJECTED_TOKENS.has(key)) {\n issues.push({\n path: `params.${key}`,\n message: `\"${key}\" is reserved (auto-injected by the renderer from input duration); remove from params`,\n });\n }\n }\n const schema = buildParamsSchema(meta);\n const parsed = schema.safeParse(dynamic);\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n const tail = issue.path.join(\".\");\n issues.push({\n path: `params.${tail}`,\n message: issue.message,\n });\n }\n }\n return issues;\n}\n\nfunction validateAndParseDynamicParams(meta: CompositionMeta, params: Params): Record<string, unknown> {\n const dynamic: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(params)) {\n if (NODE_OWNED_PARAM_KEYS.has(k)) continue;\n dynamic[k] = v;\n }\n const schema = buildParamsSchema(meta);\n const parsed = schema.safeParse(dynamic);\n if (!parsed.success) {\n const summary = parsed.error.issues.map((i) => `params.${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`hyperframe_render: composition param validation failed — ${summary}`);\n }\n return parsed.data;\n}\n\n// ---------------------------------------------------------------------------\n// Asset staging\n// ---------------------------------------------------------------------------\n\n/**\n * Stage every input referenced in canvas `inputs` against the composition's\n * declared `meta.inputs` map. Throws on:\n * - missing required input\n * - input kind mismatch (canvas passes an image where video is declared)\n * - undeclared input key\n *\n * Returns the duration of the primary input asset (when a video) so the\n * engine can substitute `{{duration}}`. Falls back to meta.default_duration.\n */\nasync function stageInputs(\n tmp: string,\n inputs: Record<string, AssetRef>,\n meta: CompositionMeta,\n ctx: ExecCtx,\n): Promise<number | null> {\n assertInputsMatchMeta(inputs, meta, \"hyperframe_render\");\n let primaryDuration: number | null = null;\n for (const [key, ref] of Object.entries(inputs ?? {})) {\n const spec = meta.inputs[key];\n if (!spec) continue;\n const filename = spec.staged_as ?? defaultFilenameForInput(key, spec.kind);\n await stageAsset(ref as StageableRef, tmp, filename);\n ctx.log(`staged ${spec.kind} → ${filename}`);\n if (spec.kind === \"video\" && primaryDuration === null) {\n primaryDuration = await probeDurationSeconds(path.join(tmp, filename));\n }\n }\n return primaryDuration;\n}\n\n/**\n * Shared input-vs-meta check used by both hyperframe nodes. Throws on:\n * - undeclared input keys (typo / stale canvas)\n * - missing required inputs\n * - kind mismatch (canvas passes an image where video is declared)\n */\nexport function assertInputsMatchMeta(\n inputs: Record<string, AssetRef> | undefined,\n meta: CompositionMeta,\n nodeId: string,\n): void {\n const declaredKeys = new Set(Object.keys(meta.inputs));\n const providedKeys = new Set(Object.keys(inputs ?? {}));\n for (const key of providedKeys) {\n if (!declaredKeys.has(key)) {\n const declared = [...declaredKeys].join(\", \") || \"<none>\";\n throw new Error(`${nodeId}: inputs.${key} is not declared in composition meta.json (declares: ${declared})`);\n }\n }\n for (const [key, spec] of Object.entries(meta.inputs)) {\n if (spec.required && !providedKeys.has(key)) {\n throw new Error(`${nodeId}: inputs.${key} is required (kind=${spec.kind})`);\n }\n }\n for (const [key, ref] of Object.entries(inputs ?? {})) {\n const spec = meta.inputs[key];\n if (!spec) continue;\n if (ref.kind !== spec.kind) {\n throw new Error(`${nodeId}: inputs.${key} expected kind \"${spec.kind}\", got \"${ref.kind}\"`);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Substitution\n// ---------------------------------------------------------------------------\n\nasync function buildSubstitutionValues(\n compositionParams: Record<string, unknown>,\n meta: CompositionMeta,\n duration: number,\n): Promise<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(compositionParams)) {\n const spec = meta.params[name];\n if (spec?.kind === \"image\") {\n out[name] = await coerceImageParam(value);\n continue;\n }\n out[name] = value;\n }\n out.duration = duration.toFixed(3);\n return out;\n}\n\nfunction coerceImageParam(value: unknown): Promise<string> {\n if (typeof value === \"string\") return Promise.resolve(value);\n if (isStageableRef(value)) return refToUrl(value);\n throw new Error(\"hyperframe_render: image param must be a URL string or AssetRef\");\n}\n\nasync function substituteCompositionFiles(tmp: string, values: Record<string, unknown>): Promise<void> {\n // Substitute in index.html (the only file Hyperframes loads). Embedded\n // <style>/<script> live inside index.html so a single pass handles all of them.\n const entryPath = path.join(tmp, \"index.html\");\n const original = await readFile(entryPath, \"utf-8\");\n const { output, missing } = substituteVariables(original, values);\n if (missing.length > 0) {\n throw new Error(\n `hyperframe_render: composition references undefined variables: ${missing.map((m) => `{{${m}}}`).join(\", \")}. ` +\n `Add to params or to meta.json's params with a default.`,\n );\n }\n await writeFile(entryPath, output, \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Render shell-out\n// ---------------------------------------------------------------------------\n\n// One headless Chrome per worker, so this is bounded by the box, not by taste.\n//\n// It used to be a flat 4, on the stated grounds that \"the e2b instance is typically a\n// 4-vCPU box\". It is not, and had not been for three months when that was written: the\n// template asks for 8 vCPU and 8 GB (`packages/e2b-template/src/build.ts`, raised in March\n// with the message \"Increase cpu count we are maxing it\"), and `nproc` inside a live\n// sandbox reports 8. So the slowest node in an ad's graph — the caption render, measured at\n// 55.6s, 39% of the critical path — was using half the machine it had been given.\n//\n// One core is left for everything else on the box: the bridge, the supervisor and the\n// ffmpeg the render shells out to. The 8 cap keeps this honest if it ever runs somewhere\n// larger, where Chrome's memory would bind long before the cores did — measured in a live\n// sandbox, 5.8 GB of the 8 were free at render time.\n//\n// Whether this actually helps is recorded, not assumed: every run writes each node's\n// duration to `canvasRuns`, so the next ad's `hyperframe_render` is directly comparable\n// against the 55.6s above. If it does not improve, this is one line to put back.\nconst MAX_WORKERS = 8;\n\nfunction workerCount(): number {\n return Math.max(1, Math.min(MAX_WORKERS, cpus().length - 1));\n}\n\nasync function runRender(opts: {\n tmp: string;\n outputPath: string;\n params: Params;\n meta: CompositionMeta;\n ctx: ExecCtx;\n}): Promise<void> {\n const { tmp, outputPath, params, meta, ctx } = opts;\n const args = buildRenderArgs(tmp, outputPath, meta, params.format);\n ctx.log(`rendering ${meta.width}x${meta.height}@${meta.fps}fps ${params.format} from ${path.basename(tmp)}`);\n try {\n await execFileAsync(\"npx\", args, { timeout: params.timeout_ms, maxBuffer: 64 * 1024 * 1024 });\n } catch (e) {\n const stderr = (e as { stderr?: string }).stderr ?? \"\";\n const stdout = (e as { stdout?: string }).stdout ?? \"\";\n const detail = stderr || stdout || (e as Error).message;\n const friendly = mapKnownHyperframesError(detail);\n throw new Error(`hyperframe_render: render failed\\n${friendly ?? detail.slice(0, 4000)}`);\n }\n}\n\nexport function buildRenderArgs(tmp: string, outputPath: string, meta: CompositionMeta, format: string): string[] {\n return [\n \"hyperframes\",\n \"render\",\n tmp,\n \"--output\",\n outputPath,\n \"--fps\",\n String(meta.fps),\n \"--quality\",\n \"high\",\n \"--format\",\n format,\n \"--workers\",\n String(workerCount()),\n ];\n}\n\nasync function probeDurationSeconds(filePath: string): Promise<number> {\n const { stdout } = await execFileAsync(\n \"ffprobe\",\n [\"-v\", \"error\", \"-show_entries\", \"format=duration\", \"-of\", \"csv=p=0\", filePath],\n { encoding: \"utf-8\" },\n );\n const seconds = Number.parseFloat(stdout.trim());\n if (!Number.isFinite(seconds)) {\n throw new Error(`hyperframe_render: ffprobe returned invalid duration: ${stdout}`);\n }\n return seconds;\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { sha256Hex } from \"../storage/sha256.ts\";\n\n// Skip transient/build artefacts when hashing — these aren't part of the\n// composition's source-of-truth and would either thrash the cache (.cache,\n// node_modules) or pollute the hash with editor noise (.DS_Store, .swp).\nconst SKIP_DIRS: ReadonlySet<string> = new Set([\".cache\", \".git\", \"node_modules\", \"dist\", \"build\", \".next\", \".turbo\"]);\n\nfunction isSkippedName(name: string): boolean {\n if (name.startsWith(\".\")) return true; // .DS_Store, dotfiles\n return SKIP_DIRS.has(name);\n}\n\ntype FileEntry = { relPath: string; contentSha: string };\n\n/**\n * Compute a deterministic content-addressed hash of every relevant file under\n * `dir`. Hash layout:\n *\n * sha256(\"<rel/path>\\0<file-sha256>\\n<rel/path>\\0<file-sha256>\\n...\")\n *\n * Entries are sorted by relative path so the result is stable regardless of\n * filesystem-walk order. Returns a hex sha256 string.\n *\n * Used by the executor to fold composition content into the cache key — any\n * edit to a tracked file invalidates the hyperframe_render cache entry.\n */\nexport async function hashCompositionDir(dir: string): Promise<string> {\n const entries = await collectFiles(dir, dir);\n entries.sort((a, b) => a.relPath.localeCompare(b.relPath));\n const manifest = entries.map((e) => `${e.relPath}\\0${e.contentSha}`).join(\"\\n\");\n return sha256Hex(manifest);\n}\n\nasync function collectFiles(root: string, current: string): Promise<FileEntry[]> {\n const out: FileEntry[] = [];\n const names = await readdir(current);\n for (const name of names) {\n if (isSkippedName(name)) continue;\n const abs = path.join(current, name);\n const s = await stat(abs);\n if (s.isDirectory()) {\n out.push(...(await collectFiles(root, abs)));\n continue;\n }\n if (!s.isFile()) continue;\n const bytes = await readFile(abs);\n const relPath = path.relative(root, abs).split(path.sep).join(\"/\");\n out.push({ relPath, contentSha: sha256Hex(bytes) });\n }\n return out;\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\n\n// One source of truth per composition: meta.json.\n//\n// The composition declares its inputs (videos/images/audios it expects staged\n// into the render directory) and params (everything you can pass via {{var}}).\n// The hyperframe_render and hyperframe_snapshot nodes load this file, build a\n// per-call Zod schema, and validate the canvas's `params` against it.\n\nconst InputKind = z.enum([\"video\", \"image\", \"audio\", \"json\", \"font\"]);\n\nconst InputSpec = z\n .object({\n kind: InputKind,\n required: z.boolean().optional().default(false),\n // Filename the composition's HTML references (e.g. `input.mp4`, `logo.png`).\n // Defaults to `<key><ext>` derived from the kind.\n staged_as: z.string().min(1).optional(),\n description: z.string().optional(),\n })\n .strict();\n\nconst ParamSpecBase = {\n required: z.boolean().optional().default(false),\n description: z.string().optional(),\n};\n\nconst StringParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"string\"),\n default: z.string().optional(),\n enum: z.array(z.string()).optional(),\n })\n .strict();\n\nconst IntegerParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"integer\"),\n default: z.number().int().optional(),\n min: z.number().int().optional(),\n max: z.number().int().optional(),\n })\n .strict();\n\nconst NumberParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"number\"),\n default: z.number().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n })\n .strict();\n\nconst BooleanParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"boolean\"),\n default: z.boolean().optional(),\n })\n .strict();\n\nconst ColorParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"color\"),\n default: z.string().optional(),\n })\n .strict();\n\nconst ImageParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"image\"),\n default: z.string().optional(),\n })\n .strict();\n\nconst JsonParam = z\n .object({\n ...ParamSpecBase,\n kind: z.literal(\"json\"),\n schema: z.unknown().optional(),\n default: z.unknown().optional(),\n })\n .strict();\n\nconst ParamSpec = z.discriminatedUnion(\"kind\", [\n StringParam,\n IntegerParam,\n NumberParam,\n BooleanParam,\n ColorParam,\n ImageParam,\n JsonParam,\n]);\n\ntype ParamSpec = z.infer<typeof ParamSpec>;\n\nexport const CompositionMetaSchema = z\n .object({\n id: z.string().min(1),\n title: z.string().min(1),\n description: z.string().optional(),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n fps: z.number().int().positive().default(30),\n default_duration: z.number().positive().default(10),\n inputs: z.record(z.string(), InputSpec).default({}),\n params: z.record(z.string(), ParamSpec).default({}),\n /**\n * The page reads its own staged files (a picture's pixels), so a snapshot opens it with\n * file access. Off by default: Chromium grants it for every file:// path, not just these.\n */\n reads_staged_files: z.boolean().optional(),\n })\n .strict();\n\nexport type CompositionMeta = z.infer<typeof CompositionMetaSchema>;\n\nexport async function loadCompositionMeta(compositionDir: string): Promise<CompositionMeta> {\n const metaPath = path.join(compositionDir, \"meta.json\");\n let raw: string;\n try {\n raw = await readFile(metaPath, \"utf-8\");\n } catch (e) {\n throw new Error(`composition meta: cannot read ${metaPath} (${(e as Error).message})`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n throw new Error(`composition meta: ${metaPath} is not valid JSON (${(e as Error).message})`);\n }\n const result = CompositionMetaSchema.safeParse(parsed);\n if (!result.success) {\n const summary = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`composition meta: ${metaPath} schema error — ${summary}`);\n }\n return result.data;\n}\n\n/**\n * Build a Zod schema for the params declared in `meta.json`. Used to validate\n * the canvas's `params` payload at validate-time and execute-time.\n *\n * Output schema is a `z.object().strict()` — unknown keys are rejected so\n * typos surface immediately. Params with a `default` are made optional and\n * Zod fills the default; required params without defaults must be provided.\n */\nexport function buildParamsSchema(meta: CompositionMeta): z.ZodObject {\n const shape: Record<string, z.ZodType> = {};\n for (const [name, spec] of Object.entries(meta.params)) {\n shape[name] = buildParamFieldSchema(name, spec);\n }\n return z.object(shape).strict();\n}\n\nfunction buildParamFieldSchema(name: string, spec: ParamSpec): z.ZodType {\n switch (spec.kind) {\n case \"string\": {\n const s: z.ZodType = spec.enum && spec.enum.length > 0 ? z.enum(spec.enum as [string, ...string[]]) : z.string();\n return finalize(s, spec.default, spec.required);\n }\n case \"integer\": {\n let s: z.ZodNumber = z.number().int();\n if (spec.min !== undefined) s = s.min(spec.min);\n if (spec.max !== undefined) s = s.max(spec.max);\n return finalize(s, spec.default, spec.required);\n }\n case \"number\": {\n let s: z.ZodNumber = z.number();\n if (spec.min !== undefined) s = s.min(spec.min);\n if (spec.max !== undefined) s = s.max(spec.max);\n return finalize(s, spec.default, spec.required);\n }\n case \"boolean\":\n return finalize(z.boolean(), spec.default, spec.required);\n case \"color\": {\n const s = z.string().regex(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {\n message: `param \"${name}\": must be a 3/6/8-digit hex color (e.g. \"#ff0066\")`,\n });\n return finalize(s, spec.default, spec.required);\n }\n case \"image\":\n // Accept a URL string or an AssetRef-shaped object — node coerces to URL\n // string before substitution.\n return finalize(z.union([z.string().min(1), z.record(z.string(), z.unknown())]), spec.default, spec.required);\n case \"json\":\n return finalize(\n z.unknown().refine((v) => v !== undefined, { message: `param \"${name}\" is required` }),\n spec.default,\n spec.required,\n );\n default: {\n const _exhaustive: never = spec;\n throw new Error(`buildParamFieldSchema: unknown kind ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n\nfunction finalize(schema: z.ZodType, dflt: unknown, required: boolean | undefined): z.ZodType {\n if (dflt !== undefined) return schema.optional().default(dflt as never);\n if (required === false) return schema.optional();\n return schema;\n}\n\n/** Default filename for an input — the composition can override via `staged_as`. */\nexport function defaultFilenameForInput(key: string, kind: \"video\" | \"image\" | \"audio\" | \"json\" | \"font\"): string {\n if (kind === \"video\") return `${key}.mp4`;\n // The browser reads a font's format off its bytes, so one name fits ttf/otf/woff/woff2.\n if (kind === \"font\") return `${key}.font`;\n if (kind === \"audio\") return `${key}.mp3`;\n if (kind === \"json\") return `${key}.json`;\n return `${key}.png`;\n}\n","import { copyFile, cp } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\n\nconst require_ = createRequire(import.meta.url);\n\n/** Copy a composition dir to a scratch dir, leaving caches and VCS metadata behind. */\nexport async function copyComposition(srcDir: string, destDir: string): Promise<void> {\n await cp(srcDir, destDir, {\n recursive: true,\n filter: (src) => {\n const name = path.basename(src);\n if (name === \".cache\" || name === \"node_modules\" || name === \".git\") return false;\n return true;\n },\n });\n}\n\n/** Put the engine's own `gsap.min.js` next to index.html, which compositions load as `./gsap.min.js`. */\nexport async function vendorGsap(dir: string, ctx: { log: (s: string) => void }): Promise<void> {\n try {\n const gsapMin = require_.resolve(\"gsap/dist/gsap.min.js\");\n await copyFile(gsapMin, path.join(dir, \"gsap.min.js\"));\n } catch (e) {\n ctx.log(`warning: could not vendor gsap.min.js (${(e as Error).message}); compositions must self-supply`);\n }\n}\n","import { mkdtemp, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { fetchExternalBytes } from \"../../../../lib/externalFetch.ts\";\nimport { type CompositionMeta, defaultFilenameForInput } from \"../../../engine/composition-meta.ts\";\nimport type { InputSource, StaticInputKind } from \"../../../engine/define.ts\";\nimport { REF_PREFIX } from \"../../../schema/canvas.ts\";\nimport { renderTemplate } from \"../../../schema/prompts.ts\";\nimport { copyComposition, vendorGsap } from \"./composition-stage.ts\";\nimport { isHyperframesAvailable, lintPreparedComposition } from \"./hyperframe-check.ts\";\nimport { ensureHyperframesMetaJson } from \"./hyperframe-meta.ts\";\nimport { extractReferencedTokens, substituteVariables } from \"./templating.ts\";\n\n// What a hyperframe node's run would refuse, decided before the run. Each check\n// here mirrors one the node makes at render time (the input staging, the `{{token}}`\n// substitution, the pre-render lint gate), and each was watched failing a real run\n// after every clip upstream of it had been paid for.\n\ntype Issue = { path: string; message: string };\n\ntype StaticCheckArgs = {\n nodeId?: string;\n dir: string;\n meta: CompositionMeta;\n /** The node's params minus its own keys: what the composition's meta.json declares. */\n compositionParams: Record<string, unknown>;\n rawInputs: unknown;\n inputKindOf?: (value: unknown) => StaticInputKind;\n /** Where an input's file comes from before the run; undefined for one a node generates. */\n inputSourceOf?: (value: unknown) => InputSource | undefined;\n lintTimeoutMs: number;\n};\n\nexport async function staticCompositionIssues(args: StaticCheckArgs): Promise<Issue[]> {\n const html = await readFile(path.join(args.dir, \"index.html\"), \"utf-8\").catch(() => null);\n const issues = inputIssues(args.meta, args.rawInputs, args.inputKindOf);\n if (html === null) return issues;\n issues.push(...undefinedVariableIssues(html, args.meta, args.compositionParams));\n issues.push(...(await gateIssues(args)));\n return issues;\n}\n\n/** The values `{{tokens}}` will be filled with, as far as they are known before the run. */\nfunction substitutionValues(\n meta: CompositionMeta,\n compositionParams: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(compositionParams)) {\n const kind = meta.params[name]?.kind;\n const wired = typeof value === \"string\" && value.startsWith(REF_PREFIX);\n // An image param given as an asset becomes its URL at run time.\n const imageAsset = kind === \"image\" && typeof value !== \"string\";\n out[name] = wired || imageAsset ? placeholderFor(kind) : value;\n }\n for (const [name, spec] of Object.entries(meta.params)) {\n if (!Object.hasOwn(out, name) && spec.default !== undefined) out[name] = spec.default;\n }\n // The engine fills `{{duration}}` itself, from the staged video or the default.\n out.duration = meta.default_duration.toFixed(3);\n return out;\n}\n\n/** A stand-in with the right JS shape for a value only the run will produce. */\nfunction placeholderFor(kind: string | undefined): unknown {\n if (kind === \"json\") return null;\n if (kind === \"integer\" || kind === \"number\") return 0;\n if (kind === \"boolean\") return false;\n return \"\";\n}\n\nfunction undefinedVariableIssues(\n html: string,\n meta: CompositionMeta,\n compositionParams: Record<string, unknown>,\n): Issue[] {\n const known = substitutionValues(meta, compositionParams);\n const missing = extractReferencedTokens(html).filter((token) => !Object.hasOwn(known, token));\n if (missing.length === 0) return [];\n return [\n {\n path: \"params.composition\",\n message:\n `composition references undefined variables: ${missing.map((m) => `{{${m}}}`).join(\", \")}. ` +\n \"Add to params or to meta.json's params with a default.\",\n },\n ];\n}\n\n/** The inputs against meta.json: the same three refusals the node makes when it stages them. */\nfunction inputIssues(\n meta: CompositionMeta,\n rawInputs: unknown,\n inputKindOf: ((value: unknown) => StaticInputKind) | undefined,\n): Issue[] {\n const inputs = (rawInputs && typeof rawInputs === \"object\" ? rawInputs : {}) as Record<string, unknown>;\n const issues: Issue[] = [];\n const declared = Object.keys(meta.inputs);\n for (const key of Object.keys(inputs)) {\n if (Object.hasOwn(meta.inputs, key)) continue;\n issues.push({\n path: `inputs.${key}`,\n message: `inputs.${key} is not declared in composition meta.json (declares: ${declared.join(\", \") || \"<none>\"})`,\n });\n }\n for (const [key, spec] of Object.entries(meta.inputs)) {\n if (!Object.hasOwn(inputs, key)) {\n if (spec.required)\n issues.push({ path: `inputs.${key}`, message: `inputs.${key} is required (kind=${spec.kind})` });\n continue;\n }\n const got = inputKindOf?.(inputs[key]) ?? \"unknown\";\n const mismatch = kindMismatch(spec.kind, got);\n if (mismatch)\n issues.push({ path: `inputs.${key}`, message: `inputs.${key} expected kind \"${spec.kind}\", ${mismatch}` });\n }\n return issues;\n}\n\nfunction kindMismatch(expected: string, got: StaticInputKind): string | null {\n if (got === \"unknown\" || got === expected) return null;\n if (got === \"list\") return \"got a list; wire one element of it (`$ref:<node>.<output>#0`)\";\n if (got === \"value\") return \"got a plain value, not an asset\";\n return `got \"${got}\"`;\n}\n\n/**\n * The render gate's lint, run on a copy prepared the way the render prepares it: gsap\n * vendored, wired inputs present, `{{tokens}}` and param slots filled. Runs only where\n * the `hyperframes` binary is installed.\n */\nasync function gateIssues(args: StaticCheckArgs): Promise<Issue[]> {\n if (!(await isHyperframesAvailable())) return [];\n const tmp = await mkdtemp(path.join(tmpdir(), \"hf-validate-\"));\n try {\n await prepareForGate(tmp, args);\n const lint = await lintPreparedComposition(tmp, args.lintTimeoutMs);\n return lint.map((b) => ({\n path: \"params.composition\",\n message: `pre-render gate would fail: [${b.code}] ${b.message}`,\n }));\n } finally {\n await rm(tmp, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n\n/** Fonts and logos are small; the cap only guards against a wrong URL. */\nconst MAX_INPUT_BYTES = 20 * 1024 * 1024;\n\n/** A 1x1 PNG: the stand-in for a picture a node will generate. */\nconst STAND_IN_PICTURE = Buffer.from(\n \"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\",\n \"base64\",\n);\n\nasync function readSource(source: InputSource): Promise<Buffer | null> {\n try {\n if (\"path\" in source) {\n // Sized before it is read: validate must never buffer a huge or special file.\n const info = await stat(source.path);\n return info.isFile() && info.size <= MAX_INPUT_BYTES ? await readFile(source.path) : null;\n }\n return (await fetchExternalBytes(source.url, { maxBytes: MAX_INPUT_BYTES, timeoutMs: 15_000 })).buffer;\n } catch {\n return null;\n }\n}\n\n/** What stands in for an input only the run produces. */\nfunction standIn(kind: string): Buffer | string {\n if (kind === \"image\") return STAND_IN_PICTURE;\n return kind === \"json\" ? \"{}\" : \"\";\n}\n\n/**\n * Prepare `target` as the render prepares its composition: every input known before the\n * run (a workspace file or a URL an `ingest` reads) staged for real, a stand-in for what a\n * node generates, and each param slot filled from the file wired to it. `faithful` says\n * the copy, fonts and marks are exactly what the render will draw; only the picture under\n * them is a stand-in. A slot only the run fills becomes an empty value of its kind, never\n * a raw `{{slot}}` the lint would read as broken script.\n */\nexport async function prepareForGate(\n target: string,\n args: Omit<StaticCheckArgs, \"lintTimeoutMs\" | \"nodeId\">,\n): Promise<{ faithful: boolean }> {\n await copyComposition(args.dir, target);\n await vendorGsap(target, { log: () => undefined });\n const staged = await stageGateInputs(target, args);\n const filled = fillParamSlots(args.meta, args.compositionParams, staged.slotTexts);\n const entry = path.join(target, \"index.html\");\n const html = await readFile(entry, \"utf-8\");\n await writeFile(entry, substituteVariables(html, substitutionValues(args.meta, filled.params)).output);\n await ensureHyperframesMetaJson(target, args.meta.id, args.meta, args.meta.default_duration);\n return { faithful: staged.faithful && filled.faithful };\n}\n\n/** Each wired input staged under the name the render stages it as; json ones also kept as slot text. */\nasync function stageGateInputs(\n target: string,\n args: Pick<StaticCheckArgs, \"meta\" | \"rawInputs\" | \"inputSourceOf\">,\n): Promise<{ faithful: boolean; slotTexts: Record<string, string> }> {\n const inputs = (args.rawInputs && typeof args.rawInputs === \"object\" ? args.rawInputs : {}) as Record<\n string,\n unknown\n >;\n const wired = Object.entries(args.meta.inputs).filter(([key]) => Object.hasOwn(inputs, key));\n // In parallel: each input has its own file, and a slow font URL must not wait on another.\n const staged = await Promise.all(\n wired.map(async ([key, spec]) => {\n const filename = path.join(target, spec.staged_as ?? defaultFilenameForInput(key, spec.kind));\n const source = args.inputSourceOf?.(inputs[key]);\n const bytes = source ? await readSource(source) : null;\n await writeFile(filename, bytes ?? standIn(spec.kind));\n // Only a generated picture has a faithful stand-in: it sits under the copy.\n const faithful = bytes !== null || (!source && spec.kind === \"image\");\n return { key, faithful, text: bytes && spec.kind === \"json\" ? bytes.toString(\"utf8\") : null };\n }),\n );\n const slotTexts: Record<string, string> = {};\n for (const { key, text } of staged) if (text !== null) slotTexts[key] = text;\n return { faithful: staged.every((s) => s.faithful), slotTexts };\n}\n\n/** The params with each `{{slot}}` filled from its staged file, or an empty value of the param's kind. */\nfunction fillParamSlots(\n meta: CompositionMeta,\n compositionParams: Record<string, unknown>,\n slotTexts: Record<string, string>,\n): { faithful: boolean; params: Record<string, unknown> } {\n let faithful = true;\n const params: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(compositionParams)) {\n const slots = typeof value === \"string\" ? extractReferencedTokens(value) : [];\n if (typeof value !== \"string\" || slots.length === 0) {\n params[name] = value;\n } else if (slots.every((slot) => Object.hasOwn(slotTexts, slot))) {\n params[name] = renderTemplate(value, slotTexts);\n } else {\n faithful = false;\n params[name] = placeholderFor(meta.params[name]?.kind);\n }\n }\n return { faithful, params };\n}\n","import { execFile } from \"node:child_process\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { hasFrameVisionKey } from \"../../../../env.ts\";\nimport { captionTextsFrom, fragmentedCaptions } from \"./caption-style.ts\";\n\n// Pre-render quality gate. Runs Hyperframes' own `lint` (composition mistakes:\n// missing ids, undeclared variables, bad structure, low contrast, …) and\n// `inspect` (text/container overflow across the timeline) on the SUBSTITUTED\n// composition in the tmp render dir, before the billable render.\n//\n// Severity policy (see the plan): overflow + structural lint errors HARD-BLOCK;\n// contrast and deferred-asset advisories WARN ONLY (low contrast can be a valid\n// brand choice; the agent drops brand fonts/assets after scaffolding). If the\n// `hyperframes` binary or these subcommands are unavailable, the gate degrades\n// gracefully — it logs and lets the render proceed rather than failing a valid\n// render on a missing tool.\n\nconst execFileAsync = promisify(execFile);\n\n// Internal-only types — exported functions return them by inference, so they\n// don't need to be exported themselves (keeps knip's unused-export gate clean).\ntype CheckSeverity = \"blocking\" | \"warning\";\ntype CheckFinding = {\n source: \"lint\" | \"inspect\" | GateSection;\n code: string;\n message: string;\n severity: CheckSeverity;\n};\ntype CheckResult = { blocking: CheckFinding[]; warnings: CheckFinding[] };\n\n// Advisory in Baker's deferred-asset pipeline — never hard-block on these even\n// at lint severity \"error\". The agent supplies brand fonts/assets and tunes\n// contrast AFTER scaffolding, so blocking here would fail legitimate creative.\n// Add a code/keyword here to downgrade a finding from blocking to warning.\n//\n// • contrast / wcag / missing_local_asset / font-* — deferred-asset advisories.\n// • transcript_not_inline — our caption track legitimately reads the transcript\n// from a STAGED `transcript.json` input (a real file at render time); the\n// gate's inline-array preference is a Studio-editing nicety, not a render\n// requirement, so fetch() works and must not block.\n// • text_occluded — the static check can't run GSAP, so it reads every\n// animate-in caption/overlay (hidden until its beat by design) as occluded.\n// A genuine occlusion still surfaces as a warning + on read-the-output-back.\nconst NEVER_BLOCK: RegExp[] = [\n /contrast/i,\n /\\bwcag\\b/i,\n /missing_local_asset/i,\n /font[_-]?family/i,\n /font[_-]?face/i,\n /transcript[_-]?not[_-]?inline/i,\n /text[_-]?occluded/i,\n];\n\n// Stderr/message fragments that mean the command itself could not run (vs. the\n// command running and reporting findings). These degrade gracefully.\nconst UNAVAILABLE = /unknown command|command not found|not found|Did you mean|Unknown argument|ENOENT/i;\n\nfunction isAdvisory(code: string, message: string): boolean {\n const hay = `${code} ${message}`;\n return NEVER_BLOCK.some((re) => re.test(hay));\n}\n\n/** Parse JSON that may be preceded by non-JSON guard lines (e.g. `[StaticGuard] …`). */\nexport function parseCheckJson(raw: string): unknown {\n if (!raw) return null;\n const trimmed = raw.trim();\n try {\n return JSON.parse(trimmed);\n } catch {\n // fall through to brace extraction\n }\n const start = trimmed.indexOf(\"{\");\n const end = trimmed.lastIndexOf(\"}\");\n if (start >= 0 && end > start) {\n try {\n return JSON.parse(trimmed.slice(start, end + 1));\n } catch {\n return null;\n }\n }\n return null;\n}\n\nexport function classifyLint(json: unknown): CheckFinding[] {\n const out: CheckFinding[] = [];\n const findings = (json as { findings?: unknown })?.findings;\n if (!Array.isArray(findings)) return out;\n for (const f of findings) {\n const rec = f as { code?: unknown; message?: unknown; severity?: unknown };\n const code = String(rec?.code ?? \"\");\n const message = String(rec?.message ?? \"\");\n const severity = String(rec?.severity ?? \"info\");\n const blocking = severity === \"error\" && !isAdvisory(code, message);\n out.push({ source: \"lint\", code, message, severity: blocking ? \"blocking\" : \"warning\" });\n }\n return out;\n}\n\nexport function classifyInspect(json: unknown): CheckFinding[] {\n const out: CheckFinding[] = [];\n const obj = json as { issues?: unknown; ok?: unknown };\n const issues = obj?.issues;\n if (!Array.isArray(issues)) return out;\n for (const iss of issues) {\n const rec = iss as { code?: unknown; type?: unknown; message?: unknown; detail?: unknown; severity?: unknown };\n const code = String(rec?.code ?? rec?.type ?? \"overflow\");\n const message = String(rec?.message ?? rec?.detail ?? JSON.stringify(iss));\n // Overflow clips/clobbers the creative → block on errors. Lesser severities\n // (or unscored issues when the run as a whole passed) only warn.\n const severity = rec?.severity ? String(rec.severity) : obj?.ok === false ? \"error\" : \"warning\";\n out.push({ source: \"inspect\", code, message, severity: severity === \"error\" ? \"blocking\" : \"warning\" });\n }\n return out;\n}\n\nexport function classifyCheckOutput(lintRaw: string, inspectRaw: string): CheckResult {\n const findings = [...classifyLint(parseCheckJson(lintRaw)), ...classifyInspect(parseCheckJson(inspectRaw))];\n return {\n blocking: findings.filter((f) => f.severity === \"blocking\"),\n warnings: findings.filter((f) => f.severity === \"warning\"),\n };\n}\n\n/** The sections `hyperframes check` reports, each with its own findings array. */\nconst GATE_SECTIONS = [\"lint\", \"runtime\", \"layout\", \"motion\", \"contrast\"] as const;\ntype GateSection = (typeof GATE_SECTIONS)[number];\n\n/**\n * The 0.8 gate: one browser session instead of two static passes.\n *\n * `check` runs lint, runtime, layout, motion and WCAG contrast together, and the\n * runtime half is signal the static passes could not produce at all — measured on\n * a real project, `lint` came back clean while the browser reported `GSAP target\n * #a-roll not found`, an animation pointed at an element that does not exist and\n * therefore silently never plays. That is the defect that makes a finished video\n * look static, and nothing in `lint`/`inspect` can see it.\n *\n * `--at-transitions` samples the tween boundaries as well as segment midpoints.\n * A caption that only collides with a lower-third while the two cross is invisible\n * to midpoint sampling, and it is exactly the kind of flaw that reads as amateur.\n */\nexport function buildCheckArgs(dir: string, samples: number): string[] {\n return [\"hyperframes\", \"check\", dir, \"--json\", \"--samples\", String(samples), \"--at-transitions\"];\n}\n\n/**\n * Findings that LOOK advisory, or merely advisory, and are actually fatal.\n *\n * HyperFrames reports a tween whose target does not exist as a console warning,\n * because from its side nothing threw. From ours the animation simply never\n * happens: measured on their own warm-grain example, six `GSAP target #a-roll\n * not found` warnings — with lint, runtime, layout and motion all green —\n * rendered an almost entirely blank 10s video. The render still billed.\n *\n * `NEVER_BLOCK` downgrades every font finding, deliberately: a missing brand `.otf` is\n * a deferred asset and the composition falls back. But a font family the compiler\n * cannot MAP is a different animal — it aborts the render outright. A closing card\n * styled `font-family: 'BrandFont', …` killed a whole ad with\n * `No deterministic font mapping for: BrandFont`, after every clip had been paid for.\n *\n * Read off the gate's own message rather than re-derived here. Answering it ourselves\n * needs the set of families the renderer bundles, which we would have to copy and keep\n * in step — and a family we wrongly believed unsupported is a refusal we invented,\n * which is worse than the limit we never had. The gate already knows, runs before a\n * single clip is billed, and says so by name.\n */\nconst PROMOTE_TO_BLOCKING = /GSAP target [^ ]+ not found|no deterministic font mapping/i;\n\nfunction toGateFinding(section: GateSection, entry: unknown): CheckFinding {\n const f = entry as { code?: unknown; severity?: unknown; message?: unknown };\n const code = typeof f.code === \"string\" ? f.code : \"unknown\";\n const message = typeof f.message === \"string\" ? f.message : \"\";\n const isError = f.severity === \"error\" || PROMOTE_TO_BLOCKING.test(message);\n // The promotion wins over the advisory downgrade: an unmapped font reads as a font\n // finding, and font findings are advisory by default, so the two rules met and the\n // fatal one lost.\n const fatal = PROMOTE_TO_BLOCKING.test(message);\n return {\n source: section,\n code,\n message,\n severity: fatal || (isError && !isAdvisory(code, message)) ? \"blocking\" : \"warning\",\n };\n}\n\n/**\n * Classify `check --json` under OUR severity policy, not the tool's verdict.\n *\n * The top-level `ok` is deliberately ignored: it goes false on a contrast finding,\n * and low contrast is a brand choice the agent tunes after scaffolding. What blocks\n * is an error the render cannot survive, minus the advisories in `NEVER_BLOCK`.\n */\nexport function classifyGateOutput(raw: string): CheckResult {\n const parsed = parseCheckJson(raw) as Record<string, unknown> | null;\n const findings = GATE_SECTIONS.flatMap((section) => {\n const block = parsed?.[section] as { findings?: unknown } | undefined;\n const entries = Array.isArray(block?.findings) ? block.findings : [];\n return entries.map((entry) => toGateFinding(section, entry));\n });\n return {\n blocking: findings.filter((f) => f.severity === \"blocking\"),\n warnings: findings.filter((f) => f.severity === \"warning\"),\n };\n}\n\n/**\n * One line per warning CODE, not per occurrence.\n *\n * A real project's gate came back with 62 warnings, 53 of them the same layout\n * code. Printed one per line that is a wall nobody reads, and a gate whose output\n * is skipped is a gate that does not exist. The count is what tells the agent\n * whether it is one stray element or the whole layout, and the first message is\n * enough to know which.\n */\nexport function summariseWarnings(warnings: CheckFinding[]): string[] {\n const groups = new Map<string, { count: number; message: string }>();\n for (const w of warnings) {\n const key = `${w.source}/${w.code}`;\n const seen = groups.get(key);\n if (seen) seen.count += 1;\n else groups.set(key, { count: 1, message: w.message });\n }\n return [...groups.entries()]\n .sort((a, b) => b[1].count - a[1].count)\n .map(([key, g]) => `${g.count}× [${key}] ${g.message}`);\n}\n\n/**\n * Baker's own reading of the gate's highest-value codes.\n *\n * The tool states what it observed; the agent needs what to DO. These are the\n * findings where the raw message is accurate and still leaves someone staring at\n * it — every one of them was watched costing a real, billed render.\n */\nconst FINDING_ADVICE: Record<string, string> = {\n sweep_static:\n \"the timeline never advanced, so this renders as a STILL IMAGE while billing like a video — and every other green verdict in this run is unreliable. Check the composition registers a paused timeline on `window.__timelines[<composition-id>]` and that the tweens target elements that exist.\",\n console_error: \"the composition threw in the browser, so whatever that script was driving did not happen.\",\n page_error: \"a script failed to evaluate — animations and any JS-driven layout in this composition did not run.\",\n http_error: \"an asset 404ed at render time; it will be missing from the frame, not substituted.\",\n content_overlap:\n \"two text blocks collide. Make one smaller, shorten it, or move it, in the composition's HTML or the param that sets it; nothing generated needs to be paid for again.\",\n console_warning:\n \"if this names a GSAP target, the element it animates does not exist, so that tween is a no-op and whatever it was meant to reveal never appears. Fix the selector or add the element before rendering.\",\n};\n\n/** The message the agent reads, with Baker's advice appended when we have any. */\nexport function explainGateFinding(finding: CheckFinding): string {\n const advice = FINDING_ADVICE[finding.code];\n return advice ? `${finding.message} — ${advice}` : finding.message;\n}\n\n/**\n * What Gemini saw, one line per frame.\n *\n * `snapshot --describe` writes a markdown file with a preamble and a `## <frame>`\n * heading per shot. The preamble is the tool talking to itself; the agent needs\n * the verdicts, and needs them in the node log rather than in a file it would\n * have to know to open.\n */\nexport function condenseFrameDescriptions(markdown: string): string[] {\n const lines: string[] = [];\n let frame: string | null = null;\n for (const line of markdown.split(\"\\n\")) {\n const heading = line.match(/^## (.+)$/);\n if (heading) {\n frame = heading[1] ?? null;\n continue;\n }\n const text = line.trim();\n if (frame && text) {\n lines.push(`${frame}: ${text}`);\n frame = null;\n }\n }\n return lines;\n}\n\nexport function buildLintArgs(dir: string): string[] {\n return [\"hyperframes\", \"lint\", dir, \"--json\"];\n}\n\n// Validate runs OUTSIDE the render (no billed clips yet) and on dev boxes that\n// have no `hyperframes` binary. `--no-install` makes npx fast-fail (ENOENT →\n// degrade) instead of attempting a network download, so validate stays free and\n// flake-proof; in the e2b sandbox the globally-pinned binary still resolves.\nfunction buildLintArgsNoInstall(dir: string): string[] {\n return [\"--no-install\", \"hyperframes\", \"lint\", dir, \"--json\"];\n}\n\n/**\n * The lint findings the render gate would block on, under the gate's own policy:\n * every error except the deferred-asset advisories in `NEVER_BLOCK`. Validate used\n * to keep only two structural codes, because it linted the composition as authored,\n * before `{{tokens}}` were filled, and a script full of tokens is a syntax error.\n * It now lints the composition prepared the way the render prepares it, so the\n * gate's verdict and validate's are the same verdict, reached before anything bills.\n */\nexport function lintBlockers(lintRaw: string): { code: string; message: string }[] {\n return classifyLint(parseCheckJson(lintRaw))\n .filter((f) => f.severity === \"blocking\")\n .map((f) => ({ code: f.code, message: f.message }));\n}\n\n// Memoized per process. The predictive validate lint should run only where\n// `hyperframes` is a first-class INSTALLED tool — i.e. the e2b render sandbox,\n// where the template installs it globally (on PATH). We probe the bare binary on\n// PATH (not `npx`, which on a dev box may resolve it from an incidental package\n// cache and then spend ~1.3s PER node linting fixtures during plain validate).\n// Off-sandbox the probe is an instant ENOENT, so validate stays free and the\n// gate degrades to a no-op exactly as the execute-time check does.\nlet hyperframesAvailable: boolean | null = null;\nexport async function isHyperframesAvailable(): Promise<boolean> {\n if (hyperframesAvailable !== null) return hyperframesAvailable;\n try {\n await execFileAsync(\"hyperframes\", [\"--version\"], { timeout: 10_000 });\n hyperframesAvailable = true;\n } catch {\n hyperframesAvailable = false;\n }\n return hyperframesAvailable;\n}\n\n/**\n * Validate-time predictive lint of a PREPARED composition dir (see\n * `composition-static-check.ts`). Returns `[]` when the tool is unavailable\n * (degrade gracefully, same as the execute-time gate) — so validate never blocks\n * a valid canvas just because the binary is absent off-sandbox.\n */\nexport async function lintPreparedComposition(\n dir: string,\n timeoutMs: number,\n): Promise<{ code: string; message: string }[]> {\n const lintRaw = await runOne(buildLintArgsNoInstall(dir), timeoutMs);\n return lintRaw === null ? [] : lintBlockers(lintRaw);\n}\n\nexport function buildInspectArgs(dir: string, samples: number): string[] {\n return [\"hyperframes\", \"inspect\", dir, \"--json\", \"--samples\", String(samples)];\n}\n\n/**\n * The nested-composition smoke test, and — with a key — the only check that can\n * see an EMPTY frame.\n *\n * Exiting 0 is what a blank render does too, so the command succeeding never\n * answered the question this check exists for. The question asks about physical\n * plausibility as well as emptiness, because the defect that reads loudest as\n * \"AI-generated\" is not a missing element — it is a present one at the wrong\n * size. Observed: a solar panel roughly five metres tall being lifted onto a\n * roof by one person, in a frame that was otherwise well lit, on brief and\n * perfectly legible. `--describe` sends the captured\n * frames to Gemini and writes back what is actually in them; measured against a\n * real render, it named the blank frames and the single visible element on its\n * own, unprompted by anything but the question.\n *\n * Without a key it stays exactly what it was: no network, no analysis, just proof\n * the render path completed. The deployment ships `GEMINI_API_KEY` by omission,\n * so this degrades rather than fails.\n */\nexport function buildSnapshotArgs(dir: string, frames: number, vision: boolean): string[] {\n const base = [\"hyperframes\", \"snapshot\", dir, \"--frames\", String(frames)];\n return vision\n ? [\n ...base,\n \"--describe\",\n \"Answer four things about this frame. (1) Is it empty or near-empty? Name every element actually visible. \" +\n \"(2) Could what is shown physically happen — object sizes, human scale, and whether every object has its \" +\n \"real-world form (a phone has ONE screen, on its front). Say so plainly if something is impossible. \" +\n \"(3) Read out every number, currency symbol and word visible INSIDE the picture — phone screens, documents, \" +\n \"signage — and say whether any of it contradicts the caption on this frame. \" +\n \"(4) Is the caption legible against what is behind it?\",\n ]\n : [...base, \"--describe\", \"false\"];\n}\n\n// A composition that EMBEDS another (a catalog block wired via data-composition-src)\n// can fail in ways lint + inspect + the studio preview ALL miss — the sub-comp's\n// <style>/<script> landing outside its <template>, a host/internal/timeline id\n// mismatch (a ~45s stall, then no animation), or styling the sub-comp root by class\n// instead of #root. Only an actual render exposes these, so when the host wires a\n// LIVE nested block we additionally run `snapshot` (a real headless render of a few\n// frames) as a smoke test. Comments are stripped first so the seeded EXAMPLE slot\n// (a commented-out data-composition-src) never trips it — only an uncommented,\n// wired embed does.\nexport function usesNestedCompositions(indexHtml: string): boolean {\n const withoutComments = indexHtml.replace(/<!--[\\s\\S]*?-->/g, \"\");\n return /data-composition-src\\s*=/.test(withoutComments);\n}\n\n/**\n * Returns the command's stdout (even on a non-zero exit — `lint`/`inspect` exit\n * non-zero when they FIND issues but still print JSON to stdout), or `null` when\n * the command itself is unavailable/failed to run.\n */\nasync function runOne(args: string[], timeoutMs: number): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync(\"npx\", args, { timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 });\n return stdout;\n } catch (e) {\n const err = e as { stdout?: string; stderr?: string; message?: string };\n if (err.stdout?.includes(\"{\")) return err.stdout; // findings present, JSON on stdout\n const blob = `${err.stderr ?? \"\"} ${err.message ?? \"\"}`;\n if (UNAVAILABLE.test(blob)) return null; // tool missing → degrade gracefully\n return null; // ran but produced nothing parseable → don't block a valid render\n }\n}\n\n/**\n * Run `snapshot` as a render-path smoke test. Unlike lint/inspect it emits no\n * findings JSON — success is exit 0, failure is a throw. We distinguish the tool\n * being absent (degrade gracefully) from the render actually failing (block).\n */\nasync function runSnapshotSmoke(\n args: string[],\n timeoutMs: number,\n): Promise<{ ok: boolean; unavailable: boolean; message: string }> {\n try {\n await execFileAsync(\"npx\", args, { timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 });\n return { ok: true, unavailable: false, message: \"\" };\n } catch (e) {\n const err = e as { stderr?: string; stdout?: string; message?: string };\n const blob = `${err.stderr ?? \"\"} ${err.message ?? \"\"}`;\n if (UNAVAILABLE.test(blob)) return { ok: false, unavailable: true, message: blob };\n return { ok: false, unavailable: false, message: (err.stderr || err.message || \"snapshot failed\").slice(0, 800) };\n }\n}\n\n/**\n * The extra gate a composition that EMBEDS another needs.\n *\n * `check` and the studio preview both miss a broken embed — the sub-comp's\n * <style>/<script> landing outside its <template>, a host/internal id mismatch,\n * styling the sub-comp root by class instead of #root. Only a real render exposes\n * those, so this runs one when a live `data-composition-src` is wired.\n */\nasync function runNestedCompositionSmoke(opts: {\n dir: string;\n nodeId: string;\n ctx: { log: (s: string) => void };\n timeoutMs: number;\n samples: number;\n}): Promise<void> {\n const { dir, nodeId, ctx, timeoutMs, samples } = opts;\n const indexHtml = await readFile(path.join(dir, \"index.html\"), \"utf-8\").catch(() => \"\");\n if (!indexHtml || !usesNestedCompositions(indexHtml)) return;\n\n // A real headless render, orders of magnitude slower than the gate that just\n // ran, so it gets its own generous budget: a slow runner timing out is not an\n // UNAVAILABLE signal and would otherwise hard-block a valid composition.\n const snap = await runSnapshotSmoke(\n buildSnapshotArgs(dir, Math.min(samples, 3), hasFrameVisionKey()),\n Math.max(timeoutMs, 120_000),\n );\n\n if (snap.unavailable) {\n ctx.log(`${nodeId}: hyperframes snapshot unavailable — skipping nested-composition smoke test`);\n return;\n }\n if (!snap.ok) {\n throw new Error(\n `${nodeId}: nested-composition smoke test failed — an embedded block did not render. ` +\n `Check the host\\u2194block id match, that the block's <style>/<script> live inside its <template>, ` +\n `and that it styles #root (not a class).\\n${snap.message}`,\n );\n }\n\n // Read back rather than trusted: exiting 0 is what a blank render does too.\n const described = await readFile(path.join(dir, \"snapshots\", \"descriptions.md\"), \"utf-8\").catch(() => \"\");\n for (const line of condenseFrameDescriptions(described)) {\n ctx.log(`${nodeId}: frame check \\u2014 ${line}`);\n }\n ctx.log(`${nodeId}: nested-composition smoke test passed`);\n}\n\n/**\n * The gate, preferring 0.8's single browser session.\n *\n * Falls back to the two static passes when the installed binary predates\n * `check`, so a sandbox pinned to an older version still gets the gate it had,\n * and returns null only when neither shape is available at all.\n */\nasync function runGate(dir: string, timeoutMs: number, samples: number): Promise<CheckResult | null> {\n const checkRaw = await runOne(buildCheckArgs(dir, samples), timeoutMs);\n if (checkRaw !== null) return classifyGateOutput(checkRaw);\n const [lintRaw, inspectRaw] = await Promise.all([\n runOne(buildLintArgs(dir), timeoutMs),\n runOne(buildInspectArgs(dir, samples), timeoutMs),\n ]);\n if (lintRaw === null && inspectRaw === null) return null;\n return classifyCheckOutput(lintRaw ?? \"\", inspectRaw ?? \"\");\n}\n\nexport async function runHyperframesCheck(opts: {\n dir: string;\n nodeId: string;\n ctx: { log: (s: string) => void };\n timeoutMs: number;\n samples?: number;\n}): Promise<void> {\n const { dir, nodeId, ctx, timeoutMs, samples = 5 } = opts;\n\n const gate = await runGate(dir, timeoutMs, samples);\n if (gate === null) {\n ctx.log(`${nodeId}: hyperframes check/lint/inspect unavailable — skipping pre-render check`);\n return;\n }\n const { blocking, warnings } = gate;\n for (const line of summariseWarnings(warnings)) {\n ctx.log(`${nodeId}: hyperframe check warning ${line}`);\n }\n if (blocking.length > 0) {\n // Grouped like the warnings: one dead GSAP target reported six times is one\n // thing to fix, and six identical bullets buries what else is wrong with it.\n const seen = new Map<string, CheckFinding>();\n for (const b of blocking) seen.set(`${b.source}/${b.code}/${b.message}`, b);\n const detail = [...seen.values()].map((b) => `• [${b.source}/${b.code}] ${explainGateFinding(b)}`).join(\"\\n\");\n throw new Error(\n `${nodeId}: pre-render check failed (${blocking.length} blocking, ${seen.size} distinct)\\n${detail}`,\n );\n }\n\n // Extra gate for nested compositions: lint/inspect/preview can't see a broken\n // embed, only a real render can (see usesNestedCompositions). Runs after the\n // cheap static checks pass, and only when a live data-composition-src is wired.\n // Whether the caption cards are whole clauses. Not something the browser gate\n // can see: a card cut mid-sentence renders perfectly.\n const composition = await readFile(path.join(dir, \"index.html\"), \"utf-8\").catch(() => \"\");\n const captionComplaint = fragmentedCaptions(captionTextsFrom(composition));\n if (captionComplaint) {\n ctx.log(`${nodeId}: caption style — ${captionComplaint}`);\n }\n\n await runNestedCompositionSmoke({ dir, nodeId, ctx, timeoutMs, samples });\n\n ctx.log(`${nodeId}: pre-render check passed (${warnings.length} warning${warnings.length === 1 ? \"\" : \"s\"})`);\n}\n","/**\n * A caption card is a whole clause, not a slice of one.\n *\n * The defect this catches was reported twice, and the second report corrected the\n * first. A finished ad had some cards ending in a full stop and others not, which\n * reads as a mistake — but the fix is not to strip the punctuation. Stripped, the\n * cards are consistent and still wrong: \"En solo nueve días tu casa\" is not a\n * thing anyone wrote, it is a sentence cut in half by a timing decision.\n *\n * What makes captions read correctly is splitting ONLY where the script already\n * punctuates. Every card is then a clause carrying its own mark, which is\n * grammatical — and consistent as a by-product, because a clause always ends in\n * something. A card that ends bare is the tell that a split landed mid-thought.\n *\n * Checked rather than rewritten. The text is the creative's, and silently\n * re-cutting someone's copy at render time is worse than telling them where it\n * was cut badly.\n */\n\n/** Terminal marks a clause can legitimately end on. */\nconst TERMINAL = /[.,;:!?…]$/;\n\n/** The rendered text of each caption card, in composition order. */\nexport function captionTextsFrom(html: string): string[] {\n const cards = html.match(/<div[^>]*class=\"[^\"]*\\bcaption\\b[^\"]*\"[\\s\\S]*?<\\/div>/g) ?? [];\n return cards\n .map((card) =>\n card\n .replace(/<[^>]+>/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim(),\n )\n .filter((text) => text.length > 0);\n}\n\n/**\n * The complaint, or null when every card closes a clause.\n *\n * The last card is exempt: a composition may legitimately end on a bare CTA.\n */\nexport function fragmentedCaptions(texts: string[]): string | null {\n if (texts.length < 2) return null;\n const fragments = texts.slice(0, -1).filter((t) => !TERMINAL.test(t));\n if (fragments.length === 0) return null;\n const example = fragments[0];\n return (\n `${fragments.length} of ${texts.length} caption cards end mid-clause, e.g. \"${example}\". ` +\n `Split captions where the script punctuates, not on a word count — a card that ends bare is a sentence ` +\n `cut in half by a timing decision, and it reads as one.`\n );\n}\n","import { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { CompositionMeta } from \"../../../engine/composition-meta.ts\";\n\n/**\n * Write the top-level `meta.json` Hyperframes itself reads (next to index.html)\n * with {id,name,duration,width,height,fps}. We always overwrite with the\n * engine's values so Hyperframes' interpretation matches ours. Shared by\n * `hyperframe_render` (before render) and `hyperframe_snapshot` (before the\n * pre-render `lint`/`inspect` gate, which reads this file).\n */\nexport async function ensureHyperframesMetaJson(\n tmp: string,\n nodeId: string,\n meta: CompositionMeta,\n duration: number,\n): Promise<void> {\n const metaPath = path.join(tmp, \"meta.json\");\n await writeFile(\n metaPath,\n JSON.stringify(\n {\n id: nodeId,\n name: meta.id,\n duration,\n width: meta.width,\n height: meta.height,\n fps: meta.fps,\n },\n null,\n 2,\n ),\n \"utf-8\",\n );\n}\n","// Single-substitution model used by the composition pipeline.\n//\n// {{var}} in HTML, embedded <style>, embedded <script>\n// ─────► replaced before render by `substituteVariables`\n//\n// String values are inlined as JS-safe literals (`JSON.stringify`-style for\n// numbers, booleans, null, and complex structures; raw text in HTML attribute\n// or text contexts). The engine does the safe stringify — composition authors\n// never call `JSON.parse` again.\n\nconst PATTERN = /\\{\\{\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}/g;\n\n/**\n * Replace every `{{name}}` token in `source` with the JS literal form of\n * `values[name]`. Strings render as their plain text (no surrounding quotes\n * — convenient for both `font-size: {{size_px}}px` and `var X = {{name}};`).\n *\n * Complex values (arrays/objects/numbers/booleans) render via JSON.stringify\n * so the result is a syntactically valid JS literal **and** valid JSON.\n *\n * Missing keys are reported in the returned `missing` array; the engine\n * collects these and surfaces a single aggregated error.\n */\nexport function substituteVariables(\n source: string,\n values: Record<string, unknown>,\n): { output: string; missing: string[]; replaced: string[] } {\n const missing: string[] = [];\n const replaced: string[] = [];\n const seenMissing = new Set<string>();\n const seenReplaced = new Set<string>();\n\n const output = source.replace(PATTERN, (_full, name: string) => {\n if (!Object.hasOwn(values, name)) {\n if (!seenMissing.has(name)) {\n missing.push(name);\n seenMissing.add(name);\n }\n return `{{${name}}}`;\n }\n if (!seenReplaced.has(name)) {\n replaced.push(name);\n seenReplaced.add(name);\n }\n return literalize(values[name]);\n });\n\n return { output, missing, replaced };\n}\n\n/**\n * Render a value as a JS-safe literal embeddable in source code.\n *\n * string → plain text (no quotes — embed inside `\"…\"` if you need a string literal)\n * number → toString() (NaN/Infinity → \"0\")\n * boolean → \"true\" / \"false\"\n * null → \"null\"\n * array/object → JSON.stringify (also valid JS literal)\n * undefined → \"null\"\n *\n * The plain-text behavior for strings is deliberate: most composition uses\n * are HTML attributes or CSS values where surrounding quotes are author-supplied.\n * Authors who need a JS string literal write `\"{{name}}\"` themselves.\n */\nfunction literalize(value: unknown): string {\n if (value === undefined || value === null) return \"null\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) return \"0\";\n return String(value);\n }\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n // Arrays, objects: JSON.stringify produces valid JS *and* JSON.\n return JSON.stringify(value);\n}\n\n/**\n * List every `{{name}}` token referenced in `source`. Used by validators to\n * cross-check declared params vs. tokens actually present in the composition.\n */\nexport function extractReferencedTokens(source: string): string[] {\n const out = new Set<string>();\n for (const m of source.matchAll(PATTERN)) {\n const name = m[1];\n if (name) out.add(name);\n }\n return [...out];\n}\n","// Map noisy Hyperframes/ffmpeg/Chromium stderr into actionable messages.\n// Each entry's `match` is tested in order — first hit wins. The matcher\n// returns the friendly message (with a suggested next step) or null when\n// no pattern applies; callers fall back to raw stderr.\n//\n// New patterns: add an entry; the engine doesn't need updating.\n\ntype Pattern = {\n match: RegExp;\n message: (m: RegExpMatchArray) => string;\n};\n\nconst KNOWN_ERROR_PATTERNS: Pattern[] = [\n {\n match: /Variable issue[^\\n]*\\bduration\\b[^\\n]*not declared/i,\n message: () =>\n `Variable \"duration\" cannot be passed in params or declared in the composition — the engine injects it from the input video's duration. Remove \"duration\" from params.`,\n },\n {\n match: /BeginFrame auto-worker calibration timed out/i,\n message: () =>\n `Hyperframes' renderer is overloaded calibrating its frame workers. Try params.quality = \"draft\", params.workers = 2, or split the composition into smaller pieces.`,\n },\n {\n match: /media_missing_id:\\s*<audio>/i,\n message: () =>\n `A <audio> element in the composition is missing an \"id\" attribute. Add id=\"…\" (e.g. id=\"bg-audio\") so Hyperframes can discover it.`,\n },\n {\n match: /media_missing_id:\\s*<video>/i,\n message: () =>\n `A <video> element in the composition is missing an \"id\" attribute. Add id=\"…\" (e.g. id=\"bg\") so Hyperframes can discover it.`,\n },\n {\n match: /Overlapping clips? detected/i,\n message: () =>\n `Two clip elements overlap in time. Shave 1 ms off the previous clip's data-duration to introduce a sub-frame gap, or re-stitch upstream with the ffmpeg node.`,\n },\n {\n match: /ENOENT[^\\n]*gsap\\.min\\.js/i,\n message: () =>\n `Composition references ./gsap.min.js but the file wasn't found in the render directory. The engine vendors gsap from node_modules; ensure 'gsap' is installed in the CLI package, or self-supply gsap.min.js inside your composition directory.`,\n },\n {\n match: /chromium.*could not be launched|Executable doesn't exist/i,\n message: () =>\n `Headless Chrome is missing. Install Playwright Chromium (\\`pnpm exec playwright install chromium\\`) or run with params.docker = true for a sandboxed render.`,\n },\n];\n\nexport function mapKnownHyperframesError(stderr: string): string | null {\n if (!stderr) return null;\n for (const pattern of KNOWN_ERROR_PATTERNS) {\n const m = stderr.match(pattern.match);\n if (m) return `${pattern.message(m)}\\n\\n--- raw output ---\\n${stderr.slice(0, 1500)}`;\n }\n return null;\n}\n","import { execFile } from \"node:child_process\";\nimport { mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { z } from \"zod\";\nimport { hashCompositionDir } from \"../../engine/composition-hash.ts\";\nimport {\n buildParamsSchema,\n type CompositionMeta,\n defaultFilenameForInput,\n loadCompositionMeta,\n} from \"../../engine/composition-meta.ts\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { type AssetRef, isStageableRef } from \"../../schema/refs.ts\";\nimport { assertInputsMatchMeta, resolveCompositionDir, validateCompositionNode } from \"./hyperframe.ts\";\nimport { refToUrl, type StageableRef, stageAsset } from \"./lib/assets.ts\";\nimport { copyComposition, vendorGsap } from \"./lib/composition-stage.ts\";\nimport { runHyperframesCheck } from \"./lib/hyperframe-check.ts\";\nimport { mapKnownHyperframesError } from \"./lib/hyperframe-errors.ts\";\nimport { ensureHyperframesMetaJson } from \"./lib/hyperframe-meta.ts\";\nimport { substituteVariables } from \"./lib/templating.ts\";\n\nconst _execFileAsync = promisify(execFile);\nconst require_ = createRequire(import.meta.url);\n\nconst WaitForSpec = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"auto\") }),\n z.object({ kind: z.literal(\"selector\"), value: z.string().min(1) }),\n z.object({ kind: z.literal(\"function\"), value: z.string().min(1) }),\n z.object({ kind: z.literal(\"timeout\"), ms: z.number().int().min(0).max(60_000) }),\n]);\n\n// Output is always rendered at the composition's declared width/height at 2x\n// device-scale (retina) for ad delivery. No knobs for full-page or transparent\n// background — composition controls the canvas size and background.\nconst HyperframeSnapshotParams = z\n .object({\n composition: z.string().min(1),\n wait_for: WaitForSpec.optional().default({ kind: \"auto\" }),\n timeout_ms: z.number().int().positive().optional().default(60_000),\n })\n .catchall(z.unknown());\n\nconst HyperframeSnapshotInputs = z.record(z.string(), z.custom<AssetRef>()).optional().default({});\n\nconst HyperframeSnapshotOutputs = z\n .object({\n image: z.custom<AssetRef>(),\n })\n .strict();\n\nconst NODE_OWNED_PARAM_KEYS = new Set([\"composition\", \"wait_for\", \"timeout_ms\"]);\n\n// Retina export — fixed for ad-creative delivery.\nconst DEVICE_SCALE_FACTOR = 2;\n\ntype Params = z.infer<typeof HyperframeSnapshotParams>;\n\nexport const hyperframeSnapshotNode = defineNode({\n id: \"hyperframe_snapshot\",\n version: \"4.1.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Render an HTML/CSS composition to a static PNG via headless Chromium at 2x device-scale (retina). Same composition model as `hyperframe_render` — point `params.composition` at a directory containing `index.html` + `meta.json`.\",\n when_to_use:\n \"Use for static visuals composed from HTML/CSS: UI mockups, branded social posts, text overlays, multi-image grids. \" +\n \"Point `params.composition` at a directory containing `index.html` + `meta.json`. \" +\n \"Inputs are keyed by `meta.json.inputs`. \" +\n \"Output size: composition `meta.json` width/height × 2 (retina). \" +\n '`wait_for` defaults to `\"auto\"` which waits for page `load` + all `<img>` decoded + web fonts ready.',\n inputs: HyperframeSnapshotInputs,\n params: HyperframeSnapshotParams,\n outputs: HyperframeSnapshotOutputs,\n cost: () => ({ credits: 0, seconds_estimate: 3 }),\n\n // The snapshot's own keys (`wait_for`) are not composition params: checked against the\n // render node's key set, a canvas that waited on a selector failed validate every time.\n validateExtra: (args) => validateCompositionNode(args, NODE_OWNED_PARAM_KEYS),\n\n cacheKeyExtras: async ({ params }) => {\n const out: Record<string, string> = {};\n const composition = (params as { composition?: unknown }).composition;\n if (typeof composition !== \"string\") return out;\n const dir = await resolveCompositionDir(composition).catch(() => null);\n if (!dir) return out;\n out.composition_content_sha = await hashCompositionDir(dir);\n return out;\n },\n\n async execute({ inputs, params, ctx }) {\n const compositionDir = await resolveCompositionDir(params.composition);\n const meta = await loadCompositionMeta(compositionDir);\n const compositionParams = validateAndParseDynamicParams(meta, params);\n\n const tmp = await mkdtemp(path.join(tmpdir(), \"hf-snap-\"));\n try {\n await copyComposition(compositionDir, tmp);\n await vendorGsap(tmp, ctx);\n await stageInputs(tmp, inputs, meta, ctx);\n\n const substitutionValues = await buildSubstitutionValues(compositionParams, meta);\n await substituteCompositionFiles(tmp, substitutionValues);\n\n // Pre-render quality gate on the SUBSTITUTED composition: lint + inspect.\n // `inspect` reads the top-level meta.json, so write it first. One sample\n // is enough for a still. Overflow/structural errors block; contrast +\n // deferred-asset advisories only warn; missing tool degrades gracefully.\n await ensureHyperframesMetaJson(tmp, ctx.nodeId, meta, meta.default_duration);\n await runHyperframesCheck({\n dir: tmp,\n nodeId: \"hyperframe_snapshot\",\n ctx,\n timeoutMs: params.timeout_ms,\n samples: 1,\n });\n\n const entryPath = path.join(tmp, \"index.html\");\n const entryUrl = `file://${entryPath}`;\n ctx.log(`snapshotting ${meta.width}x${meta.height}@${DEVICE_SCALE_FACTOR}x wait=${params.wait_for.kind}`);\n\n const pwSpecifier = [\"play\", \"wright\"].join(\"\");\n const playwright = require_(pwSpecifier) as typeof import(\"playwright\");\n const { chromium } = playwright;\n const browser = await chromium.launch({ headless: true, args: chromiumArgsFor(meta) });\n try {\n const context = await browser.newContext({\n viewport: { width: meta.width, height: meta.height },\n deviceScaleFactor: DEVICE_SCALE_FACTOR,\n });\n const page = await context.newPage();\n await page.goto(entryUrl, { waitUntil: \"load\", timeout: params.timeout_ms });\n\n await waitForReady(page, params.wait_for, params.timeout_ms);\n\n const bytes = await page.screenshot({ type: \"png\" });\n\n ctx.log(`captured ${bytes.length} bytes`);\n const ref = await ctx.assets.ingestBytes({\n bytes: Buffer.from(bytes),\n kind: \"image\",\n mime: \"image/png\",\n metadata: {\n width: meta.width * DEVICE_SCALE_FACTOR,\n height: meta.height * DEVICE_SCALE_FACTOR,\n },\n });\n return { image: ref };\n } finally {\n await browser.close();\n }\n } catch (e) {\n const detail = (e as Error).message;\n const friendly = mapKnownHyperframesError(detail);\n if (friendly) throw new Error(`hyperframe_snapshot: ${friendly}`);\n throw e;\n } finally {\n await rm(tmp, { recursive: true, force: true }).catch((cleanupErr) => {\n ctx.log(`warning: tmp cleanup failed (${(cleanupErr as Error).message})`);\n });\n }\n },\n});\n\n/**\n * The flags a composition's page is opened with. File access lets a page read its own staged\n * files (the static ad reads its picture's pixels to place the copy; without it a file://\n * canvas is tainted), but Chromium grants it for every file:// path the Runtime can reach, so\n * only a composition whose meta.json declares `reads_staged_files` gets it.\n */\nexport function chromiumArgsFor(meta: Pick<CompositionMeta, \"reads_staged_files\">): string[] {\n return meta.reads_staged_files ? [\"--allow-file-access-from-files\"] : [];\n}\n\nfunction validateAndParseDynamicParams(meta: CompositionMeta, params: Params): Record<string, unknown> {\n const dynamic: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(params)) {\n if (NODE_OWNED_PARAM_KEYS.has(k)) continue;\n dynamic[k] = v;\n }\n const schema = buildParamsSchema(meta);\n const parsed = schema.safeParse(dynamic);\n if (!parsed.success) {\n const summary = parsed.error.issues.map((i) => `params.${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`hyperframe_snapshot: composition param validation failed — ${summary}`);\n }\n return parsed.data;\n}\n\nasync function stageInputs(\n tmp: string,\n inputs: Record<string, AssetRef>,\n meta: CompositionMeta,\n ctx: { log: (s: string) => void },\n): Promise<void> {\n assertInputsMatchMeta(inputs, meta, \"hyperframe_snapshot\");\n for (const [key, ref] of Object.entries(inputs ?? {})) {\n const spec = meta.inputs[key];\n if (!spec) continue;\n const filename = spec.staged_as ?? defaultFilenameForInput(key, spec.kind);\n await stageAsset(ref as StageableRef, tmp, filename);\n ctx.log(`staged ${spec.kind} → ${filename}`);\n }\n}\n\nasync function buildSubstitutionValues(\n compositionParams: Record<string, unknown>,\n meta: CompositionMeta,\n): Promise<Record<string, unknown>> {\n const out: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(compositionParams)) {\n const spec = meta.params[name];\n if (spec?.kind === \"image\") {\n out[name] = await coerceImageParam(value);\n continue;\n }\n out[name] = value;\n }\n // Snapshots have no real duration; expose a sensible value for compositions\n // shared with hyperframe_render that reference {{duration}}.\n out.duration = meta.default_duration.toFixed(3);\n return out;\n}\n\nfunction coerceImageParam(value: unknown): Promise<string> {\n if (typeof value === \"string\") return Promise.resolve(value);\n if (isStageableRef(value)) return refToUrl(value);\n throw new Error(\"hyperframe_snapshot: image param must be a URL string or AssetRef\");\n}\n\nasync function substituteCompositionFiles(tmp: string, values: Record<string, unknown>): Promise<void> {\n const entryPath = path.join(tmp, \"index.html\");\n const original = await readFile(entryPath, \"utf-8\");\n const { output, missing } = substituteVariables(original, values);\n if (missing.length > 0) {\n throw new Error(\n `hyperframe_snapshot: composition references undefined variables: ${missing.map((m) => `{{${m}}}`).join(\", \")}.`,\n );\n }\n await writeFile(entryPath, output, \"utf-8\");\n}\n\nasync function waitForReady(\n page: import(\"playwright\").Page,\n waitFor: Params[\"wait_for\"],\n timeoutMs: number,\n): Promise<void> {\n switch (waitFor.kind) {\n case \"auto\":\n await page.evaluate(async () => {\n const imgs = Array.from(document.images);\n await Promise.all(\n imgs.map((img) =>\n img.complete && img.naturalWidth > 0 ? Promise.resolve() : img.decode().catch(() => undefined),\n ),\n );\n if (document.fonts && typeof document.fonts.ready?.then === \"function\") {\n await document.fonts.ready;\n }\n });\n return;\n case \"selector\":\n await page.waitForSelector(waitFor.value, { timeout: timeoutMs });\n return;\n case \"function\":\n await page.waitForFunction(waitFor.value, undefined, { timeout: timeoutMs });\n return;\n case \"timeout\":\n await page.waitForTimeout(waitFor.ms);\n return;\n default: {\n const _exhaustive: never = waitFor;\n throw new Error(`hyperframe_snapshot: unknown wait_for kind ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { getToolVersion, runCli } from \"./lib/cli-runner.ts\";\n\nconst execFileAsync = promisify(execFile);\n\nconst OutputDecl = z\n .object({\n kind: z.enum([\"image\", \"video\", \"audio\"]),\n ext: z.string().min(1).max(8),\n })\n .strict();\n\nconst ImageMagickParams = z\n .object({\n args: z.array(z.string()).min(1),\n outputs: z.record(z.string(), OutputDecl).default({}),\n })\n .strict();\n\nconst ImageMagickInputs = z.record(z.string(), z.unknown());\n\nconst ImageMagickOutputs = z.record(z.string(), z.custom<AssetRef>());\n\n/**\n * Resolve which ImageMagick binary to use. Prefer `magick` (v7+); fall back to\n * `convert` (v6). Memoized per process; if neither is on PATH we fail at\n * execute time with a clear message.\n */\nlet resolvedBin: string | undefined;\nasync function resolveBin(): Promise<string> {\n if (resolvedBin) return resolvedBin;\n for (const candidate of [\"magick\", \"convert\"] as const) {\n try {\n await execFileAsync(candidate, [\"-version\"], { encoding: \"utf-8\" });\n resolvedBin = candidate;\n return candidate;\n } catch {\n // try next candidate\n }\n }\n throw new Error(\"imagemagick: neither `magick` nor `convert` found on PATH\");\n}\n\nexport const imagemagickNode = defineNode({\n id: \"imagemagick\",\n version: \"1.0.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Local ImageMagick passthrough. Declare inputs, declare outputs, write the argv you'd type on the command line. Inputs are staged into a tmp dir and reached via `{{in.<slot>}}`; declared outputs are allocated at `{{out.<name>}}` and ingested as assets when the run completes.\",\n when_to_use:\n \"Use whenever you'd reach for ImageMagick on the command line: resize, crop, composite, convert formats, annotate, montage, fx. Pass the full argv as `params.args` (no shell — argv array, no string interpolation). \" +\n \"Wire input images via `inputs.<slot>` (single AssetRef) or `inputs.<slot>: [...]` (array, addressed as `{{in.<slot>.0}}`, …). \" +\n \"Declare every output file under `params.outputs` (name → {kind, ext}); the engine allocates the path, you reference it with `{{out.<name>}}`. \" +\n \"Requires `magick` (v7+) or `convert` (v6) on PATH. URL-style inputs and raw filesystem paths in args are rejected — keep everything in the engine's asset model.\",\n inputs: ImageMagickInputs,\n params: ImageMagickParams,\n outputs: ImageMagickOutputs,\n cost: () => ({ credits: 0, seconds_estimate: 3 }),\n\n async cacheKeyExtras() {\n const bin = await resolveBin().catch(() => \"imagemagick:unavailable\");\n return { imagemagick_version: await getToolVersion(bin) };\n },\n\n async execute({ inputs, params, ctx }) {\n const bin = await resolveBin();\n return await runCli({\n bin,\n args: params.args,\n inputs: inputs as Record<string, AssetRef | AssetRef[]>,\n outputs: params.outputs,\n ctx,\n });\n },\n});\n","import { createRequire } from \"node:module\";\nimport { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { frameGround, type NativeAssets, nativeSurfaceHtml } from \"../../scaffold/native-surface.ts\";\nimport { nativeLayoutInsets } from \"../../scaffold/platform-safe-area.ts\";\nimport { readAdSpec } from \"../../scaffold/static-ad-spec.ts\";\nimport { deliveredSize } from \"../../scaffold/static-sizes.ts\";\nimport { FontRef, ImageRef, JsonRef } from \"../../schema/refs.ts\";\nimport { dataUrl, inkLightness, logoFor, markOfLockup, wholeLogos } from \"./lib/native-assets.ts\";\nimport { bytesOf } from \"./lib/static-review.ts\";\n\nconst require_ = createRequire(import.meta.url);\n\n/** How long the page may take to load its fonts and pictures. */\nconst LOAD_TIMEOUT_MS = 45_000;\n\ntype Asset = { path?: string | undefined; url?: string | undefined };\n\nconst sourceOf = (asset: Asset | undefined): string | undefined => asset?.path ?? asset?.url;\n\nasync function fileOf(asset: Asset | undefined): Promise<{ bytes: Buffer; name: string } | undefined> {\n const source = sourceOf(asset);\n if (!source) return undefined;\n return { bytes: await bytesOf(source), name: source };\n}\n\n/** Whether a hex colour is dark enough to need light type and a light logo. */\nfunction darkHex(hex: string): boolean {\n const n = Number.parseInt(hex.slice(1), 16);\n return 0.2126 * ((n >> 16) & 255) + 0.7152 * ((n >> 8) & 255) + 0.0722 * (n & 255) < 140;\n}\n\nexport const staticNativeNode = defineNode({\n id: \"static_native\",\n version: \"1.0.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Draw a static ad that is a screen people know (a post on X, a WhatsApp or iMessage chat, a wall of Trustpilot or Google reviews, a note, a Slack message) exactly, from HTML, at one delivered size: the `native` block of the ad spec (prompt.json) is laid out in the platform's own layout, type and colours, with the brand's real logo and the photos given, fitted inside the size's safe area. Free and deterministic.\",\n when_to_use:\n \"Scaffolded by `baker canvas scaffold-static-ad` for an ad spec with a `native` block, one node per size (`native_<size>`, role `static_native`). Edit prompt.json's `native` and run again: nothing is billed but the photos whose description changed.\",\n inputs: z\n .object({\n spec: JsonRef,\n /** The brand's logo files (every variant the brand has): the one that reads on the screen is used. */\n logos: z.array(ImageRef).optional(),\n /** The brand's square mark, for an avatar; else cut from the logo's lockup. */\n mark: ImageRef.optional(),\n /** The brand's face, for the frame's headline. */\n font: FontRef.optional(),\n /** The photos the screen shows, in `params.photo_ids` order. */\n photos: z.array(ImageRef).optional(),\n })\n .strict(),\n params: z\n .object({\n format: z.string().min(1),\n placements: z.array(z.string().min(1)).optional(),\n photo_ids: z.array(z.string().regex(/^PHOTO_\\d+$/)).default([]),\n })\n .strict(),\n outputs: z\n .object({\n image: ImageRef,\n // A chat's earliest lines this size scrolled up under its header to keep the rest readable.\n scrolled: z.array(z.string()).optional(),\n })\n .strict(),\n outputKinds: { image: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 4 }),\n async execute({ inputs, params, ctx }) {\n const specSource = sourceOf(inputs.spec);\n if (!specSource) throw new Error(\"static_native: the ad spec (prompt.json) has no file to read\");\n const spec = readAdSpec(JSON.parse((await bytesOf(specSource)).toString(\"utf8\")));\n if (!spec.native) throw new Error(\"static_native: prompt.json has no `native` block to draw\");\n const files = {\n logos: (await Promise.all((inputs.logos ?? []).map(fileOf))).filter((f) => f !== undefined),\n mark: await fileOf(inputs.mark),\n font: await fileOf(inputs.font),\n photos: await Promise.all((inputs.photos ?? []).map(fileOf)),\n };\n const html = await nativePage(spec, files, params);\n const size = deliveredSize(params.format);\n const { png, scrolled } = await screenshot(html, size, ctx.log);\n const image = await ctx.assets.ingestBytes({\n bytes: png,\n kind: \"image\",\n mime: \"image/png\",\n metadata: { width: size.width, height: size.height },\n });\n return { image: image as z.infer<typeof ImageRef>, ...(scrolled.length > 0 ? { scrolled } : {}) };\n },\n});\n\ntype File = { bytes: Buffer; name: string };\n\n/** The pictures as the page loads them: the logo that reads on a card and the one on the ground, the mark, the photos by id. */\nasync function pageAssets(\n files: { logos: File[]; mark?: File | undefined; font?: File | undefined; photos: Array<File | undefined> },\n photoIds: readonly string[],\n ground: string,\n): Promise<NativeAssets> {\n // A logo file cropped through its letters gives way to a whole one, the brand's mark last.\n const whole = await wholeLogos(files.mark ? [...files.logos, files.mark] : files.logos);\n const wholeOfLogos = whole.filter((l) => files.logos.includes(l));\n const logos = wholeOfLogos.length > 0 ? wholeOfLogos : whole;\n const logo = await logoFor(logos, false);\n // The frame's own logo sits on the colour the frame is drawn on.\n const frameLogo = await logoFor(logos, darkHex(ground));\n const cut = !files.mark && logo ? await markOfLockup(logo.bytes).catch(() => null) : null;\n const mark = files.mark ? dataUrl(files.mark.bytes, files.mark.name) : cut ? dataUrl(cut, \"mark.png\") : undefined;\n const photos: Record<string, string> = {};\n photoIds.forEach((id, i) => {\n const file = files.photos[i];\n if (file) photos[id] = dataUrl(file.bytes, file.name);\n });\n // A brand with only a white logo: it is inked dark where it sits on a light ground.\n const light = await Promise.all(\n [logo, frameLogo].map(async (l) =>\n l && ((await inkLightness(l.bytes).catch(() => null)) ?? 0) > 200 ? dataUrl(l.bytes, l.name) : null,\n ),\n );\n const lightLogos = light.filter((u): u is string => u !== null);\n return {\n ...(lightLogos.length > 0 ? { lightLogos } : {}),\n ...(logo ? { logo: dataUrl(logo.bytes, logo.name) } : {}),\n ...(frameLogo ? { frameLogo: dataUrl(frameLogo.bytes, frameLogo.name) } : {}),\n ...(mark ? { mark } : {}),\n ...(files.font ? { font: dataUrl(files.font.bytes, files.font.name) } : {}),\n photos,\n };\n}\n\n/** The page for one size: the spec's screen, its pictures as data URLs, the brand's logo that reads on it. */\nexport async function nativePage(\n spec: ReturnType<typeof readAdSpec>,\n files: { logos: File[]; mark?: File | undefined; font?: File | undefined; photos: Array<File | undefined> },\n params: { format: string; placements?: string[] | undefined; photo_ids: string[] },\n): Promise<string> {\n const native = spec.native;\n if (!native) throw new Error(\"static_native: prompt.json has no `native` block to draw\");\n const size = deliveredSize(params.format);\n // The frame's logo is picked for the colour it is drawn on at this size.\n const assets = await pageAssets(\n files,\n params.photo_ids,\n frameGround(native, spec.brand?.ground ? { ground: spec.brand.ground } : {}, size),\n );\n return nativeSurfaceHtml(native, assets, {\n width: size.width,\n height: size.height,\n safe: nativeLayoutInsets(params.format, params.placements),\n brand: {\n ...(spec.brand?.name ? { name: spec.brand.name } : {}),\n ...(spec.brand?.ground ? { ground: spec.brand.ground } : {}),\n ...(spec.brand?.ink ? { ink: spec.brand.ink } : {}),\n ...(spec.brand?.accent ? { accent: spec.brand.accent } : {}),\n },\n ...(spec.viewer?.market?.language ? { language: spec.viewer.market.language } : {}),\n });\n}\n\nasync function screenshot(\n html: string,\n size: { width: number; height: number },\n log: (s: string) => void,\n): Promise<{ png: Buffer; scrolled: string[] }> {\n const playwright = require_([\"play\", \"wright\"].join(\"\")) as typeof import(\"playwright\");\n const browser = await playwright.chromium.launch({ headless: true });\n try {\n const context = await browser.newContext({\n viewport: { width: size.width / 2, height: size.height / 2 },\n deviceScaleFactor: 2,\n });\n const page = await context.newPage();\n // Fonts load from the web; a page that cannot reach them still draws, in the fallback face.\n await page\n .setContent(html, { waitUntil: \"load\", timeout: LOAD_TIMEOUT_MS })\n .catch((e: Error) => log(`load: ${e.message}`));\n await page.waitForSelector(\"body[data-ready]\", { timeout: LOAD_TIMEOUT_MS });\n const png = Buffer.from(await page.screenshot({ type: \"png\" }));\n const hidden = await page.evaluate(() => document.body.dataset.hidden ?? \"[]\");\n const scrolled = JSON.parse(hidden) as unknown;\n return { png, scrolled: Array.isArray(scrolled) ? scrolled.filter((t): t is string => typeof t === \"string\") : [] };\n } finally {\n await browser.close();\n }\n}\n","/**\n * The pictures a native surface is drawn with, made ready for the page: the brand's logo in the\n * variant that reads on the screen's colour, its square mark for an avatar, and every picture as a\n * data URL the page loads without a server.\n */\nimport sharp from \"sharp\";\n\nconst MIME: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n woff2: \"font/woff2\",\n woff: \"font/woff\",\n ttf: \"font/ttf\",\n otf: \"font/otf\",\n};\n\n/** A file's bytes as a data URL, its type read off its name. */\nexport function dataUrl(bytes: Buffer, name: string): string {\n const ext = name.split(/[?#]/)[0]?.split(\".\").pop()?.toLowerCase() ?? \"png\";\n return `data:${MIME[ext] ?? \"application/octet-stream\"};base64,${bytes.toString(\"base64\")}`;\n}\n\n/** How light a picture's drawn pixels are (0 black, 255 white), ignoring what is transparent; null when nothing is drawn. */\nexport async function inkLightness(bytes: Buffer): Promise<number | null> {\n const { data, info } = await sharp(bytes)\n .resize({ width: 200, height: 200, fit: \"inside\" })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n let sum = 0;\n let n = 0;\n for (let i = 0; i < data.length; i += info.channels) {\n const alpha = data[i + 3] ?? 0;\n if (alpha < 128) continue;\n sum += 0.2126 * (data[i] ?? 0) + 0.7152 * (data[i + 1] ?? 0) + 0.0722 * (data[i + 2] ?? 0);\n n++;\n }\n return n === 0 ? null : sum / n;\n}\n\n/**\n * Of the brand's logo files, the one that reads on a background: on a light one the darker ink,\n * on a dark one the lighter. Audited Oct 2026: a white wordmark on the white review card showed\n * only the icon. The first when none can be read.\n */\nexport async function logoFor<T extends { bytes: Buffer }>(\n logos: readonly T[],\n backgroundIsDark: boolean,\n): Promise<T | undefined> {\n if (logos.length <= 1) return logos[0];\n const scored = await Promise.all(logos.map(async (l) => ({ l, ink: await inkLightness(l.bytes).catch(() => null) })));\n const read = scored.filter((s): s is { l: T; ink: number } => s.ink !== null);\n if (read.length === 0) return logos[0];\n read.sort((a, b) => (backgroundIsDark ? b.ink - a.ink : a.ink - b.ink));\n return read[0]?.l;\n}\n\n/**\n * Whether a logo file's drawing runs off its own edge: a file cropped through its letters. Ink is\n * what is opaque and unlike the file's corner (its background, transparent or not).\n */\nasync function runsOffEdge(bytes: Buffer): Promise<boolean> {\n const { data, info } = await sharp(bytes)\n .resize({ width: 400, height: 400, fit: \"inside\" })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n const { width, height, channels } = info;\n const at = (x: number, y: number) => (y * width + x) * channels;\n const corner = [0, 1, 2, 3].map((c) => data[c] ?? 0);\n const ink = (x: number, y: number) => {\n const i = at(x, y);\n if ((data[i + 3] ?? 0) <= 40) return false;\n return (corner[3] ?? 0) <= 40 || [0, 1, 2].some((c) => Math.abs((data[i + c] ?? 0) - (corner[c] ?? 0)) > 60);\n };\n const share = (n: number, hit: (i: number) => boolean) => {\n let k = 0;\n for (let i = 0; i < n; i++) if (hit(i)) k++;\n return k / n;\n };\n const edges = [\n share(width, (x) => ink(x, 0)),\n share(width, (x) => ink(x, height - 1)),\n share(height, (y) => ink(0, y)),\n share(height, (y) => ink(width - 1, y)),\n ];\n return edges.some((e) => e > 0.02);\n}\n\n/** The brand's logo files whose drawing is whole, or every file when none is (a cropped one is better than none). */\nexport async function wholeLogos<T extends { bytes: Buffer }>(logos: readonly T[]): Promise<T[]> {\n const cut = await Promise.all(logos.map((l) => runsOffEdge(l.bytes).catch(() => false)));\n const whole = logos.filter((_, i) => !cut[i]);\n return whole.length > 0 ? whole : [...logos];\n}\n\n/**\n * The square mark at the head of a lockup (an icon, then a gap, then the wordmark), for an avatar:\n * a lockup in a circle is a line of letters too small to read. Null when the logo is not such a\n * lockup (a wordmark alone, a stacked logo): the avatar then shows the whole logo, contained.\n */\nexport async function markOfLockup(bytes: Buffer): Promise<Buffer | null> {\n const { data, info } = await sharp(bytes).ensureAlpha().raw().toBuffer({ resolveWithObject: true });\n const alpha = (x: number, y: number) => (data[(y * info.width + x) * info.channels + 3] ?? 0) > 40;\n const box = drawnBox(alpha, info.width, info.height);\n if (!box || box.width / box.height < 1.8) return null;\n const mark = leadingMark(alpha, box);\n return mark ? await sharp(bytes).extract(mark).png().toBuffer() : null;\n}\n\ntype Box = { left: number; top: number; width: number; height: number };\ntype Opaque = (x: number, y: number) => boolean;\n\nfunction anyIn(from: number, to: number, hit: (i: number) => boolean): boolean {\n for (let i = from; i <= to; i++) if (hit(i)) return true;\n return false;\n}\n\n/** The box the logo is drawn in, past the file's own transparent margin; null when nothing is drawn. */\nfunction drawnBox(opaque: Opaque, width: number, height: number): Box | null {\n const row = (y: number) => anyIn(0, width - 1, (x) => opaque(x, y));\n let top = 0;\n while (top < height && !row(top)) top++;\n if (top >= height) return null;\n let bottom = height - 1;\n while (bottom > top && !row(bottom)) bottom--;\n const column = (x: number) => anyIn(top, bottom, (y) => opaque(x, y));\n let left = 0;\n while (left < width && !column(left)) left++;\n let right = width - 1;\n while (right > left && !column(right)) right--;\n return { left, top, width: right - left + 1, height: bottom - top + 1 };\n}\n\n/**\n * The mark at the head of a lockup: the first run of drawn columns, roughly square, spanning the\n * lockup's height (a wordmark's first letter, a \"p\", leaves the ascenders' top), then a gap.\n */\nfunction leadingMark(opaque: Opaque, box: Box): Box | null {\n const bottom = box.top + box.height - 1;\n const right = box.left + box.width - 1;\n const column = (x: number) => anyIn(box.top, bottom, (y) => opaque(x, y));\n let x = box.left;\n while (x <= right && column(x)) x++;\n let gap = x;\n while (gap <= right && !column(gap)) gap++;\n const width = x - box.left;\n const ratio = width / box.height;\n if (gap > right || gap - x < box.height * 0.05 || ratio < 0.75 || ratio > 1.4) return null;\n const row = (y: number) => anyIn(box.left, x - 1, (cx) => opaque(cx, y));\n let markTop = box.top;\n while (markTop < bottom && !row(markTop)) markTop++;\n let markBottom = bottom;\n while (markBottom > markTop && !row(markBottom)) markBottom--;\n if ((markBottom - markTop + 1) / box.height < 0.85) return null;\n return { left: box.left, top: box.top, width, height: box.height };\n}\n","/**\n * Look at a finished static ad before anyone is told it is done.\n *\n * Nothing in a static run ever opened the picture it made: a run whose copy came back\n * garbled, whose logo was drawn twice, or whose 9:16 was really a 3:4 reported\n * `completed` the same as a good one. This reads the delivered images back: their pixel\n * shape against the format they are labelled (free, exact), and one vision pass that reads\n * every word against the copy and checks the logo, clipping, legibility and the Stories\n * safe zones. Findings travel on the run record's `review`; a blocking one means the run\n * is not ready to show, whatever the node statuses say.\n *\n * The words are the gate that matters: text mistakes are the first reason marketers\n * reject a version (creativeFeedback, Sept 2026), and 10 of 14 rejected versions had\n * passed a review that only asked \"anything wrong?\". So the model transcribes each\n * expected string per format, and the code compares the transcription to the copy,\n * character for character; a difference is blocking whatever the model thought of it.\n */\nimport { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport sharp from \"sharp\";\nimport { z } from \"zod\";\nimport { perceptualHash } from \"../../../landing-library/visualHash.ts\";\nimport {\n blockingPlacements,\n type SafeArea,\n type SafeAreaPlacement,\n staticSafeArea,\n} from \"../../../scaffold/platform-safe-area.ts\";\nimport { claimedPlatform, namedPlatform } from \"../../../scaffold/review-platforms.ts\";\nimport { type BandEdge, type BandViolation, bandViolations, FRAME_CUT } from \"./band-check.ts\";\nimport { inkOf, type OffBrandInk, offBrandInks } from \"./brand-colour.ts\";\nimport {\n continuesColumn,\n copySimilarity,\n type ExpectedLine,\n type LineMismatch,\n normalizeCopy,\n readingOrder,\n textMismatch,\n} from \"./copy-match.ts\";\n\nimport { boxesOfLine, roleKey, shownBoxes, withWordmark } from \"./must-show.ts\";\nimport { askAboutStills, type RenderFinding, type ReviewUsage } from \"./render-review.ts\";\nimport { readTextBoxes, type TextBox, type TextBoxReading } from \"./text-boxes.ts\";\n\n/** A delivered image whose ratio is further than this from its label is the wrong shape. */\nconst RATIO_TOLERANCE = 0.02;\n\nfunction ratioOf(format: string): number | null {\n const [w, h] = format.split(\":\").map(Number);\n return w && h ? w / h : null;\n}\n\n/** Each delivered image, measured against the format it is labelled. Pure. */\nexport function ratioFindings(images: Array<{ format: string; width: number; height: number }>): RenderFinding[] {\n return images.flatMap(({ format, width, height }) => {\n const want = ratioOf(format);\n if (want === null || width <= 0 || height <= 0) return [];\n if (Math.abs(width / height / want - 1) <= RATIO_TOLERANCE) return [];\n return [\n {\n severity: \"blocking\" as const,\n what: `The ${format} version came out ${width}x${height}, which is not ${format}. It cannot be delivered as ${format}.`,\n },\n ];\n });\n}\n\n/**\n * One candidate's standing with the judge. `scores` per criterion; `of` is the most a\n * criterion can score (the candidates it can beat on a ranking; 5 on an older score line).\n */\nexport type JudgeScore = { image: number; total: number; of?: number; scores: Record<string, number> };\n\nconst RANKING_LINE =\n /^[\\s*#>-]*(?:\\d+[.)]\\s*)?\\**\\s*([a-z][a-z ]*?)\\s*\\**\\s*:\\s*\\**\\s*(image\\s+\\d+(?:\\s*[>=]\\s*image\\s+\\d+)+)/i;\n\n/**\n * The judge's ranking lines (`judgeBrief`): \"<criterion>: Image X > Image Y > …\", best first,\n * `=` for a tie. Each candidate scores, per criterion, the number of candidates ranked below it.\n */\nfunction parseJudgeRanking(reasoning: string): JudgeScore[] {\n const byImage = new Map<number, Record<string, number>>();\n let candidates = 0;\n for (const line of reasoning.split(\"\\n\")) {\n const match = line.match(RANKING_LINE);\n if (!match) continue;\n const criterion = (match[1] ?? \"\").trim().toLowerCase().replace(/\\s+/g, \"_\");\n // Tiers, best first: \"Image 2 = Image 1 > Image 3\" is [[2, 1], [3]].\n const tiers = (match[2] ?? \"\")\n .split(\">\")\n .map((tier) => [...tier.matchAll(/image\\s+(\\d+)/gi)].map((m) => Number(m[1])));\n const ranked = tiers.flat();\n candidates = Math.max(candidates, ranked.length, ...ranked);\n let below = ranked.length;\n for (const tier of tiers) {\n below -= tier.length;\n for (const image of tier) {\n const scores = byImage.get(image) ?? {};\n scores[criterion] = below;\n byImage.set(image, scores);\n }\n }\n }\n return [...byImage.entries()]\n .sort(([a], [b]) => a - b)\n .map(([image, scores]) => ({\n image,\n total: Object.values(scores).reduce((a, b) => a + b, 0),\n of: candidates - 1,\n scores,\n }));\n}\n\n/**\n * The judge's per-candidate standing, read off the lines its brief asks it to start\n * `reasoning` with: the ranking lines `judgeBrief` asks for, or an older judge's score lines\n * (\"Image 1: idea=4 … total=28\"). Lines that follow neither format are skipped: a missing\n * score is reported as missing, never guessed.\n */\nexport function parseJudgeScores(reasoning: string): JudgeScore[] {\n const out: JudgeScore[] = [];\n for (const line of reasoning.split(\"\\n\")) {\n const head = line.match(/^\\s*Image\\s+(\\d+)\\s*:(.*)$/i);\n if (!head) continue;\n const scores: Record<string, number> = {};\n let total: number | undefined;\n for (const pair of (head[2] ?? \"\").matchAll(/([a-z_]+)\\s*=\\s*(\\d+(?:\\.\\d+)?)/gi)) {\n const key = (pair[1] ?? \"\").toLowerCase();\n const value = Number(pair[2]);\n if (key === \"total\") total = value;\n else scores[key] = value;\n }\n if (Object.keys(scores).length === 0) continue;\n out.push({ image: Number(head[1]), total: total ?? Object.values(scores).reduce((a, b) => a + b, 0), scores });\n }\n return out.length > 0 ? out : parseJudgeRanking(reasoning);\n}\n\n/**\n * The candidate the judge's standing puts first. On ranking lines the criteria gate in the\n * order `judgeBrief` lists them, so the first criterion that separates two candidates\n * decides; on older score lines, the highest total. Null with no standing. Pure.\n */\nexport function judgeWinner(judge: readonly JudgeScore[]): JudgeScore | null {\n const byGate = (a: JudgeScore, b: JudgeScore): number => {\n if (a.of === undefined) return b.total - a.total;\n for (const criterion of Object.keys(a.scores)) {\n const gap = (b.scores[criterion] ?? 0) - (a.scores[criterion] ?? 0);\n if (gap !== 0) return gap;\n }\n return b.total - a.total;\n };\n return [...judge].sort(byGate)[0] ?? null;\n}\n\nconst StaticFinding = z.object({\n severity: z.enum([\"blocking\", \"warning\"]),\n format: z.string().optional(),\n what: z.string().trim().min(1),\n});\n\n/** The review model's answer, reduced to findings the agent can act on. */\n/** A call to action left out, which the review prompt says is never a finding (`buildStaticReviewPrompt`). */\nconst ABSENCE_NEVER_A_FINDING =\n /\\b(?:missing|no|lacks?|without)\\s+(?:an?\\s+|any\\s+|clear\\s+|visible\\s+)?(?:call to action|cta)\\b|\\b(?:does not|doesn't) (?:show|tell) the viewer what to do/i;\n\n/** Findings of an item the prompt marks blocking that the model tends to soften (`5a. SUBJECT`). */\nconst ALWAYS_BLOCKING = /^(?:SUBJECT|PERSON):/i;\n\nexport function classifyStaticFindings(json: unknown): RenderFinding[] {\n const findings = (json as { findings?: unknown } | null)?.findings;\n if (!Array.isArray(findings)) return [];\n return findings.flatMap((raw) => {\n const parsed = StaticFinding.safeParse(raw);\n if (!parsed.success) return [];\n const { severity, format, what } = parsed.data;\n if (ABSENCE_NEVER_A_FINDING.test(what)) return [];\n // One subject drawn as two is blocking whatever the model said: it leaves the viewer lost.\n const graded = ALWAYS_BLOCKING.test(what) ? \"blocking\" : severity;\n return [{ severity: graded, what: format?.trim() ? `${format.trim()}: ${what}` : what }];\n });\n}\n\nconst CopyRead = z.object({\n format: z.string().trim().min(1),\n string: z.string(),\n /** What the model read where the string should be; null when it found nothing like it. */\n seen: z.string().nullable(),\n});\n\n/**\n * A string as two readings of it compare: whitespace collapsed, the typographic quotes,\n * dashes and ellipsis a transcription may straighten folded to one form, and case folded\n * too, because case is a typographic treatment the blueprint sets (`font_characteristics`\n * says \"uppercase\" and the model sets the line in caps, as it should). Accents are kept:\n * \"Más\" set as \"Mas\" is a text mistake.\n */\nexport function comparableCopy(text: string): string {\n return text\n .replace(/[‘’‚′]/g, \"'\")\n .replace(/[“”„″]/g, '\"')\n .replace(/[‐-―−]/g, \"-\")\n .replace(/…/g, \"...\")\n .replace(/\\s+/g, \" \")\n .trim()\n .toLowerCase();\n}\n\n/**\n * The copy gate: every expected string, on every format, transcribed by the model and\n * compared here. A string read differently, or not found, blocks; a format the model\n * transcribed nothing for is reported as unread rather than passed; a line a size scrolled off\n * (`scrolled`, a chat on a 9:16) is expected nowhere on it. Pure.\n */\nexport function copyFindings(\n json: unknown,\n copy: string[],\n formats: string[],\n scrolled: Readonly<Record<string, readonly string[]>> = {},\n): RenderFinding[] {\n if (copy.length === 0) return [];\n const raw = (json as { copy?: unknown } | null)?.copy;\n const reads = Array.isArray(raw)\n ? raw.flatMap((entry) => {\n const parsed = CopyRead.safeParse(entry);\n return parsed.success ? [parsed.data] : [];\n })\n : [];\n return formats.flatMap((format) => {\n const forFormat = reads.filter((r) => r.format === format);\n if (forFormat.length === 0) {\n return [\n {\n severity: \"blocking\" as const,\n what: `${format}: the copy could not be read back; look at every word yourself.`,\n },\n ];\n }\n const gone = scrolled[format] ?? [];\n return copy.flatMap((expected) => {\n if (gone.includes(expected)) return [];\n const read = forFormat.find((r) => comparableCopy(r.string) === comparableCopy(expected));\n const seen = read?.seen ?? null;\n if (seen === null) {\n return [{ severity: \"blocking\" as const, what: `${format}: Text: \"${expected}\" is missing.` }];\n }\n if (comparableCopy(seen) === comparableCopy(expected)) return [];\n return [{ severity: \"blocking\" as const, what: `${format}: Text: \"${expected}\" reads \"${seen}\".` }];\n });\n });\n}\n\nconst EDGE_WORDS: Record<BandEdge, string> = {\n top: \"the top of the screen\",\n bottom: \"the bottom of the screen\",\n left: \"the left edge\",\n right: \"the right edge\",\n};\n\n/**\n * Lines past the band that read as one block: the first, how many lines run under it, and whether\n * every line of it supports the ad rather than carries it (`SUPPORTING_ROLES`).\n */\ntype Misplaced = { violation: BandViolation; under: number; supporting?: boolean };\n\n/** A block as the person names it: the logo, or its first line and how many run under it. */\nfunction blockName(v: BandViolation, under: number): string {\n if (v.kind === \"logo\") return \"the logo\";\n return under > 0 ? `\"${v.text}\" and the ${under === 1 ? \"line\" : `${under} lines`} under it` : `\"${v.text}\"`;\n}\n\n/**\n * Where a block sits past the band, said as the person would see it happen. `storiesToo`: the ad\n * set runs in Stories as well as Reels (`blockingPlacements`).\n */\nfunction outsideBand(\n { violation: v, under }: Misplaced,\n placement: SafeAreaPlacement | \"master\",\n surface: string,\n storiesToo: boolean,\n): string {\n const what = blockName(v, under);\n const sits = under > 0 ? \"sit\" : \"sits\";\n const it = under > 0 ? \"them\" : \"it\";\n const edges = v.edges.map((e) => EDGE_WORDS[e]).join(\" and \");\n if (placement === \"reels\" || placement === \"stories\" || placement === \"vertical\") {\n if (v.reelsOnly) return `in Reels, the caption and buttons cover ${what} (${edges}); Stories shows ${it}.`;\n // A 9:16 that runs in Reels and Stories: what blocks it there is covered in both.\n const names = placement === \"reels\" && storiesToo ? [\"Stories\", ...surface.split(\" and \")] : surface.split(\" and \");\n const apps = names.length > 1 ? `${names.slice(0, -1).join(\", \")} and ${names.at(-1)}` : surface;\n return `${what} ${sits} where ${apps} ${names.length > 1 ? \"cover\" : \"covers\"} ${it} (${edges}).`;\n }\n if (placement === \"master\") return `${what} ${sits} outside the space every size keeps clear (${edges}).`;\n return `${what} ${under > 0 ? \"run\" : \"runs\"} off the edge of the picture (${edges}).`;\n}\n\n/**\n * Past the band is blocking where the interface really draws (`covered`), where the frame cuts\n * it, or where it leaves the space every size of a master keeps clear; only close to where\n * Reels or Stories draws is a note. The feed covers nothing: only words the frame cuts are\n * lost, and words merely close to its edge are no finding at all (audited Sept 2026, every\n * one of them was seen). Null: not worth saying.\n */\nfunction bandSeverity(v: BandViolation, placement: SafeAreaPlacement | \"master\"): RenderFinding[\"severity\"] | null {\n if (placement === \"master\" || v.clearance <= FRAME_CUT) return \"blocking\";\n if (placement === \"feed\") return null;\n // Close to where an app draws is drawn on by nothing (Oct 2026 bench: 19 notes, none seen).\n if (!v.covered) return null;\n return v.reelsOnly ? \"warning\" : \"blocking\";\n}\n\nconst EDGE_ORDER: readonly BandEdge[] = [\"top\", \"bottom\", \"left\", \"right\"];\n\n/** The logo's pieces (its mark, its wordmark) as one violation, reaching every edge any piece does. */\nfunction oneLogo(violations: readonly BandViolation[]): BandViolation[] {\n const logo = violations.filter((v) => v.kind === \"logo\");\n if (logo.length <= 1) return [...violations];\n const edges = EDGE_ORDER.filter((e) => logo.some((v) => v.edges.includes(e)));\n const merged: BandViolation = {\n text: \"\",\n kind: \"logo\",\n edges,\n overshoot: Math.max(...logo.map((v) => v.overshoot)),\n clearance: Math.min(...logo.map((v) => v.clearance)),\n covered: logo.some((v) => v.covered),\n // `every` holds on no pieces at all: only a covered logo can be covered by Reels alone.\n reelsOnly: logo.some((v) => v.covered) && logo.filter((v) => v.covered).every((v) => v.reelsOnly),\n };\n const first = violations.indexOf(logo[0] as BandViolation);\n const rest = violations.filter((v) => v.kind !== \"logo\");\n return [...rest.slice(0, first), merged, ...rest.slice(first)];\n}\n\n/**\n * Each box past the band with its violation, the lines of one column that cross the same\n * edges the same way gathered into one block (reported by its first line), and the logo's\n * pieces as one logo. Pure.\n */\n/** Two boxes with more than a tenth of the frame between them: two things, never pieces of one. */\nconst SEPARATE = 0.1;\nfunction apart(a: TextBox, b: TextBox): boolean {\n const across = Math.max(0, b.x0 - a.x1, a.x0 - b.x1);\n const down = Math.max(0, b.y0 - a.y1, a.y0 - b.y1);\n return Math.max(across, down) > SEPARATE;\n}\n\nfunction misplacedBlocks(\n boxes: readonly TextBox[],\n check: (box: TextBox) => BandViolation | undefined,\n supports: (box: TextBox) => boolean = () => false,\n): Misplaced[] {\n const checked = readingOrder(boxes).map((box) => ({ box, violation: check(box) }));\n // The logo sits where nothing covers it: a separate mark elsewhere is printed on something in the\n // scene (a bag, a van), never the ad's logo (Oct 2026 bench).\n const clearLogos = checked.filter((c) => c.box.kind === \"logo\" && !c.violation).map((c) => c.box);\n const past = checked.flatMap(({ box, violation }) =>\n violation && !(box.kind === \"logo\" && clearLogos.length > 0 && clearLogos.every((l) => apart(l, box)))\n ? [{ box, violation }]\n : [],\n );\n const logos = oneLogo(past.filter((p) => p.violation.kind === \"logo\").map((p) => p.violation));\n const blocks: Array<{\n last: TextBox;\n first: BandViolation;\n under: number;\n worst: BandViolation;\n supporting: boolean;\n }> = [];\n for (const { box, violation } of past.filter((p) => p.violation.kind !== \"logo\")) {\n const open = blocks[blocks.length - 1];\n const same =\n open &&\n continuesColumn(open.last, box) !== null &&\n open.first.covered === violation.covered &&\n Boolean(open.first.reelsOnly) === Boolean(violation.reelsOnly) &&\n open.first.edges.join() === violation.edges.join();\n if (open && same) {\n open.last = box;\n open.under++;\n open.supporting = open.supporting && supports(box);\n if (violation.clearance < open.worst.clearance) open.worst = violation;\n continue;\n }\n blocks.push({ last: box, first: violation, under: 0, worst: violation, supporting: supports(box) });\n }\n return [\n ...blocks.map((b) => ({\n violation: { ...b.first, clearance: b.worst.clearance },\n under: b.under,\n supporting: b.supporting,\n })),\n ...logos.map((violation) => ({ violation, under: 0 })),\n ];\n}\n\n/**\n * What the reader measured on one image, as findings the person can read: each line not\n * printed as written, words nobody asked for, anything the placement's interface covers or\n * the frame cuts (blocking), words close to a feed image's edge and designed type in a\n * colour that is not the brand's (warnings). `band`\n * replaces the format's safe area for a variant, which is checked against the band every\n * size of its master keeps clear. Pure.\n */\nexport function measuredFindings(input: {\n format: string;\n boxes: readonly TextBox[];\n lines: readonly ExpectedLine[];\n /** Strings that may appear without being copy: the brand's name, its wordmark, what the scene prints. */\n allowed?: readonly string[];\n /** The words that are the brand's logo (its name, its wordmark); `allowed` unless given. */\n logoWords?: readonly string[];\n /** A size re-laid out from the chosen picture: its supporting lines are held to their meaning (`loadBearing`). */\n adapted?: boolean;\n /** The ad keeps the brand's logo: a size without it, or with it twice, is noted. */\n expectLogo?: boolean;\n placements?: readonly string[];\n band?: SafeArea;\n offBrand?: readonly OffBrandInk[];\n /** A native screen: the words its own interface prints (a status bar, a keyboard) are the app's, wherever they sit. */\n native?: boolean;\n}): RenderFinding[] {\n const { format } = input;\n const text = textMismatch(input.lines, input.boxes, { allowed: input.allowed });\n const area = staticSafeArea(format, input.placements);\n const placement = input.band ? \"master\" : area.placement;\n // The brand's name set beside its mark is its wordmark: part of the logo, where it sits.\n const names = (input.logoWords ?? input.allowed ?? []).filter((n) => normalizeCopy(n).length > 0);\n const placed = withWordmark(input.boxes, names);\n const bandOpts = {\n ...(input.placements ? { placements: input.placements } : {}),\n ...(input.band ? { band: input.band } : {}),\n };\n const copyBoxes = new Set(input.lines.flatMap((line) => boxesOfLine(line, input.boxes)));\n // Only the ad's own words and its logo are measured against the interface (`must-show.ts`).\n const shown = shownBoxes(placed, input.lines, (box) => bandViolations([box], format, bandOpts).length === 0).filter(\n (s) => !(input.native && s.box.kind === \"text\" && interfaceWord(s.box.text, input.allowed)),\n );\n const supporting = new Set(shown.filter((s) => s.supporting).map((s) => s.box));\n const storiesToo = blockingPlacements(input.placements).includes(\"stories\");\n const misplaced = misplacedBlocks(\n shown.map((s) => s.box),\n (box) => {\n const v = bandViolations([box], format, bandOpts)[0];\n // A 9:16 runs in Stories as well: under Reels' footer alone it is still seen there (`blockingArea`).\n if (!v?.covered || placement !== \"reels\") return v;\n const blocked = bandViolations([box], format, { placements: blockingPlacements(input.placements) })[0];\n // Said at the edges Stories covers it at: Reels' buttons down the right are Reels' alone.\n return blocked?.covered ? { ...v, edges: blocked.edges } : { ...v, reelsOnly: true };\n },\n (box) => supporting.has(box),\n );\n const lead = leadLine(input.lines);\n const isLead = (v: BandViolation) =>\n v.kind !== \"logo\" && lead !== undefined && ` ${normalizeCopy(lead.text)} `.includes(` ${normalizeCopy(v.text)} `);\n const blocking = (what: string): RenderFinding => ({ severity: \"blocking\", what: `${format}: ${what}` });\n const small = leadTooSmall(format, input.lines, input.boxes);\n // The brand's wordmark is the logo, never a word of a line that starts with the brand's name (Oct 2026 bench).\n const scale = typeScaleFindings(format, input.lines, placed);\n const logos = input.expectLogo ? logoCountFinding(placed) : null;\n const said = (l: LineMismatch) =>\n l.read === null ? `Text: \"${l.text}\" is missing.` : `Text: \"${l.text}\" reads \"${l.read}\".`;\n const misspelt = misspeltNames([...placed.filter((b) => b.kind === \"logo\").map((b) => b.text), ...text.extra], names);\n const unasked = text.extra.filter(\n (chunk) =>\n // A symbol with no word or number (\"€\", \"%\") is a mark beside one, never copy (Oct 2026 bench).\n /[\\p{L}\\p{N}]/u.test(chunk) &&\n !misspelt.some((m) => m.chunk === chunk) &&\n // The logo read in pieces (\"EALDE\", then \"Business School\" under it) is the logo.\n !fragmentOf(chunk, names, { share: 1, cut: input.native === true }) &&\n !(input.native && (fragmentOf(chunk, input.allowed ?? [], { cut: true }) || crumb(chunk))),\n );\n // A line of the copy printed again (a headline repeated on a screen in the scene) is said twice, not invented.\n const copyTexts = input.lines.map((l) => l.text);\n const echoes = unasked.filter((chunk) => fragmentOf(chunk, copyTexts, { share: 0.8, cut: input.native === true }));\n // Only most of a line said again is worth saying; a word or two of it on a screen or a label in\n // the scene is the scene (Oct 2026 bench: 8 of 9 \"printed twice\" were a prop echoing the copy).\n const twice = echoes.filter((chunk) => mostOfALine(chunk, copyTexts));\n const extra = unasked.filter((chunk) => !echoes.includes(chunk));\n return [\n ...text.lines.flatMap((l): RenderFinding[] => {\n // A native screen's words are typeset from the spec: one read otherwise is the reader running a\n // line into the card behind it (Oct 2026 bench), never the screen. A line it lost still counts.\n if (input.native && l.read !== null) return [];\n if (!input.adapted || loadBearing(l)) return [blocking(said(l))];\n // A re-laid-out size may reword a supporting line: it passes on its meaning, and a note says what changed.\n return l.similarity >= MEANING_MATCH ? [] : [{ severity: \"warning\", what: `${format}: ${said(l)}` }];\n }),\n ...extraWordFindings(format, extra, input.boxes, [...copyBoxes]),\n ...twice.map(\n (t): RenderFinding => ({ severity: \"warning\", what: `${format}: \"${t}\" is on the ad twice; keep it once.` }),\n ),\n ...misspelt.map((m) => blocking(`the brand's name reads \"${m.read}\", not \"${m.name}\".`)),\n ...unbadgedClaims(input.lines, input.boxes).map((c) =>\n blocking(\n `\"${c.line}\" names ${c.platform}, but ${c.platform}'s logo is not on the ad: show its real badge, or drop the claim.`,\n ),\n ),\n // A native screen is laid out as its app lays it out: only what the interface really covers is a defect.\n ...(input.native ? misplaced.filter((m) => m.violation.covered) : misplaced).flatMap((m): RenderFinding[] => {\n // A 9:16 is laid out in Stories' band: under Reels' caption alone, only the lead line is worth saying.\n if (m.violation.reelsOnly && !isLead(m.violation)) return [];\n const severity = bandSeverity(m.violation, placement);\n if (!severity) return [];\n const what = `${format}: ${outsideBand(m, placement, area.surface, storiesToo)}`;\n // A supporting line under an interface is the ad's choice; one the frame cuts, or a master's band, is not.\n const optional =\n severity === \"blocking\" && m.supporting && placement !== \"master\" && m.violation.clearance > FRAME_CUT;\n return optional\n ? [{ severity: \"warning\", what: `${what} Fine for a supporting line nobody has to read.` }]\n : [{ severity, what }];\n }),\n ...(small ? [blocking(`the lead line \"${small}\" is too small to read at the size the feed shows the ad.`)] : []),\n ...scale.map((what): RenderFinding => ({ severity: \"warning\", what: `${format}: ${what}` })),\n ...(logos ? [{ severity: \"warning\" as const, what: `${format}: ${logos}` }] : []),\n ...(input.offBrand ?? []).map(\n (ink): RenderFinding => ({\n severity: \"warning\",\n what: `${format}: \"${ink.text}\" is set in a colour that is not one of the brand's.`,\n }),\n ),\n ];\n}\n\n/** The line the ad leads with: its key figure, else its headline. */\nfunction leadLine(lines: readonly ExpectedLine[]): ExpectedLine | undefined {\n return lines.find((l) => FIGURE_ROLES.has(roleKey(l.role))) ?? lines.find((l) => HEADLINE_ROLES.has(roleKey(l.role)));\n}\n\n/** Roles whose line leads the ad: the key figure first, else the headline (`static-brief.ts` ranks them so). */\nconst FIGURE_ROLES = new Set([\"price\", \"stat\", \"figure\", \"number\", \"discount\"]);\nconst HEADLINE_ROLES = new Set([\"headline\", \"title\"]);\n\n/**\n * The fewest pixels a lead line's letters may stand at the width a feed shows the ad\n * (`THUMBNAIL_WIDTH`): below it the line cannot be read in passing.\n */\nconst LEAD_MIN_PX = 9;\n\n/**\n * The lead line, when it is printed too small to read at the size the feed shows the ad: its\n * tallest box under `LEAD_MIN_PX` at `THUMBNAIL_WIDTH`. Measured, never asked: the review model\n * called the largest words on screen \"too small\" (audited Sept 2026). Null when the ad names no\n * lead, or it was not found (the copy check reports that).\n */\nfunction leadTooSmall(format: string, lines: readonly ExpectedLine[], boxes: readonly TextBox[]): string | null {\n const ratio = ratioOf(format);\n if (ratio === null) return null;\n const lead =\n lines.find((l) => FIGURE_ROLES.has(roleKey(l.role))) ?? lines.find((l) => HEADLINE_ROLES.has(roleKey(l.role)));\n if (!lead) return null;\n const found = boxesOfLine(lead, boxes);\n if (found.length === 0) return null;\n const tallest = Math.max(...found.map((b) => b.y1 - b.y0));\n const thumbnailHeight = THUMBNAIL_WIDTH / ratio;\n return tallest * thumbnailHeight < LEAD_MIN_PX ? lead.text : null;\n}\n\n/**\n * Roles whose words must print exactly on every size: what is offered and what it costs, the\n * button, a promise or a rating, who said a quote and what they said, the small print.\n */\nconst LOAD_BEARING_ROLES = new Set([\n ...FIGURE_ROLES,\n ...HEADLINE_ROLES,\n \"cta\",\n \"button\",\n \"call_to_action\",\n \"offer\",\n \"claim\",\n \"rating\",\n \"quote\",\n \"testimonial\",\n \"attribution\",\n \"badge\",\n \"fine_print\",\n \"legal\",\n \"disclaimer\",\n \"footnote\",\n]);\n\n/** How close a supporting line on a re-laid-out size must read to pass on its meaning. */\nconst MEANING_MATCH = 0.8;\n\n/** Whether a line must print to the letter on every size: a load-bearing role, or any figure (a claim). */\nfunction loadBearing(line: ExpectedLine): boolean {\n return LOAD_BEARING_ROLES.has(roleKey(line.role)) || /[\\d%€$£]/.test(line.text);\n}\n\n/** A string with its accents off, to tell a misspelling from a name set without its accent (\"Kampe\" for \"Kämpe\"). */\nconst folded = (text: string): string => normalizeCopy(text).normalize(\"NFD\").replace(/\\p{M}/gu, \"\");\n\n/** How close a read must be to the brand's name to be that name misspelt rather than another word. */\nconst MISSPELT_NAME = 0.7;\n\n/**\n * The brand's name printed misspelt: a run of words in the logo or among the words nobody asked\n * for, as long as the name, that reads close to it and is not it (audited Sept 2026: the logo\n * read \"zenio\" and was reported only as an extra word). A name set without its accent is the\n * name. Pure.\n */\nfunction misspeltNames(\n texts: readonly string[],\n names: readonly string[],\n): Array<{ chunk: string; read: string; name: string }> {\n const out: Array<{ chunk: string; read: string; name: string }> = [];\n for (const chunk of texts) {\n const words = folded(chunk).split(\" \").filter(Boolean);\n for (const name of names) {\n const want = folded(name);\n const size = want.split(\" \").length;\n if (want.length < 4) continue;\n for (let i = 0; i + size <= words.length; i++) {\n const read = words.slice(i, i + size).join(\" \");\n if (read === want || copySimilarity(read, want) < MISSPELT_NAME) continue;\n out.push({ chunk, read, name });\n break;\n }\n }\n }\n return out;\n}\n\n/**\n * Copy lines that name a review platform as the source of a rating or reviews while nothing on\n * the ad is that platform's badge: a logo box, or a box that is only its name, never the claim's\n * own words. Pure.\n */\nfunction unbadgedClaims(\n lines: readonly ExpectedLine[],\n boxes: readonly TextBox[],\n): Array<{ line: string; platform: string }> {\n return lines.flatMap((line) => {\n const platform = claimedPlatform(line.text);\n if (!platform) return [];\n // The copy's own mentions of the name are read once each, whole or split off: a box that is\n // only the name and read more often than every line together says it is a badge the reader took for text.\n const naming = boxes.filter((b) => b.kind === \"text\" && namedPlatform(b.text) === platform);\n const mentions = new RegExp(platform.pattern.source, `${platform.pattern.flags}g`);\n const said = lines.reduce((n, l) => n + (l.text.match(mentions) ?? []).length, 0);\n const badge =\n boxes.some((b) => b.kind === \"logo\" && namedPlatform(b.text) === platform) ||\n (naming.length > said && naming.some((b) => normalizeCopy(b.text).split(\" \").length <= 3));\n return badge ? [] : [{ line: line.text, platform: platform.name }];\n });\n}\n\nconst BUTTON_ROLES = new Set([\"cta\", \"button\", \"call_to_action\"]);\nconst FINE_PRINT_ROLES = new Set([\"fine_print\", \"legal\", \"disclaimer\", \"footnote\"]);\n\n/** The width a phone shows a static ad at, in CSS pixels (`glance-check.ts` `GLANCE_WIDTH`). */\nconst PHONE_WIDTH = 400;\n/**\n * A supporting line's box under this many pixels at `PHONE_WIDTH` cannot be read in passing. Oct\n * 2026 bench: at 7, a city's name with no descender (\"Cali\") failed beside ones set the same.\n */\nconst LINE_MIN_PX = 6;\n/** How much taller a supporting line must be than the lead to read as the lead itself. */\nconst INVERTED = 1.15;\n\nconst tallest = (line: ExpectedLine, boxes: readonly TextBox[]): number | null => {\n const found = boxesOfLine(line, boxes);\n return found.length > 0 ? Math.max(...found.map((b) => b.y1 - b.y0)) : null;\n};\n\n/**\n * The type scale as measured: the lead line set smaller than a supporting line (the hierarchy\n * upside down: audited Sept 2026), and supporting lines too small to read on a phone (the fine\n * print is meant to be small). Notes, never blocking. Pure.\n */\nfunction typeScaleFindings(format: string, lines: readonly ExpectedLine[], boxes: readonly TextBox[]): string[] {\n const ratio = ratioOf(format);\n if (ratio === null) return [];\n const lead = leadLine(lines);\n const supporting = lines.filter(\n (l) =>\n l !== lead &&\n l.lettering === \"designed\" &&\n !BUTTON_ROLES.has(roleKey(l.role)) &&\n !FINE_PRINT_ROLES.has(roleKey(l.role)),\n );\n const out: string[] = [];\n const leadHeight = lead ? tallest(lead, boxes) : null;\n if (lead && leadHeight !== null) {\n const bigger = supporting\n .map((l) => ({ l, h: tallest(l, boxes) }))\n .filter((x): x is { l: ExpectedLine; h: number } => x.h !== null && x.h > leadHeight * INVERTED)\n .sort((a, b) => b.h - a.h)[0];\n if (bigger)\n out.push(`the main line \"${lead.text}\" is smaller than \"${bigger.l.text}\", so people read the wrong line first.`);\n }\n const phoneHeight = PHONE_WIDTH / ratio;\n const small = supporting.filter((line) => {\n const h = tallest(line, boxes);\n return h !== null && h * phoneHeight < LINE_MIN_PX;\n });\n // Once per size: a small phone mock-up's chat came back as one note per message (Oct 2026 bench).\n const [only] = small;\n if (small.length === 1 && only) out.push(`\"${only.text}\" is hard to read on a phone; make it bigger.`);\n if (small.length > 1) {\n out.push(\n `${small.length} lines are hard to read on a phone (${small.map((l) => `\"${l.text}\"`).join(\", \")}); make them bigger.`,\n );\n }\n return out;\n}\n\n/** A mark under this share of the logo's area is printed on something in the scene, not the logo again. */\nconst SCENE_MARK_SHARE = 0.25;\n\n/** Two logo boxes this close (share of the frame) are one logo: a mark and its wordmark. */\nconst LOGO_GAP = 0.05;\n\n/**\n * The brand's logo on one size, counted: none (the ad keeps it, and this size lost it) or more\n * than one place (audited Sept 2026: the name twice, the logo in the header and the body). Pure.\n */\nfunction logoCountFinding(boxes: readonly TextBox[]): string | null {\n const logos = boxes.filter((b) => b.kind === \"logo\");\n const near = (a: TextBox, b: TextBox) =>\n Math.max(a.x0 - b.x1, b.x0 - a.x1, 0) <= LOGO_GAP && Math.max(a.y0 - b.y1, b.y0 - a.y1, 0) <= LOGO_GAP;\n const clusters: TextBox[][] = [];\n for (const box of logos) {\n const joined = clusters.filter((c) => c.some((b) => near(b, box)));\n const merged = [box, ...joined.flat()];\n for (const c of joined) clusters.splice(clusters.indexOf(c), 1);\n clusters.push(merged);\n }\n if (clusters.length === 0) return \"the brand's logo is not on it.\";\n // A mark far smaller than the logo is printed on something in the scene (a hoodie, a courier's\n // bag: Oct 2026 bench), never the logo set twice.\n const area = (c: TextBox[]) =>\n (Math.max(...c.map((b) => b.x1)) - Math.min(...c.map((b) => b.x0))) *\n (Math.max(...c.map((b) => b.y1)) - Math.min(...c.map((b) => b.y0)));\n const largest = Math.max(...clusters.map(area));\n const set = clusters.filter((c) => area(c) >= largest * SCENE_MARK_SHARE).length;\n return set > 1 ? `the logo is on the ad ${set} times; keep it once.` : null;\n}\n\nexport type StaticVerdict = \"ready\" | \"ready_with_notes\" | \"needs_fixes\" | \"not_reviewed\";\n\n/** What the run may say about itself once the review is in. */\nexport function staticVerdict(findings: RenderFinding[] | null): StaticVerdict {\n if (findings === null) return \"not_reviewed\";\n if (findings.some((f) => f.severity === \"blocking\")) return \"needs_fixes\";\n return findings.length > 0 ? \"ready_with_notes\" : \"ready\";\n}\n\n/** What the review model is asked about the words: transcribe them, or (measured) only their glyphs. */\nfunction textInstructions(copy: readonly string[], measured: boolean): string[] {\n const wordless =\n \"This ad has no words on purpose: the picture is the ad. Do not report missing copy, a missing headline or a missing call to action.\";\n if (measured) {\n return [\n ...(copy.length === 0 ? [wordless] : []),\n \"The words were already read back and measured by another check: do not transcribe them or report their spelling, extra words or position.\",\n \"1. TEXT: garbled or malformed letters, or letters that do not read as words (a brand name inside a drawn logo is fine).\",\n ];\n }\n if (copy.length > 0) {\n return [\n \"First, transcribe the copy. On every full-size image, find each of these strings and copy out EXACTLY what is printed there, character for character (accents, case, punctuation), or null when nothing like it is there:\",\n copy.map((c) => `- \"${c}\"`).join(\"\\n\"),\n \"1. TEXT: extra words that are not in the list, a string shown twice, or garbled letters (a brand name inside a drawn logo is fine).\",\n ];\n }\n return [wordless, \"1. TEXT: any letters or words that appear anyway (invented or garbled text is blocking).\"];\n}\n\n/** What the review prompt is told about the ad. */\nexport type StaticReviewPromptOpts = {\n copy: string[];\n formats: string[];\n brand?: string;\n /** The concept carries a logo and the real file was given to the model. Without one no logo is expected. */\n logo?: boolean;\n /** Each format is also shown at phone-feed thumbnail size, after the full-size images. */\n thumbnails?: boolean;\n /** The devices the concept rests on (its winning mechanisms), to check they survived. */\n mechanisms?: string[];\n /** Who the ad reaches, in one phrase (`describeViewer`): the finished ad is read as them. */\n viewer?: string;\n /**\n * What the ad keeps of the reference it is built on (C7): its load-bearing elements with what of\n * ours carries each, and the trick, checked against the reference shown as the last image.\n */\n concept?: { mustKeep: readonly string[]; idea?: string };\n /**\n * The ad is a native screen drawn exactly from HTML (`native-surface.ts`), named as a viewer\n * would (\"a post on X\"): its interface is meant, and only its photos were painted.\n */\n native?: string;\n /**\n * The box reader already read every word and measured where it sits on every image\n * (`measuredFindings`): the model is not asked to transcribe or to measure the safe\n * zones, which it did by eye.\n */\n measured?: boolean;\n /** Real people the copy names whom the ad was given no photo of: any face shown as them is a stranger. */\n uncastPeople?: readonly string[];\n};\n\n/** The safe-zone item, asked of the model only when nothing measured the boxes. */\nfunction storiesItem(opts: StaticReviewPromptOpts): string[] {\n if (opts.measured || !opts.formats.includes(\"9:16\")) return [];\n const vertical = staticSafeArea(\"9:16\");\n const name = vertical.placement === \"reels\" ? \"REELS AND STORIES\" : vertical.surface.toUpperCase();\n return [\n `4. ${name}: on the 9:16 image, text, a logo or a button that exists clearly inside the top ${Math.round(vertical.top * 100)}% or the bottom ${Math.round(vertical.bottom * 100)}% of the frame (measure it; an element that starts below the top line is fine).`,\n ];\n}\n\n/** What the ad keeps of its reference (C7), else the devices it is built on, as a warning. */\nfunction conceptItem(opts: StaticReviewPromptOpts): string[] {\n const concept = opts.concept && opts.concept.mustKeep.length > 0 ? opts.concept : undefined;\n if (concept) {\n return [\n `8. CONCEPT: the last image is the ad this one is built on, shown only to compare (never judge its words, brand or people, which are another advertiser's). On every size, check the finished ad still carries each of these: ${concept.mustKeep.join(\"; \")}${concept.idea ? `, and still works by the same trick: ${concept.idea}` : \"\"}. Judge each by the role it plays, never by the form the reference gave it (a newspaper on a desk carries a newspaper held in hands; other words carry the same pain): an element is carried when the ad does its job, and what people are doing, how many there are, the setting or the visual style is never a finding. The trick is how the ad persuades, never a look it must have. One element missing is blocking: name it, in a sentence a marketer reads (what the ad no longer does), never a list of details.`,\n ];\n }\n return opts.mechanisms && opts.mechanisms.length > 0\n ? [`8. CONCEPT: the ad no longer carries these devices it is built on (warning): ${opts.mechanisms.join(\"; \")}.`]\n : [];\n}\n\n/** The viewer, the lead line at thumbnail size and the sizes, each only when it applies. */\nfunction contextItems(opts: StaticReviewPromptOpts): {\n viewer: string[];\n thumbnail: string[];\n sizes: string[];\n} {\n return {\n // The pre-render viewer check read the copy and a description of the picture; this reads\n // the picture. A warning: the render is paid for, and the copy already passed that viewer.\n viewer: opts.viewer\n ? [\n `9. VIEWER: seen by ${opts.viewer}, the finished ad does not show what is offered, to whom, and what to do next (warning).`,\n ]\n : [],\n // Measured, the lead's size is checked on its boxes (`leadTooSmall`): the model's call was wrong on most flags.\n thumbnail: opts.measured\n ? []\n : [\n opts.thumbnails\n ? \"7. THUMBNAIL: at thumbnail size the lead line (the largest words) cannot be read. Judge only whether those words can be read, nothing else. That is blocking: most people only ever see the ad that small.\"\n : \"7. THUMBNAIL: the lead line (the largest words) would be unreadable on a phone feed. That is blocking.\",\n ],\n sizes:\n opts.formats.length > 1\n ? [\n \"12. SIZES: one size loses what the others are built on: the letter sheet, the email or chat window, the post card, the comic's panels and their order, the product the ad is about (blocking). Rearranged for its shape is fine.\",\n ]\n : [],\n };\n}\n\n/**\n * The review prompt, built on what makes a static win rather than on a checklist of\n * elements: one idea read at a glance on a phone, the words spelled right, the brand known\n * with the logo covered, nothing clipped, no artifacts. An ad with no logo, no call to\n * action or no copy at all is not missing anything; only what the concept holds is checked.\n */\nexport function buildStaticReviewPrompt(opts: StaticReviewPromptOpts): string {\n const brand = opts.brand ? ` for ${opts.brand}` : \"\";\n const order = opts.formats.join(\", \");\n const shown = opts.thumbnails\n ? `These are the finished versions of ONE static advertisement${brand}, in this order: ${order}. After them come the same versions again, in the same order, shrunk to phone-feed thumbnail size (about 240px wide).`\n : `These are the finished versions of ONE static advertisement${brand}, in this order: ${order}.`;\n const text = textInstructions(opts.copy, opts.measured === true);\n const logo = opts.native\n ? `2. LOGO: the brand's logo appears where ${opts.native} shows it (an avatar, a sender's picture, a summary card), possibly more than once: report it only when distorted or cut.`\n : opts.logo\n ? \"2. LOGO: the brand logo distorted, redrawn in another style, cut off, or set twice as the ad's logo. The brand's mark printed on something in the scene (clothing, a bag, a van, packaging) is the scene, never a second logo; call it mirrored only when its letters read backwards.\"\n : \"2. LOGO: this ad carries no logo on purpose (the platform shows the brand's name and picture); never report a missing logo. A made-up logo or wordmark in the picture is blocking.\";\n const { viewer, thumbnail, sizes } = contextItems(opts);\n const copyShape =\n opts.copy.length > 0 && !opts.measured\n ? ',\"copy\":[{\"format\":\"4:5\",\"string\":\"<a listed string>\",\"seen\":\"<what is printed>|null\"}]'\n : \"\";\n return [\n shown,\n ...text,\n \"\",\n \"Then report, per format, only what is actually wrong:\",\n logo,\n \"3. LAYOUT: anything the ad shows clipped by the frame edge (part of a word, the logo, a button, the product or a face cut away; an element whole but near the edge is not clipped, and scenery the frame crops as any photo does is the picture), overlapping, or unreadable against what is behind it.\",\n ...storiesItem(opts),\n // Tier 0, Oct 2026: the real defects were where a size was extended; 8 of 8 hand claims checked were wrong.\n \"5. PICTURE: a ghost, a smear, a seam, a blurred strip, a duplicated or mirrored object, or garbled pseudo-text in focus where a viewer reads it, typically where the picture was extended to fill a size (blocking). Text out of focus in the background (a screen, a shelf of books) is the scene; an ordinary object a viewer would not stop at on a phone is not malformed.\",\n '5a. SUBJECT: the ad\\'s subject (its pet, person or product) shown more than once as visibly different ones where the ad means one, or a second, different subject beside it that leaves a viewer asking whose story it is (blocking; start the finding with \"SUBJECT:\").',\n ...(opts.uncastPeople && opts.uncastPeople.length > 0\n ? [\n `5c. PERSON: the copy names ${opts.uncastPeople.join(\" and \")}, real, and this ad was given no photo of them: a face shown as them or beside their name is a stranger wearing that name (blocking; start the finding with \"PERSON:\").`,\n ]\n : []),\n \"5b. PEOPLE: a person's hands, face or body drawn so wrong a viewer would notice at a glance on a phone (warning). Never a detail you have to zoom in to see.\",\n \"6. ONE IDEA: two or more elements compete equally for the eye, so no single promise leads (warning).\",\n ...thumbnail,\n ...conceptItem(opts),\n ...viewer,\n \"11. PROOF: stars, a rating or reviews shown while the ad names no source for them anywhere (a review platform, a survey such as CSAT or NPS, a study), so the rating has no source (blocking).\",\n ...sizes,\n ...(opts.native\n ? [\n `13. NATIVE: this ad is ${opts.native}, drawn exactly as the platform shows it: its interface, names, handles, times and counts are meant to be there, and whatever of them it leaves out is left out on purpose (never report the interface as wrong, incomplete or clipped); on a 9:16 the app fills the screen as a phone shows it, its bars and the card with the brand's headline included, and a chat's earliest messages scrolled up under the header are meant; on a wall of reviews the cards piled behind and cut by the frame's edges are the pile, never cut-off copy; the platform prints its own button under the ad, so never report a missing call to action. Report a photo in it that looks AI-made or staged rather than a real person's (blocking when a face is plainly AI-made), or that does not show what the words say (warning).`,\n ]\n : []),\n \"Blocking = it cannot ship as is (a broken logo, clipped content, a ghost or smear in the picture). Warning = it can ship but should improve.\",\n \"Never report an element as missing because ads usually have one: judge only what this concept holds. An empty findings list is the right answer for a clean ad. Keep each item under twenty words.\",\n `Reply with only this JSON: {\"findings\":[{\"severity\":\"blocking|warning\",\"format\":\"4:5\",\"what\":\"...\"}]${copyShape}}`,\n ].join(\"\\n\");\n}\n\n/**\n * How much smaller than the copy an extra word must be set to read as something in the scene (a\n * mug's print, a notebook's scribble) rather than copy: a note, never a block. Tier 1, Oct 2026: a\n * mug's \"Good code, better days\" blocked a newspaper ad.\n */\nconst SCENE_PRINT_SCALE = 0.45;\n/**\n * This many extra lines, each set smaller than every line of copy, are a screen or a document in\n * the scene (code on a laptop, a quote's items), never copy. Oct 2026 bench: a laptop's code, set\n * at 0.45-0.8 of the smallest copy line, blocked a quote ad twice and its erasing left a blank screen.\n */\nconst SCENE_BLOCK_LINES = 3;\n\n/** The share of a copy line's words a repeat must say again to be the line printed twice. */\nconst LINE_REPEAT_SHARE = 0.6;\n\n/** `chunk` says most of one copy line's words again. Pure. */\nfunction mostOfALine(chunk: string, copy: readonly string[]): boolean {\n const wordsOf = (text: string) =>\n normalizeCopy(text)\n .split(\" \")\n .filter((w) => /[\\p{L}\\p{N}]/u.test(w));\n const said = new Set(wordsOf(chunk));\n return copy.some((line) => {\n // Only words count: \"7.300 €\" shares a sign and \"300\" with \"85.300 €\", and is another price.\n const words = wordsOf(line);\n return words.length > 0 && words.filter((w) => said.has(w)).length / words.length >= LINE_REPEAT_SHARE;\n });\n}\n\n/** Words that make a claim of their own: a price, a share, an offer. A bare digit is a calculator key or a date. */\nconst CLAIM = /[%€$£]|\\b(?:free|gratis|gratuit[oa]?s?|off|sale|oferta|descuento|rebajas?|promo)\\b/i;\n\n/** How close to the copy's usual size extra words must be set to read as copy. */\nconst COPY_SIZE_SHARE = 0.8;\n\n/**\n * Extra words: blocking when set at the copy's usual size, or when they make a claim (a figure, an\n * offer) and are not tiny; a note otherwise, as words on a screen, a poster or a mug in the scene\n * (Oct 2026 bench: a poster, a phone's bubbles and a laptop's code blocked 4 ads). Pure.\n */\nfunction extraWordFindings(\n format: string,\n extra: readonly string[],\n boxes: readonly TextBox[],\n copyBoxes: readonly TextBox[],\n): RenderFinding[] {\n if (extra.length === 0) return [];\n const heights = copyBoxes.map((b) => b.y1 - b.y0).filter((h) => h > 0);\n const smallest = heights.length > 0 ? Math.min(...heights) : 0;\n const boxOf = (chunk: string) =>\n boxes.find((b) => b.kind === \"text\" && normalizeCopy(b.text).includes(normalizeCopy(chunk)));\n const height = (chunk: string) => {\n const box = boxOf(chunk);\n return box === undefined ? Number.POSITIVE_INFINITY : box.y1 - box.y0;\n };\n const sorted = [...heights].sort((a, b) => a - b);\n const usual = sorted[Math.floor(sorted.length / 2)] ?? 0;\n const underCopy = extra.filter((chunk) => height(chunk) < usual * COPY_SIZE_SHARE);\n const scenery = (chunk: string) =>\n height(chunk) < smallest * SCENE_PRINT_SCALE ||\n (underCopy.length >= SCENE_BLOCK_LINES && underCopy.includes(chunk));\n const printed = (chunk: string) => usual > 0 && underCopy.includes(chunk) && (!CLAIM.test(chunk) || scenery(chunk));\n const quote = (chunks: readonly string[]) => chunks.map((e) => `\"${e}\"`).join(\", \");\n const asCopy = extra.filter((chunk) => !printed(chunk));\n const inScene = extra.filter(printed);\n return [\n ...(asCopy.length > 0\n ? [\n {\n severity: \"blocking\" as const,\n // `static-autofix.ts` EXTRA_WORDS reads this sentence to erase them.\n what: `${format}: the picture adds words that aren't in the copy: ${quote(asCopy)}. Remove them, or add them to the copy.`,\n },\n ]\n : []),\n ...(inScene.length > 0\n ? [\n {\n severity: \"warning\" as const,\n what: `${format}: words drawn in the scene, not in the copy: ${quote(inScene)}. Fine if they belong there.`,\n },\n ]\n : []),\n ];\n}\n\n/** Bits of 64 two dHashes may differ by and still be one picture: a re-encode, not a change. */\nconst SAME_PICTURE_BITS = 4;\n\nfunction hammingHex(a: string, b: string): number {\n let bits = 0;\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n let x = Number.parseInt(a[i] ?? \"0\", 16) ^ Number.parseInt(b[i] ?? \"0\", 16);\n while (x) {\n bits += x & 1;\n x >>= 1;\n }\n }\n return bits;\n}\n\n/**\n * Each size whose picture is the previous attempt's again (dHash within `SAME_PICTURE_BITS`):\n * a retry that changed nothing, said so the next one changes the spec or the remedy rather\n * than paying for the same picture a third time. A note, never blocking.\n */\nexport async function nearIdenticalFindings(\n current: ReadonlyArray<{ path: string; format: string }>,\n previous: ReadonlyArray<{ path: string; format: string }>,\n): Promise<RenderFinding[]> {\n const hash = async (source: string) => perceptualHash(await bytesOf(source).catch(() => Buffer.alloc(0)));\n const out: RenderFinding[] = [];\n for (const image of current) {\n const before = previous.find((p) => p.format === image.format);\n if (!before) continue;\n const [a, b] = await Promise.all([hash(image.path), hash(before.path)]);\n if (a === null || b === null || hammingHex(a, b) > SAME_PICTURE_BITS) continue;\n out.push({\n severity: \"warning\",\n what: `${image.format}: near-identical to the previous attempt: this retry changed nothing. Change what prompt.json asks for, or use a different fix, rather than running it again.`,\n });\n }\n return out;\n}\n\n/**\n * The sizes whose delivered file is `previous`'s byte for byte: the ones a round did not touch\n * (their nodes came from the cache). Bytes, never a perceptual hash: four words erased move a\n * dHash by less than a re-encode does, and a fixed size must never be read as the old one.\n */\nexport async function samePictureSizes(\n current: ReadonlyArray<{ path: string; format: string }>,\n previous: ReadonlyArray<{ path: string; format: string }>,\n): Promise<string[]> {\n const digest = async (source: string) =>\n createHash(\"sha256\")\n .update(await bytesOf(source).catch(() => Buffer.from(source)))\n .digest(\"hex\");\n const same: string[] = [];\n for (const image of current) {\n const before = previous.find((p) => p.format === image.format);\n if (before && (await digest(image.path)) === (await digest(before.path))) same.push(image.format);\n }\n return same;\n}\n\n/** A box this close to an edge (share of the frame) is at it: a claim it is clipped there is believed. */\nconst AT_EDGE = 0.025;\n\n/** A claim the frame clips something; \"touches\" only when it names the frame, not another element. */\nconst CLIPPED =\n /\\b(?:clipped|cut off|cut by|cropped|runs? off)\\b|\\btouch(?:es|ing)?\\s+(?:the\\s+)?(?:(?:top|bottom|left|right|frame)\\b|edge)/i;\n/** A claim about the words: only they (and the logo, and the button) were measured. */\nconst ABOUT_COPY = /\\b(?:headline|text|copy|line|words?|title|subhead(?:line)?|tagline|price|letters?)\\b|[\"“«]/i;\nconst EDGE_WORD: ReadonlyArray<[BandEdge, RegExp]> = [\n [\"top\", /\\btop\\b/i],\n [\"bottom\", /\\bbottom\\b/i],\n [\"left\", /\\bleft\\b/i],\n [\"right\", /\\bright\\b/i],\n];\n\n/** How far a box sits from each edge it may be claimed to be clipped at. */\nconst clearanceTo = (b: TextBox, edge: BandEdge): number =>\n edge === \"top\" ? b.y0 : edge === \"bottom\" ? 1 - b.y1 : edge === \"left\" ? b.x0 : 1 - b.x1;\n\n/**\n * The review model's claims that the button, the logo or a line is clipped by the frame, checked\n * against the boxes the reader measured on that format: a claim stays blocking when a box of that\n * element reaches the edge it names, and becomes a note when every one sits clear (Tier 0, Oct\n * 2026: 5 of 9 such claims were wrong). Claims about anything else, or a format nobody measured,\n * stand. Pure.\n */\nexport function corroborateEdgeClaims(\n findings: readonly RenderFinding[],\n boxesByFormat: Readonly<Record<string, readonly TextBox[]>>,\n lines: readonly ExpectedLine[],\n): RenderFinding[] {\n const buttons = lines.filter((l) => /^(cta|button|call_to_action)$/.test(roleKey(l.role)));\n return findings.map((f) => {\n if (f.severity !== \"blocking\" || !CLIPPED.test(f.what)) return f;\n const format = f.what.match(/^([\\d.]+:[\\d.]+):/)?.[1];\n const boxes = format ? boxesByFormat[format] : undefined;\n if (!boxes) return f;\n const element = claimedBoxes(f.what, boxes, buttons);\n if (element.length === 0) return f;\n const named = claimedEdges(f.what);\n const atEdge = element.some((b) => named.some((edge) => clearanceTo(b, edge) <= AT_EDGE));\n return atEdge ? f : { ...f, severity: \"warning\" };\n });\n}\n\n/** The edges a clipping claim names; every edge when it names none. */\nfunction claimedEdges(what: string): BandEdge[] {\n const edges = EDGE_WORD.filter(([, word]) => word.test(what)).map(([edge]) => edge);\n return edges.length > 0 ? edges : [\"top\", \"bottom\", \"left\", \"right\"];\n}\n\n/**\n * The boxes of the element a clipping claim is about: the logo, the button, else the words. None\n * for a claim about the picture (a cropped head): nothing measured it, so the claim stands.\n */\nfunction claimedBoxes(what: string, boxes: readonly TextBox[], buttons: readonly ExpectedLine[]): readonly TextBox[] {\n if (/\\b(?:logos?|wordmark|mark)\\b/i.test(what)) return boxes.filter((b) => b.kind === \"logo\");\n if (/\\b(button|cta|call[- ]to[- ]action)\\b/i.test(what)) return buttons.flatMap((l) => boxesOfLine(l, boxes));\n return ABOUT_COPY.test(what) ? boxes.filter((b) => b.kind === \"text\") : [];\n}\n\n/** A delivered image's bytes, from its local file or its URL. */\nexport async function bytesOf(source: string): Promise<Buffer> {\n if (!/^https?:\\/\\//i.test(source)) return await readFile(source);\n const res = await fetch(source);\n if (!res.ok) throw new Error(`fetch ${res.status}`);\n return Buffer.from(await res.arrayBuffer());\n}\n\n/** How wide a static ad shows in a phone feed at a glance: the size the thumbnail test reads it at. */\nconst THUMBNAIL_WIDTH = 240;\n\n/** A delivered image, downscaled to JPEG for the review model: to fit 1024px, or to a thumbnail width. */\nasync function jpegFor(bytes: Buffer, width?: number): Promise<string> {\n const resized = width\n ? sharp(bytes).resize({ width, withoutEnlargement: true })\n : sharp(bytes).resize(1024, 1024, { fit: \"inside\", withoutEnlargement: true });\n const jpeg = await resized.jpeg({ quality: 85 }).toBuffer();\n return jpeg.toString(\"base64\");\n}\n\n/** A picture as the review model is shown it, or null when it cannot be read. */\nasync function stillOf(source: string): Promise<string | null> {\n try {\n return await jpegFor(await bytesOf(source));\n } catch {\n return null;\n }\n}\n\n/** The colour each designed line is set in, read where the reader found it. */\nasync function designedInks(\n image: Buffer,\n lines: readonly ExpectedLine[],\n boxes: readonly TextBox[],\n): Promise<Array<{ text: string; hex: string }>> {\n const inks: Array<{ text: string; hex: string }> = [];\n for (const line of lines.filter((l) => l.lettering === \"designed\")) {\n const [box] = boxesOfLine(line, boxes);\n const hex = box ? await inkOf(image, box) : null;\n if (hex) inks.push({ text: line.text, hex });\n }\n return inks;\n}\n\nexport type StaticImageCheck =\n | { status: \"checked\"; findings: RenderFinding[]; boxes: TextBox[] }\n | { status: \"not_checked\" };\n\n/**\n * A box a native screen's own interface prints, which a copy line sharing the word must not claim:\n * a single key or letter, or exactly one of the screen's own strings (\"Post\", \"espacio\").\n */\nfunction interfaceWord(text: string, own: readonly string[] = []): boolean {\n const read = normalizeCopy(text);\n return crumb(text) || own.some((o) => normalizeCopy(o) === read);\n}\n\n/** Words of two letters at most: what a reader keeps of a card or a key the frame cut, never a line of copy. */\nfunction crumb(text: string): boolean {\n return normalizeCopy(text)\n .split(\" \")\n .every((w) => w.length <= 2);\n}\n\n/**\n * Whether words read off the ad are a piece of one of `own` strings: most of its words are that\n * string's. `cut`: a native screen, whose frame or a card over it cuts words (\"nente me\n * consiguieron el trabajo\" of a review), so a piece of a word counts, and so does a crumb with no\n * word left in it. Pure.\n */\nfunction fragmentOf(chunk: string, own: readonly string[], opts: { share?: number; cut?: boolean } = {}): boolean {\n const share = opts.share ?? 0.5;\n const words = normalizeCopy(chunk)\n .split(\" \")\n .filter((w) => w.length > 1);\n if (words.length === 0) return opts.cut === true;\n return own.some((text) => {\n const theirs = new Set(normalizeCopy(text).split(\" \"));\n // A word the frame or a card cut keeps one end: \"verif\" of \"verificada\", \"mpe\" of \"kampe\".\n const shared = words.filter(\n (w) => theirs.has(w) || (opts.cut === true && [...theirs].some((t) => t.length > w.length && t.includes(w))),\n ).length;\n return shared / words.length >= share;\n });\n}\n\n/**\n * The deterministic checks on one image: its words and logo read with their boxes\n * (`readTextBoxes`), the copy compared line by line, every box measured against the safe\n * area (or `band`), and the ink of each designed line against the brand `palette`.\n * `not_checked` when the reader could not answer: the caller falls back to the review model.\n */\nexport async function checkStaticImage(\n image: Buffer,\n opts: {\n format: string;\n lines: readonly ExpectedLine[];\n allowed?: readonly string[];\n logoWords?: readonly string[];\n placements?: readonly string[];\n band?: SafeArea;\n palette?: readonly string[];\n /** A size re-laid out from the chosen picture (`measuredFindings`). */\n adapted?: boolean;\n /** The ad keeps the brand's logo (`measuredFindings`). */\n expectLogo?: boolean;\n /** A native screen, whose own interface words are never misplaced (`measuredFindings`). */\n native?: boolean;\n /** Maps boxes read on this image to the frame the band is in (a variant rendered at another shape than its master). */\n toFrame?: (boxes: TextBox[]) => TextBox[];\n onSpend?: (usage: ReviewUsage) => void;\n /** The box reader (the vision model behind the gateway); `readTextBoxes` unless given. */\n read?: (image: Buffer) => Promise<TextBoxReading>;\n },\n): Promise<StaticImageCheck> {\n const reading = await (opts.read ?? ((bytes: Buffer) => readTextBoxes(bytes, { onSpend: opts.onSpend })))(image);\n if (reading.status === \"not_checked\") return reading;\n const palette = opts.palette ?? [];\n const inks = palette.length > 0 ? await designedInks(image, opts.lines, reading.boxes) : [];\n const boxes = opts.toFrame ? opts.toFrame(reading.boxes) : reading.boxes;\n const findings = measuredFindings({\n format: opts.format,\n boxes,\n lines: opts.lines,\n ...(opts.allowed ? { allowed: opts.allowed } : {}),\n ...(opts.logoWords ? { logoWords: opts.logoWords } : {}),\n ...(opts.adapted ? { adapted: true } : {}),\n ...(opts.expectLogo ? { expectLogo: true } : {}),\n ...(opts.native ? { native: true } : {}),\n ...(opts.placements ? { placements: opts.placements } : {}),\n ...(opts.band ? { band: opts.band } : {}),\n offBrand: offBrandInks(inks, palette),\n });\n return { status: \"checked\", findings, boxes };\n}\n\n/** The delivered images that could be read, in lockstep: their pixels, shape, review still and thumbnail. */\nasync function loadDelivered(images: ReadonlyArray<{ path: string; format: string }>) {\n const measured: Array<{ format: string; width: number; height: number }> = [];\n const originals: Buffer[] = [];\n const stills: string[] = [];\n const thumbs: string[] = [];\n for (const image of images) {\n try {\n const bytes = await bytesOf(image.path);\n const meta = await sharp(bytes).metadata();\n const still = await jpegFor(bytes);\n const thumb = await jpegFor(bytes, THUMBNAIL_WIDTH);\n measured.push({ format: image.format, width: meta.width ?? 0, height: meta.height ?? 0 });\n originals.push(bytes);\n stills.push(still);\n thumbs.push(thumb);\n } catch {\n // An image that cannot be read is reported by the vision pass, or not at all.\n }\n }\n return { measured, originals, stills, thumbs };\n}\n\n/** Nobody looked at the picture: a measured defect still blocks, but a clean measure is not a review. */\nfunction unreviewed(shape: RenderFinding[], measured: RenderFinding[]): RenderFinding[] | null {\n const known = [...shape, ...measured];\n if (known.some((f) => f.severity === \"blocking\")) return known;\n return shape.length > 0 ? shape : null;\n}\n\n/** What the review prompt is told, from what the run review was given. */\nfunction reviewPromptOpts(\n opts: Parameters<typeof reviewRenderedStatic>[0],\n formats: string[],\n read: boolean,\n referenceShown: boolean,\n): StaticReviewPromptOpts {\n return {\n copy: opts.copy,\n formats,\n brand: opts.brand,\n logo: opts.logo,\n thumbnails: !read,\n mechanisms: opts.mechanisms,\n viewer: opts.viewer,\n ...(referenceShown && opts.concept\n ? { concept: { mustKeep: opts.concept.mustKeep, ...(opts.concept.idea ? { idea: opts.concept.idea } : {}) } }\n : {}),\n ...(opts.native ? { native: opts.native } : {}),\n measured: read,\n ...(opts.uncastPeople && opts.uncastPeople.length > 0 ? { uncastPeople: opts.uncastPeople } : {}),\n };\n}\n\n/**\n * A size's lines and allowed words once a chat scrolled `gone` off it (`static_native`'s `scrolled`):\n * those lines are expected nowhere on it, and what still shows of them is the screen's own. Pure.\n */\nexport function scrolledOff<T extends { lines: readonly ExpectedLine[]; allowed?: readonly string[] }>(\n checks: T,\n gone: readonly string[],\n): T & { lines: ExpectedLine[]; allowed: string[] } {\n return {\n ...checks,\n lines: checks.lines.filter((l) => !gone.includes(l.text)),\n allowed: [...(checks.allowed ?? []), ...gone],\n };\n}\n\n/** What every size's measured check is told, whatever its format (`checkStaticImage`). */\nfunction everySizeChecks(opts: Parameters<typeof reviewRenderedStatic>[0]) {\n const logoWords = [...(opts.brand ? [opts.brand] : []), ...(opts.lockup ?? [])];\n return {\n allowed: [...logoWords, ...(opts.sceneText ?? [])],\n logoWords,\n ...(opts.expectLogo ? { expectLogo: true } : {}),\n ...(opts.native ? { native: true } : {}),\n ...(opts.placements ? { placements: opts.placements } : {}),\n ...(opts.palette ? { palette: opts.palette } : {}),\n onSpend: opts.onSpend,\n };\n}\n\n/**\n * Review the delivered images. The ratio check always runs. The box reader then reads every\n * word and measures where it sits (`checkStaticImage`); where it answered for every image,\n * its findings replace the review model's transcription and its eye for the safe zones. The\n * vision pass runs when a gateway key is present. Returns null only when nothing could look.\n */\nexport async function reviewRenderedStatic(opts: {\n images: Array<{ path: string; format: string }>;\n copy: string[];\n /** By format, the lines a chat scrolled off that size (`scrolledOff`). */\n scrolled?: Readonly<Record<string, readonly string[]>>;\n /** The copy as the ad spec holds it, in-scene or designed; defaults to `copy`, all designed. */\n lines?: ExpectedLine[];\n brand?: string;\n /** The words the brand's logo prints (its wordmark), which are the logo and never extra words. */\n lockup?: readonly string[];\n /** Words the scene prints by itself and other brands' names (`allowedSceneWords`): never extra words. */\n sceneText?: readonly string[];\n /** The brand's colours (the ad spec's), for the ink of designed lines. */\n palette?: string[];\n /** The ad set's placements, for the safe areas (`staticSafeArea`). */\n placements?: string[];\n /** The format the chosen picture was delivered at; every other size was re-laid out from it. */\n masterFormat?: string;\n /** The concept carries a logo and the real file was given to the model. */\n logo?: boolean;\n /** The ad spec keeps the brand's logo: a size without it, or with it twice, is noted. */\n expectLogo?: boolean;\n /** The devices the concept rests on, to check they survived. */\n mechanisms?: string[];\n /** Who the ad reaches, in one phrase (`describeViewer`). */\n viewer?: string;\n /** What the ad keeps of its reference, and the reference's picture (a path or URL), for the concept check (C7). */\n concept?: { mustKeep: readonly string[]; idea?: string; reference: string };\n /** The ad is a native screen, named as a viewer would (\"a post on X\"). */\n native?: string;\n /** Real people the copy names whom the ad was given no photo of (`uncastPeople`). */\n uncastPeople?: readonly string[];\n onSpend?: (usage: ReviewUsage) => void;\n}): Promise<RenderFinding[] | null> {\n const { measured, originals, stills, thumbs } = await loadDelivered(opts.images);\n if (measured.length === 0) return null;\n // The reference the concept check compares with, last among the pictures; skipped if unreadable.\n const reference = opts.concept ? await stillOf(opts.concept.reference) : null;\n const shape = ratioFindings(measured);\n // `measured` grows in lockstep with `stills`, so an unreadable image drops out of both\n // and the model is never told a format it was not shown.\n const formats = measured.map((m) => m.format);\n const lines = opts.lines ?? opts.copy.map((text): ExpectedLine => ({ text, lettering: \"designed\" }));\n const every = { lines, ...everySizeChecks(opts) };\n // Unmeasured, the thumbnails ride in the same call, after the full-size images: the feed test\n // costs one small image per format, not a second review. Measured, the lead's size is read off\n // its boxes and the thumbnails are not sent.\n const ask = (measuredRead: boolean) => {\n const prompt = buildStaticReviewPrompt(reviewPromptOpts(opts, formats, measuredRead, reference !== null));\n const shown = [...(measuredRead ? stills : [...stills, ...thumbs]), ...(reference ? [reference] : [])];\n return askAboutStills(shown, prompt, opts.onSpend);\n };\n // The review is asked as if the words were read while they are being read (they almost always\n // are); a read that fails asks again unmeasured, the one case that pays a second review.\n const asking = ask(true);\n const checks = await Promise.all(\n originals.map((bytes, i) =>\n checkStaticImage(bytes, {\n ...scrolledOff(every, opts.scrolled?.[formats[i] ?? \"\"] ?? []),\n format: formats[i] ?? \"\",\n adapted: opts.masterFormat !== undefined && formats[i] !== opts.masterFormat,\n }),\n ),\n );\n // All or nothing: a review model told the words were read would skip a format nobody read.\n const read = checks.every((c) => c.status === \"checked\");\n const measuredText = read ? checks.flatMap((c) => (c.status === \"checked\" ? c.findings : [])) : [];\n // The gateway answers null on any refusal (a rate limit, a 5xx, a timeout) and a run that\n // ends unreviewed cannot become `rendered`; one more ask is cheap next to the render.\n const first = read ? await asking : (void asking.catch(() => null), null);\n const answer = first ?? (await ask(read));\n if (answer === null) return unreviewed(shape, measuredText);\n const text = read ? measuredText : copyFindings(answer, opts.copy, formats, opts.scrolled);\n const boxesByFormat = read\n ? Object.fromEntries(checks.flatMap((c, i) => (c.status === \"checked\" ? [[formats[i] ?? \"\", c.boxes]] : [])))\n : {};\n return [...shape, ...text, ...corroborateEdgeClaims(classifyStaticFindings(answer), boxesByFormat, lines)];\n}\n","import sharp from \"sharp\";\n\n/**\n * A perceptual fingerprint of what a section actually looks like.\n *\n * The corpus stores many pages per site, and a site puts the same navbar,\n * footer and closing CTA on all of them. Those repeats are byte-different in\n * markup — different nav item is active, different year in the copyright — so\n * neither the HTML nor the structural fingerprint catches them. Pixels do.\n *\n * dHash rather than a mean-brightness hash: it encodes the *gradient* between\n * neighbouring pixels, so it survives the small exposure and anti-aliasing\n * differences between two screenshots of the same element, while still\n * separating two sections that merely share an overall tone. A section is\n * mostly whitespace often enough that average-brightness hashing collapses\n * unrelated sections onto the same value.\n */\n\n/** 9 wide because each row yields 8 comparisons — 8 rows x 8 bits = 64. */\nconst HASH_WIDTH = 9;\nconst HASH_HEIGHT = 8;\n\nexport async function perceptualHash(image: Buffer): Promise<string | null> {\n try {\n const raw = await sharp(image).greyscale().resize(HASH_WIDTH, HASH_HEIGHT, { fit: \"fill\" }).raw().toBuffer();\n return bitsToHex(rowGradientBits(new Uint8Array(raw)));\n } catch {\n // A hash we couldn't compute must not fail the capture. Downstream treats a\n // missing hash as \"unknown\", which keeps the section rather than guessing.\n return null;\n }\n}\n\n/**\n * Is each pixel brighter than the one to its right?\n *\n * Exported for testing: this is the whole of the algorithm, and it is far\n * easier to reason about against a hand-built grid than through sharp.\n */\nexport function rowGradientBits(pixels: Uint8Array): boolean[] {\n const bits: boolean[] = [];\n for (let y = 0; y < HASH_HEIGHT; y++) {\n for (let x = 0; x < HASH_WIDTH - 1; x++) {\n const left = pixels[y * HASH_WIDTH + x] ?? 0;\n const right = pixels[y * HASH_WIDTH + x + 1] ?? 0;\n bits.push(left > right);\n }\n }\n return bits;\n}\n\nfunction bitsToHex(bits: readonly boolean[]): string {\n let hex = \"\";\n for (let index = 0; index < bits.length; index += 4) {\n let nibble = 0;\n for (let offset = 0; offset < 4; offset++) {\n if (bits[index + offset]) nibble |= 1 << (3 - offset);\n }\n hex += nibble.toString(16);\n }\n return hex;\n}\n","/**\n * Whether every word and the logo of a static ad sit where the viewer can see them: inside\n * the safe band a placement's interface leaves clear. The band is never restated here; its\n * one owner is `staticSafeArea(format, placements)`. A variant is checked against its\n * master's planned band instead (`static-sizes.ts` `planStaticFormats`). Boxes are\n * `text-boxes.ts` boxes (frame fractions). Pure.\n */\nimport { type CoveredArea, type SafeArea, staticSafeArea } from \"../../../scaffold/platform-safe-area.ts\";\nimport type { TextBox } from \"./text-boxes.ts\";\n\n/** A reader's box is drawn a little loose around its glyphs: a reach up to this share of the frame is forgiven. */\nexport const BOX_TOLERANCE = 0.01;\n\nexport type BandEdge = \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * A box at most this far from the frame's edge has letters at it or past it: the reader\n * clamps what runs off the frame to the edge, and draws its box a little loose.\n */\nexport const FRAME_CUT = 0.005;\n\n/**\n * `clearance`: how far the box stays from the frame's own edge, on the nearest edge it crosses\n * the band at. `covered`: it reaches where the placement's interface really draws (its\n * `covered` area or Reels' buttons), not only into the margin kept around it.\n */\nexport type BandViolation = {\n text: string;\n kind: TextBox[\"kind\"];\n edges: BandEdge[];\n overshoot: number;\n clearance: number;\n covered: boolean;\n /** Covered only by Reels' tall footer or its buttons: Stories, where the same 9:16 also runs, shows it. */\n reelsOnly?: boolean;\n};\n\n/** Whether a box reaches where the interface draws, past the tolerance. */\nfunction underInterface(box: TextBox, covered: CoveredArea | undefined, tolerance: number): boolean {\n if (!covered) return false;\n if (covered.top - box.y0 > tolerance || box.y1 - (1 - covered.bottom) > tolerance) return true;\n const rail = covered.rail;\n return Boolean(rail && box.y1 > rail.from && box.x1 - (1 - rail.width) > tolerance);\n}\n\n/**\n * Every box that reaches past the band into the interface, with the edges it crosses and\n * its deepest reach. The band is `format`'s safe area for `placements` unless `band` is given.\n */\nexport function bandViolations(\n boxes: readonly TextBox[],\n format: string,\n opts: { placements?: readonly string[]; band?: SafeArea; tolerance?: number } = {},\n): BandViolation[] {\n const own = staticSafeArea(format, opts.placements);\n // A master's planned band is all margin: whatever reaches past it is lost to some size's cut.\n const band = opts.band ?? own;\n const covered = opts.band ? undefined : own.covered;\n const rail = opts.band ? undefined : own.rail;\n const tolerance = opts.tolerance ?? BOX_TOLERANCE;\n return boxes.flatMap((box) => {\n const right = rail && box.y1 > rail.from ? Math.max(band.right, rail.width) : band.right;\n const reach: Record<BandEdge, number> = {\n top: band.top - box.y0,\n bottom: box.y1 - (1 - band.bottom),\n left: band.left - box.x0,\n right: box.x1 - (1 - right),\n };\n const edges = (Object.keys(reach) as BandEdge[]).filter((edge) => reach[edge] > tolerance);\n if (edges.length === 0) return [];\n const toFrame: Record<BandEdge, number> = { top: box.y0, bottom: 1 - box.y1, left: box.x0, right: 1 - box.x1 };\n return [\n {\n text: box.text,\n kind: box.kind,\n edges,\n overshoot: Math.max(...edges.map((edge) => reach[edge])),\n clearance: Math.max(0, Math.min(...edges.map((edge) => toFrame[edge]))),\n covered: opts.band !== undefined || underInterface(box, covered, tolerance),\n },\n ];\n });\n}\n","/**\n * Whether the designed type of a static ad is set in the brand's colours.\n *\n * Distance is CIEDE2000 (chroma-js `deltaE`, the difference a designer means by \"that is\n * not our red\"), the measure the bench's palette score uses (`scripts/creative-evals/\n * palette.mjs`). A warning, never blocking: a model that sets a line in a near-brand colour\n * made a choice, not a mistake a person could not ship. White, black and greys never count:\n * every brand sets type in them.\n */\nimport chroma from \"chroma-js\";\nimport sharp from \"sharp\";\nimport type { TextBox } from \"./text-boxes.ts\";\n\n/** Further than this from every brand colour is another colour, not a shade of one. */\nexport const OFF_BRAND_DELTA_E = 20;\n/** Colours a line's box is split into: its ground, its ink, and the blends of their edges. */\nconst INK_CLUSTERS = 4;\n/** A colour covering less of the box than this is an edge blend or a speck, not the ink. */\nconst MIN_INK_SHARE = 0.05;\n/** Below this LCh chroma an ink is a neutral (white, grey, black), which any brand may use. */\nconst NEUTRAL_CHROMA = 12;\n\nexport type OffBrandInk = { text: string; hex: string; nearest: string; deltaE: number };\n\n/**\n * The colour a line is set in: the pixels of its box quantised to a few colours\n * (libimagequant through sharp, the bench's quantiser). The most common is the ground the\n * letters sit on; the ink is the colour furthest from it, which skips the blends of\n * anti-aliased edges that sit between the two. Null when the box holds one colour or\n * cannot be read.\n */\nexport async function inkOf(image: Buffer, box: TextBox): Promise<string | null> {\n try {\n const { width = 0, height = 0 } = await sharp(image).metadata();\n const left = Math.max(0, Math.floor(box.x0 * width));\n const top = Math.max(0, Math.floor(box.y0 * height));\n const region = {\n left,\n top,\n width: Math.min(width - left, Math.ceil((box.x1 - box.x0) * width)),\n height: Math.min(height - top, Math.ceil((box.y1 - box.y0) * height)),\n };\n if (region.width < 2 || region.height < 2) return null;\n const quantized = await sharp(image)\n .extract(region)\n .removeAlpha()\n .resize({ width: 96, withoutEnlargement: true })\n .png({ palette: true, colours: INK_CLUSTERS, dither: 0 })\n .toBuffer();\n const { data, info } = await sharp(quantized).removeAlpha().raw().toBuffer({ resolveWithObject: true });\n const counts = new Map<string, number>();\n for (let i = 0; i + 2 < data.length; i += info.channels) {\n const hex = chroma(data[i] ?? 0, data[i + 1] ?? 0, data[i + 2] ?? 0).hex();\n counts.set(hex, (counts.get(hex) ?? 0) + 1);\n }\n const total = info.width * info.height;\n const [ground, ...rest] = [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([hex]) => hex);\n const inks = rest.filter((hex) => (counts.get(hex) ?? 0) / total >= MIN_INK_SHARE);\n if (!ground || inks.length === 0) return null;\n return inks.sort((a, b) => chroma.deltaE(ground, b) - chroma.deltaE(ground, a))[0] ?? null;\n } catch {\n return null;\n }\n}\n\n/** Hues this close (degrees) are one colour in another shade: thin type blends its brand navy toward the card it sits on. */\nconst SHADE_HUE = 30;\n/** An ink this dark or this light reads as black or white, however it is tinted. */\nconst NEAR_BLACK_L = 20;\nconst NEAR_WHITE_L = 92;\n\nconst hueGap = (a: number, b: number): number => {\n const d = Math.abs(a - b) % 360;\n return d > 180 ? 360 - d : d;\n};\n\n/** Whether an ink is a lighter or darker shade of one of the palette's colours (same hue, any lightness). */\nfunction shadeOfBrand(hex: string, palette: readonly string[]): boolean {\n const [, , hue] = chroma(hex).lch();\n return palette.some((p) => {\n const [, c, h] = chroma(p).lch();\n return c >= NEUTRAL_CHROMA && !Number.isNaN(h) && !Number.isNaN(hue) && hueGap(h, hue) <= SHADE_HUE;\n });\n}\n\n/**\n * Each saturated ink further than `OFF_BRAND_DELTA_E` from every colour of `palette`, and no\n * shade of one: audited Sept 2026, thin navy body copy on a white card measured as a grey-blue\n * blend and gold on a yellow brand were called off-brand on every version. Near-black and\n * near-white inks never count. Pure.\n */\nexport function offBrandInks(\n inks: ReadonlyArray<{ text: string; hex: string }>,\n palette: readonly string[],\n): OffBrandInk[] {\n if (palette.length === 0) return [];\n return inks.flatMap((ink) => {\n const [lightness, inkChroma] = chroma(ink.hex).lch();\n if (inkChroma < NEUTRAL_CHROMA || lightness < NEAR_BLACK_L || lightness > NEAR_WHITE_L) return [];\n if (shadeOfBrand(ink.hex, palette)) return [];\n const [nearest] = palette\n .map((hex) => ({ hex, deltaE: chroma.deltaE(ink.hex, hex) }))\n .sort((a, b) => a.deltaE - b.deltaE);\n if (!nearest || nearest.deltaE <= OFF_BRAND_DELTA_E) return [];\n return [{ text: ink.text, hex: ink.hex, nearest: nearest.hex, deltaE: nearest.deltaE }];\n });\n}\n","/**\n * Whether a finished static ad prints its copy: every line of the ad spec, in-scene\n * lettering and designed type alike, read back from the picture and compared here.\n *\n * Reader boxes are not the copy's lines: a headline often wraps into two boxes, and a\n * sticker's two lines often come back as one. So the boxes are flattened into one word\n * stream and each line is matched to the run of words it reads best against, scored as\n * 1 - normalised edit distance. A line passes at 98% or better (the bench's \"shippable\"\n * line, `scripts/creative-evals/shippable.mjs`), which on a short line means exact. Words\n * no line claimed are extra words the model invented, except the brand's name and the\n * logo's own wordmark.\n *\n * Case and punctuation are typographic treatments and fold away; accents do not (\"Más\"\n * printed \"Mas\" is a text mistake). Pure.\n */\nimport { distance } from \"fastest-levenshtein\";\nimport type { TextBox } from \"./text-boxes.ts\";\n\n/** The share of a line that may differ (normalised edit distance) and still pass. */\nexport const MAX_LINE_DISTANCE = 0.02;\n\n/** Runs of words this many shorter or longer than the line are tried, so a dropped or added word still finds its line. */\nconst SPAN_SLACK = 2;\n/** Below this, a \"match\" is other text that happens to share letters: the line counts as missing. */\nconst MIN_MATCH = 0.5;\n/** An allowed string (the brand's name) claims words only when it clearly reads as itself. */\nconst ALLOWED_MATCH = 0.8;\n\n/** One line the ad must print; `role` (headline, cta, price…) as the ad spec gives it. */\nexport type ExpectedLine = { text: string; lettering: \"in_scene\" | \"designed\"; role?: string };\n\n/** A line that did not print as written: `read` is the closest run of words, null when nothing came close. */\nexport type LineMismatch = ExpectedLine & { read: string | null; similarity: number };\n\nexport type TextMismatch = { ok: boolean; lines: LineMismatch[]; extra: string[] };\n\n/**\n * A string as the comparison sees it: case, punctuation and spacing folded, letters (accents\n * included), digits and currency kept. An apostrophe joins its word (\"It's\" and \"Its\" read\n * alike), \"&\" reads \"and\", and the trademark signs, being no letters, drop out.\n */\nexport function normalizeCopy(text: string): string {\n return text\n .replace(/[™®℠©]/g, \"\")\n .normalize(\"NFKC\")\n .toLowerCase()\n .replace(/[’'`´ʼ‘]/g, \"\")\n .replace(/\\s*&\\s*/g, \" and \")\n .replace(/[^\\p{L}\\p{N}%€$£]+/gu, \" \")\n .trim();\n}\n\n/**\n * A box an interface the scene draws prints by itself, as the reader read it: a status-bar clock\n * (\"9:41\", \"9:14 AM\"), an avatar's initial, a code editor's line number. Never copy, never extra\n * words; a rating (\"4.8\") or a price (\"29.99\") is neither. Pure.\n */\nexport function isInterfaceChrome(raw: string): boolean {\n const text = raw.trim();\n if (/^\\d{1,2}:\\d{2}(?:\\s?[ap]\\.?\\s?m\\.?)?$/i.test(text)) return true;\n if (/^\\d{1,3}$/.test(text)) return true;\n return /^\\p{L}$/u.test(text);\n}\n\n/** The vertical extent a box's line takes, never zero. */\nconst lineHeight = (b: TextBox): number => Math.max(b.y1 - b.y0, 0.005);\n\n/** Whether `next` is the line under `last` in one column (below it, close, overlapping it across): the gap between them, else null. */\nexport function continuesColumn(last: TextBox, next: TextBox): number | null {\n if (next.y0 < last.y0 + 0.3 * lineHeight(last)) return null;\n const gap = next.y0 - last.y1;\n if (gap > 1.2 * Math.max(lineHeight(last), lineHeight(next))) return null;\n const overlap = Math.min(last.x1, next.x1) - Math.max(last.x0, next.x0);\n const narrower = Math.min(last.x1 - last.x0, next.x1 - next.x0);\n return narrower > 0 && overlap >= 0.3 * narrower ? gap : null;\n}\n\n/**\n * Whether `next` carries on `last`'s line to its right: the same row, and a gap under a line's\n * height (a colour change splits one line into boxes; a second column sits further off).\n */\nfunction continuesRow(last: TextBox, next: TextBox): boolean {\n const shared = Math.min(last.y1, next.y1) - Math.max(last.y0, next.y0);\n const height = Math.min(lineHeight(last), lineHeight(next));\n const gap = next.x0 - last.x1;\n return shared >= 0.5 * height && gap > -0.5 * height && gap <= Math.max(lineHeight(last), lineHeight(next));\n}\n\ntype Placed<T> = { box: T; index: number };\n/** One printed line: its boxes left to right, and the box they span. */\ntype Row<T> = { items: Array<Placed<T>>; span: TextBox };\n\n/** The boxes as printed lines: boxes side by side on one row join it, left to right. */\nfunction rowsOf<T extends TextBox>(boxes: readonly T[]): Array<Row<T>> {\n const rows: Array<Row<T>> = [];\n const byLeft = boxes.map((box, index) => ({ box, index })).sort((a, b) => a.box.x0 - b.box.x0 || a.index - b.index);\n for (const item of byLeft) {\n const row = rows.find((r) => {\n const last = r.items[r.items.length - 1];\n return last !== undefined && continuesRow(last.box, item.box);\n });\n if (!row) {\n rows.push({ items: [item], span: item.box });\n continue;\n }\n row.items.push(item);\n const { span } = row;\n row.span = {\n text: \"\",\n kind: span.kind,\n x0: Math.min(span.x0, item.box.x0),\n y0: Math.min(span.y0, item.box.y0),\n x1: Math.max(span.x1, item.box.x1),\n y1: Math.max(span.y1, item.box.y1),\n };\n }\n return rows;\n}\n\n/**\n * The boxes as blocks of text read top to bottom, each column on its own: a reader may return\n * a two-column row line by line across both columns, and a line wrapped in one column must still\n * read as one run of words. Boxes side by side on one row are one line, read left to right. Blocks\n * keep the order the reader first met them in. Pure.\n */\nexport function readingOrder<T extends TextBox>(boxes: readonly T[]): T[] {\n const first = (row: Row<T>) => Math.min(...row.items.map((i) => i.index));\n const byTop = rowsOf(boxes).sort((a, b) => a.span.y0 - b.span.y0 || first(a) - first(b));\n const blocks: Array<Array<Row<T>>> = [];\n for (const row of byTop) {\n let best: { block: Array<Row<T>>; gap: number } | null = null;\n for (const block of blocks) {\n const last = block[block.length - 1];\n const gap = last ? continuesColumn(last.span, row.span) : null;\n if (gap !== null && (best === null || gap < best.gap)) best = { block, gap };\n }\n if (best) best.block.push(row);\n else blocks.push([row]);\n }\n return blocks\n .sort((a, b) => Math.min(...a.map(first)) - Math.min(...b.map(first)))\n .flatMap((block) => block.flatMap((row) => row.items.map((i) => i.box)));\n}\n\n/** 1 - normalised edit distance between two normalised strings. */\nexport function copySimilarity(a: string, b: string): number {\n return similarity(a, b);\n}\n\n/**\n * Normalized copy with the letters a sans-serif draws as one stroke made one: a capital I and a\n * lowercase l (normalized, \"i\" and \"l\"). Bench, Oct 2026: \"AI business platform\" read back as\n * \"Al business platform\" blocked an ad whose viewer reads it exactly as written.\n */\nconst sameStroke = (text: string): string => text.replace(/l/g, \"i\");\n\nfunction similarity(a: string, b: string): number {\n const longest = Math.max(a.length, b.length);\n return longest === 0 ? 1 : 1 - distance(sameStroke(a), sameStroke(b)) / longest;\n}\n\ntype Word = { word: string; box: number };\ntype Span = { indexes: number[]; text: string };\ntype Match = { span: Span; similarity: number };\n\n/** Every contiguous run of `words` whose length is within the slack of `size`. */\nfunction spans(words: Word[], size: number): Span[] {\n const out: Span[] = [];\n for (let length = Math.max(1, size - SPAN_SLACK); length <= size + SPAN_SLACK; length++) {\n for (let start = 0; start + length <= words.length; start++) {\n const indexes = Array.from({ length }, (_, i) => start + i);\n out.push({ indexes, text: indexes.map((i) => words[i]?.word ?? \"\").join(\" \") });\n }\n }\n return out;\n}\n\n/** Best pairs first, one to one: a word printed once satisfies one line. */\nfunction assign(targets: string[], words: Word[], claimed: Set<number>, threshold: number): Map<number, Match> {\n const pairs = targets.flatMap((target, index) =>\n target.length === 0\n ? []\n : spans(words, target.split(\" \").length).map((span) => ({\n index,\n span,\n similarity: similarity(target, span.text),\n })),\n );\n pairs.sort((a, b) => b.similarity - a.similarity);\n const matches = new Map<number, Match>();\n for (const pair of pairs) {\n if (pair.similarity < threshold) break;\n if (matches.has(pair.index)) continue;\n if (pair.span.indexes.some((i) => claimed.has(i))) continue;\n matches.set(pair.index, { span: pair.span, similarity: pair.similarity });\n for (const i of pair.span.indexes) claimed.add(i);\n }\n return matches;\n}\n\n/** Every hyphenated compound the copy spells (\"all-natural\"), as the break below compares it. */\nfunction hyphenatedCompounds(texts: readonly string[]): Set<string> {\n const out = new Set<string>();\n for (const text of texts) {\n for (const [compound] of text\n .normalize(\"NFKC\")\n .toLowerCase()\n .matchAll(/\\p{L}+(?:[-‐‑]\\p{L}+)+/gu)) {\n const words = compound.split(/[-‐‑]/u);\n for (let i = 1; i < words.length; i++) out.add(`${words[i - 1]}-${words[i]}`);\n }\n }\n return out;\n}\n\n/**\n * A line ending in a hyphen continues on the next: a long word broken at the line end is one\n * word (\"adver-\" / \"tising\"), unless the copy itself spells that hyphen (\"All-\" / \"natural\"\n * for \"All-natural\"), where the hyphen is the word's own and stays.\n */\nfunction joinHyphenBreaks(texts: readonly string[], compounds: ReadonlySet<string>): string[] {\n const out: string[] = [];\n let carry = \"\";\n for (const text of texts) {\n const next = text.trim();\n const before = carry.match(/\\p{L}+$/u)?.[0] ?? \"\";\n const after = next.match(/^\\p{L}+/u)?.[0] ?? \"\";\n const own = carry !== \"\" && compounds.has(`${before}-${after}`.normalize(\"NFKC\").toLowerCase());\n const line = carry + (own ? \"-\" : \"\") + next;\n const broken = line.match(/^(.*\\p{L})[-‐‑]$/u);\n if (broken) carry = broken[1] ?? \"\";\n else {\n out.push(line);\n carry = \"\";\n }\n }\n if (carry) out.push(`${carry}-`);\n return out;\n}\n\n/**\n * A line the copy breaks on purpose (\"15%\\noff\" on a badge) is printed as separate lines,\n * which the reader may return in any order with other text between them: each part is\n * matched on its own, and the line passes when every part does.\n */\nfunction lineParts(text: string): string[] {\n const parts = text.split(/\\r?\\n/).filter((part) => part.trim().length > 0);\n return parts.length > 0 ? parts : [text];\n}\n\n/**\n * The lines of `expected` that did not print as written, and the extra words printed that\n * no line asked for. `allowed` strings (the brand's name) may appear without being extra;\n * logo boxes never count as copy.\n */\nexport function textMismatch(\n expected: readonly ExpectedLine[],\n boxes: readonly TextBox[],\n opts: { allowed?: readonly string[] } = {},\n): TextMismatch {\n const compounds = hyphenatedCompounds(expected.map((line) => line.text));\n const texts = readingOrder(boxes.filter((b) => b.kind === \"text\")).map((b) => b.text);\n const joined = joinHyphenBreaks(texts, compounds);\n const words: Word[] = joined.flatMap((text, box) =>\n normalizeCopy(text)\n .split(\" \")\n .filter((word) => word.length > 0)\n .map((word) => ({ word, box })),\n );\n const claimed = new Set<number>();\n const parts = expected.flatMap((line, lineIndex) =>\n lineParts(line.text).map((text) => ({ lineIndex, text: normalizeCopy(text) })),\n );\n const matches = assign(\n parts.map((part) => part.text),\n words,\n claimed,\n MIN_MATCH,\n );\n const allowed = (opts.allowed ?? []).map(normalizeCopy).filter((a) => a.length > 0);\n // The brand's name (or a word the scene prints) may show more than once: every occurrence is claimed.\n for (let before = -1; claimed.size !== before; ) {\n before = claimed.size;\n assign(allowed, words, claimed, ALLOWED_MATCH);\n }\n // A line that is the brand's name is printed when the logo carries it (the sender of an email mock-up).\n const logos = boxes.filter((b) => b.kind === \"logo\").map((b) => normalizeCopy(b.text));\n const inLogo = (text: string): boolean => {\n const t = normalizeCopy(text);\n return (\n allowed.some((a) => similarity(a, t) >= ALLOWED_MATCH) && logos.some((l) => similarity(l, t) >= ALLOWED_MATCH)\n );\n };\n\n const lines = expected.flatMap((line, index): LineMismatch[] => {\n if (inLogo(line.text)) return [];\n // A part with nothing to read (\"★★★★★\", \"→\") folds to \"\" and is never matched: it cannot be checked, so it never fails.\n const lineMatches = parts.flatMap((part, partIndex) =>\n part.lineIndex === index && part.text.length > 0 ? [matches.get(partIndex)] : [],\n );\n if (lineMatches.length === 0) return [];\n const score = Math.min(...lineMatches.map((match) => match?.similarity ?? 0));\n if (score >= 1 - MAX_LINE_DISTANCE) return [];\n const read = lineMatches.flatMap((match) => (match ? [match.span.text] : [])).join(\" \");\n return [{ ...line, read: read.length > 0 ? read : null, similarity: score }];\n });\n\n const extra: Array<{ text: string; box: number }> = [];\n words.forEach(({ word, box }, index) => {\n if (claimed.has(index)) return;\n const previous = words[index - 1];\n const last = extra.at(-1);\n if (previous && !claimed.has(index - 1) && previous.box === box && last) {\n last.text += ` ${word}`;\n } else {\n extra.push({ text: word, box });\n }\n });\n const unasked = extra.filter((chunk) => !isInterfaceChrome(joined[chunk.box] ?? \"\")).map((chunk) => chunk.text);\n return { ok: lines.length === 0 && unasked.length === 0, lines, extra: unasked };\n}\n","/**\n * What of a static ad an interface must not cover: its logo and its own copy. What the scene\n * prints (a poster, a quote on a desk, a screen) may sit under any interface, and so may a copy\n * line printed again in the scene while the line itself sits clear. Among the copy, a supporting\n * line (`SUPPORTING_ROLES`) may sit partly under one; the logo, the button, the headline, the\n * figure and the offer may not. The brand's name read as words beside its mark is its wordmark,\n * and so the logo (`withWordmark`). The review measures these boxes and the placer moves the ad for\n * them, so the two never disagree. Pure.\n */\nimport { copySimilarity, type ExpectedLine, normalizeCopy } from \"./copy-match.ts\";\nimport type { TextBox } from \"./text-boxes.ts\";\n\n/** A copy line as these checks need it: its words, and the role it plays. */\nexport type CopyLine = Pick<ExpectedLine, \"text\" | \"role\">;\n\n/** A role as the role sets spell it: \"Call to action\" → \"call_to_action\". */\nexport const roleKey = (role: string | undefined): string =>\n (role ?? \"\")\n .trim()\n .toLowerCase()\n .replace(/[\\s-]+/g, \"_\");\n\n/**\n * Roles of lines that support the ad rather than carry it: one partly under an interface is a note.\n * An unnamed role counts as one that must be seen.\n */\nexport const SUPPORTING_ROLES: ReadonlySet<string> = new Set([\n \"body\",\n \"caption\",\n \"subheadline\",\n \"subhead\",\n \"subtitle\",\n \"supporting\",\n \"support\",\n \"label\",\n \"tagline\",\n \"eyebrow\",\n \"kicker\",\n \"description\",\n \"detail\",\n \"details\",\n \"note\",\n \"fine_print\",\n \"legal\",\n \"disclaimer\",\n \"footnote\",\n \"attribution\",\n]);\n\n/** How close a read must be to the brand's name to be its wordmark (\"Kampe\" for \"Kämpe\", \"KAMPE\"). */\nconst WORDMARK_MATCH = 0.8;\n\n/** The boxes, a text box read close to one of the logo's words (its wordmark) marked as the logo. */\nexport function withWordmark(boxes: readonly TextBox[], logoWords: readonly string[]): TextBox[] {\n const names = logoWords.map(normalizeCopy).filter((n) => n.length > 0);\n const isName = (text: string) => {\n const read = normalizeCopy(text);\n return read.length > 0 && names.some((n) => copySimilarity(n, read) >= WORDMARK_MATCH);\n };\n return boxes.map((b) => (b.kind === \"text\" && isName(b.text) ? { ...b, kind: \"logo\" as const } : b));\n}\n\n/** The boxes a line was read in: every text box whose words are a run of the line's words. */\nexport function boxesOfLine(line: CopyLine, boxes: readonly TextBox[]): TextBox[] {\n const words = ` ${normalizeCopy(line.text)} `;\n return boxes.filter((b) => {\n const read = normalizeCopy(b.text);\n return b.kind === \"text\" && read.length > 0 && words.includes(` ${read} `);\n });\n}\n\n/** One box an interface must not cover, and whether every line it belongs to only supports the ad. */\nexport type ShownBox = { box: TextBox; supporting: boolean };\n\n/**\n * The logo's boxes and the copy's, in the order given, each copy box with whether it only\n * supports the ad. `clear` says whether a box sits clear of the interface: a copy box with the\n * same words in another clear box of the same line is that line printed again, and left out.\n */\nexport function shownBoxes(\n boxes: readonly TextBox[],\n lines: readonly CopyLine[],\n clear: (box: TextBox) => boolean,\n): ShownBox[] {\n const linesOfBox = new Map<TextBox, Array<{ line: CopyLine; found: TextBox[] }>>();\n for (const line of lines) {\n const found = boxesOfLine(line, boxes);\n for (const box of found) linesOfBox.set(box, [...(linesOfBox.get(box) ?? []), { line, found }]);\n }\n const echo = (box: TextBox, of: Array<{ found: TextBox[] }>) =>\n of.every(({ found }) =>\n found.some((other) => other !== box && normalizeCopy(other.text) === normalizeCopy(box.text) && clear(other)),\n );\n return boxes.flatMap((box): ShownBox[] => {\n if (box.kind === \"logo\") return [{ box, supporting: false }];\n const of = linesOfBox.get(box);\n if (!of || echo(box, of)) return [];\n return [{ box, supporting: of.every(({ line }) => SUPPORTING_ROLES.has(roleKey(line.role))) }];\n });\n}\n","import { execFile } from \"node:child_process\";\nimport { mkdtemp, readdir, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\nimport { z } from \"zod\";\nimport { hasOpenRouterKey, openRouterKey } from \"../../../../env.ts\";\n\n/**\n * Somebody looks at the finished video.\n *\n * Until this existed, nothing in the pipeline ever saw a generated clip. The\n * frame-vision pass (`hyperframe-check.ts`) reads the HTML composition — captions,\n * overlay, legibility — and stops there; what the video model actually returned was\n * never opened by anything. The gap is not theoretical. A finished 28-second ad\n * shipped with:\n *\n * • two fitters mounting a solar panel lying across the roof tiles with no rails\n * under it, one of them drilling into the face of the module;\n * • a bracket base and a phone floating unattached in shot;\n * • a worker missing a leg;\n * • four different men playing what the script treats as one customer;\n * • no brand mark in any frame, and no closing card — it ends on a man shuffling\n * blank paper, four seconds after the last caption.\n *\n * Every one is obvious to anyone watching and invisible to every check the run\n * already had. Reviewing the render is not a nicety layered on top: it is the first\n * time anything in the system looks at the thing it sold.\n *\n * Reported, never blocking. The render is already paid for by the time this runs, so\n * discarding it would burn the credits twice; the findings come back as the work to\n * do on the next pass.\n */\n\n/**\n * The dense model, not the fast one — measured, not assumed.\n *\n * Asked about the frame holding the panel with no rails under it, `gemini-flash-latest`\n * answered \"nothing wrong\" at 512px, at 1024px, and alone on a single frame. The same\n * frame and the same question on `gemini-pro-latest` came back with the missing rails,\n * the hovering bracket and a worker's missing leg. A cheap reviewer that always says\n * clean is worse than none: it launders an unwatched render as a checked one.\n *\n * `~google/…` is the floating alias: OpenRouter resolves it per request, so the pass\n * follows the current Pro tier instead of a version somebody has to remember to bump.\n * Safe to float here in a way it was not before — the price now comes back with the\n * answer, so a model swap underneath cannot put a stale rate in the ledger.\n */\nexport const RENDER_REVIEW_MODEL = \"~google/gemini-pro-latest\";\n\n/** How many frames the reviewer sees. */\nexport const RENDER_REVIEW_FRAMES = 12;\n\n/** Widest edge of a sampled frame, in pixels. */\nexport const RENDER_REVIEW_WIDTH = 1024;\n\n/**\n * The model that WATCHES the finished ad, as opposed to the one that reads stills.\n *\n * A flash tier on purpose: this pass uploads the whole video, so it is the expensive one\n * to run, and the questions it answers (is this the right accent, does the voice fit the\n * mouth, do the cuts hold) are judgements a flash model makes as well as a pro one.\n *\n * Floating, like the stills model above, and for the same reason. Measured 2026-09-17,\n * the alias resolves to `google/gemini-3.8-flash` — which a pinned `gemini-3.7-flash`\n * would have kept us off for no reason anybody had decided.\n */\nexport const RENDER_WATCH_MODEL = \"~google/gemini-flash-latest\";\n\n/**\n * What one review call spent, as the GATEWAY reports it.\n *\n * `usage.cost` is OpenRouter's own charge for the call, in dollars, returned with the\n * answer because the request opts in with `usage: { include: true }` — the same thing\n * every other provider call on this platform does. So there is no price list to keep:\n * the sandbox relays the provider's number instead of computing one, and `reportCost`\n * records it exactly as it records a canvas node's.\n *\n * This pass used to call Gemini directly, on the stated grounds that the video goes up\n * inline and OpenRouter had no route that takes one. It does: a `video_url` content part\n * accepts a base64 data URL, verified end to end against this account — HTTP 200, 128\n * video tokens counted, the model describing the clip it was sent. The direct route cost\n * a hand-maintained price table, a second credential in every Runtime, and a model id\n * that had to stay pinned so the table's key would not drift out from under it.\n *\n * Tokens are still carried, because they are what a human reads when asking why a review\n * cost what it did. The dollars are what the ledger uses.\n */\nexport type ReviewUsage = { model: string; inputTokens: number; outputTokens: number; costUsd: number };\n\ntype UsageSink = (usage: ReviewUsage) => void;\n\n/**\n * The gateway's own accounting, as returned. Absent on a failed call, which costs nothing.\n *\n * The cost is the first STRICTLY POSITIVE of `usage.cost` and\n * `cost_details.upstream_inference_cost`, which is the rule `extractCostUsd` already\n * states in `convex/lib/canvasOpenRouter.ts`: on a BYOK, cached or free-tier route\n * OpenRouter charges 0 while the upstream figure is what the company actually owes, and\n * picking the first DEFINED value instead reports that call as free. Duplicated rather\n * than imported because the backend is not on this side of the wire; if the two ever\n * have to disagree, that is the bug.\n */\nexport function usageFrom(model: string, body: unknown): ReviewUsage | null {\n const usage = (\n body as {\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cost?: number;\n cost_details?: { upstream_inference_cost?: number };\n };\n }\n )?.usage;\n if (!usage) return null;\n const positive = (value: number | undefined): number | null =>\n typeof value === \"number\" && Number.isFinite(value) && value > 0 ? value : null;\n return {\n model,\n inputTokens: usage.prompt_tokens ?? 0,\n outputTokens: usage.completion_tokens ?? 0,\n costUsd: positive(usage.cost) ?? positive(usage.cost_details?.upstream_inference_cost) ?? 0,\n };\n}\n\n/** Above this the video is not watched at all, rather than watched in part. */\nexport const WATCH_MAX_BYTES = 60 * 1024 * 1024;\n\nexport type RenderFinding = {\n severity: \"blocking\" | \"warning\";\n /** What is wrong, in the words the agent has to act on. */\n what: string;\n /** The scene file to edit to fix it, when the finding belongs to one shot. */\n scene?: string;\n};\n\n/** One shot's slot on the finished timeline, and the file that describes it. */\nexport type SceneWindow = { file: string; startS: number; endS: number };\n\n/**\n * The scene a moment belongs to.\n *\n * A finding that says \"at 18.36s a worker stands on a solar panel\" is only half an\n * instruction: somebody still has to work out which of six shot briefs produced that\n * second. The blueprint already knows — every scene file carries `start_s`/`end_s` —\n * so the finding can name the file to edit instead of leaving that as homework. Re-run\n * after editing and the cache regenerates only the beat that changed.\n */\nexport function sceneAt(windows: SceneWindow[], atS: number): string | undefined {\n return windows.find((w) => atS >= w.startS && atS < w.endS)?.file;\n}\n\n/**\n * The frames to look at, spread across the whole runtime.\n *\n * Evenly spaced rather than sampled at a fixed rate: a rate that catches a 4-second\n * defect in a 20s ad misses it in a 60s one, and the frame count is what costs. The\n * last frame is taken explicitly — the closing card is exactly what a spread sample\n * lands just short of, and a dead ending was one of the defects that shipped.\n */\nexport function reviewFrameTimes(durationS: number, frames: number): number[] {\n const n = Math.max(2, frames);\n const last = Math.max(0, durationS - 0.15);\n const step = last / (n - 1);\n return Array.from({ length: n }, (_, i) => Math.round(i * step * 100) / 100);\n}\n\n/** ffmpeg args that pull one frame at `atS`. */\nexport function buildFrameGrabArgs(video: string, atS: number, out: string): string[] {\n return [\n \"-v\",\n \"error\",\n \"-ss\",\n String(atS),\n \"-i\",\n video,\n \"-frames:v\",\n \"1\",\n \"-vf\",\n `scale=${RENDER_REVIEW_WIDTH}:-2`,\n out,\n \"-y\",\n ];\n}\n\nconst FrameReview = z.object({\n /** Something in shot that could not happen, or is being done wrong. */\n impossible: z.array(z.string()).default([]),\n /** Objects present in frame that belong to no part of the scene. */\n strays: z.array(z.string()).default([]),\n});\n\nconst ReelReview = z.object({\n /** Whether the people the script treats as one person stay one person. */\n cast_is_consistent: z.boolean().default(true),\n cast_note: z.string().default(\"\"),\n /** Whether the advertiser's mark is visible anywhere. */\n brand_mark_visible: z.boolean().default(false),\n /** Whether the last frames close the ad rather than trailing off. */\n ends_on_a_close: z.boolean().default(false),\n ending_note: z.string().default(\"\"),\n /** Whether the ad ever cuts away from the presenter to show anything. */\n shows_more_than_a_face: z.boolean().default(true),\n shows_note: z.string().default(\"\"),\n});\n\n/**\n * What is wrong with ONE frame.\n *\n * Asked per frame rather than over the batch, because the batch answers like a\n * summary. Given all twelve at once the model reported the single worst thing it\n * saw; given the rooftop frame alone it reported the missing rails, the hovering\n * bracket and the missing leg. The questions are closed and each names a defect that\n * reached a finished ad — an open \"review this\" comes back as praise.\n */\nexport function buildFrameReviewPrompt(opts: { subject?: string }): string {\n const subject = opts.subject ? ` The advertiser is ${opts.subject}.` : \"\";\n return (\n `This is ONE frame from a video advertisement.${subject}\\n\\n` +\n `Answer only about what is visible in THIS frame. Do not judge whether the ad is good, ` +\n `and do not comment on style, mood or lighting.\\n\\n` +\n `1. IMPOSSIBLE OR WRONG. Is anything shown that could not physically happen, or that ` +\n `shows the job being done incorrectly? Judge the PROCEDURE, not only the physics: ` +\n `equipment resting where it would never rest, a component fitted to nothing, someone ` +\n `handling a part in a way that would damage it, an object at the wrong scale beside a ` +\n `person, a body with a limb missing or duplicated.\\n` +\n `2. STRAYS. Is any object present that belongs to no part of the scene — floating, ` +\n `unattached, or cut off from whatever should hold it?\\n\\n` +\n `Report only what you can actually see. An empty list is the right answer for a clean ` +\n `frame. Keep each item under twelve words.\\n\\n` +\n `Reply with only this JSON: {\"impossible\":[],\"strays\":[]}`\n );\n}\n\n/**\n * What is wrong with the reel as a whole.\n *\n * These three cannot be asked of one frame: whether the customer is the same person\n * throughout, whether the mark appears anywhere at all, and whether the last shot\n * closes the ad. All three shipped broken in the same render.\n */\nexport function buildReelReviewPrompt(opts: { subject?: string; script?: string }): string {\n const subject = opts.subject ? ` The advertiser is ${opts.subject}.` : \"\";\n const script = opts.script ? `\\n\\nThe voiceover says, in order:\\n${opts.script}` : \"\";\n return (\n `These frames are sampled in order from one video advertisement.${subject}` +\n script +\n `\\n\\nAnswer only about what is visible.\\n\\n` +\n `1. CAST. Do the people stay the same people? If the script speaks about one customer ` +\n `and the frames show different faces in that role, say so and count them.\\n` +\n `2. BRAND. Is the advertiser's logo or wordmark visible in ANY frame? Answer yes only if ` +\n `you can actually see a mark — a colour is not a mark.\\n` +\n `3. ENDING. Do the final frames close the advertisement — a call to action, a mark, a ` +\n `card — or does the picture simply carry on and stop?\\n` +\n `4. WHAT IT SHOWS. Does the advertisement ever cut away from the person to show the ` +\n `thing it is selling, or its result? Answer false if every frame is the same person ` +\n `with only the framing changed, and nothing is ever shown.\\n\\n` +\n `Reply with only this JSON:\\n` +\n `{\"cast_is_consistent\":true,\"cast_note\":\"\",\"brand_mark_visible\":false,` +\n `\"ends_on_a_close\":false,\"ending_note\":\"\",\"shows_more_than_a_face\":true,\"shows_note\":\"\"}`\n );\n}\n\n/** The model's reply, as JSON, however it wrapped it. */\nexport function parseReviewJson(raw: string): unknown {\n const fenced = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/);\n const body = (fenced?.[1] ?? raw).trim();\n const start = body.indexOf(\"{\");\n const end = body.lastIndexOf(\"}\");\n if (start === -1 || end === -1 || end < start) return null;\n try {\n return JSON.parse(body.slice(start, end + 1));\n } catch {\n return null;\n }\n}\n\n/** Same complaint, different words — enough to collapse repeats of one defect. */\nconst normalise = (s: string) =>\n s\n .toLowerCase()\n .replace(/[^a-z0-9 ]/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\n/**\n * Per-frame answers, collapsed into one list of findings.\n *\n * A defect that persists across a shot is reported by every frame of it, so the same\n * complaint arrives up to a dozen times. Deduplicated on the wording and stamped with\n * where it was seen, which is also what tells the agent WHICH beat to re-render.\n */\nconst ANATOMY = /\\b(finger|fingers|hand|hands|thumb|limb|leg|legs|arm|arms|foot|feet|anatomy)\\b/i;\n\n/** Each distinct complaint, with every frame time that reported it. */\nfunction collapseRepeats(frames: { at: number; json: unknown }[]): { what: string; at: number[] }[] {\n const seen = new Map<string, { what: string; at: number[] }>();\n for (const frame of frames) {\n const parsed = FrameReview.safeParse(frame.json);\n if (!parsed.success) continue;\n for (const item of [...parsed.data.impossible, ...parsed.data.strays]) {\n const text = item.trim();\n const key = normalise(text);\n if (!key) continue;\n const hit = seen.get(key);\n if (hit) hit.at.push(frame.at);\n else seen.set(key, { what: text, at: [frame.at] });\n }\n }\n return [...seen.values()];\n}\n\nexport function classifyFrameFindings(\n frames: { at: number; json: unknown }[],\n windows: SceneWindow[] = [],\n): RenderFinding[] {\n const seen = collapseRepeats(frames);\n // Mangled hands are the signature failure of every video model, so they arrive\n // once per frame in slightly different words and bury everything else. Rolled up\n // into one finding about the render, which is also the honest reading: anatomy\n // broken in most frames is not a note, it is a reason not to ship the take.\n const anatomy = seen.filter((f) => ANATOMY.test(f.what));\n const rest = seen.filter((f) => !ANATOMY.test(f.what));\n const findings = rest.map((f) => {\n const scene = sceneAt(windows, f.at[0] as number);\n return {\n severity: \"blocking\" as const,\n what: `At ${f.at.map((s) => `${s}s`).join(\", \")}${scene ? ` (${scene})` : \"\"}: ${f.what}`,\n ...(scene ? { scene } : {}),\n };\n });\n if (anatomy.length > 0) {\n const at = [...new Set(anatomy.flatMap((f) => f.at))].sort((a, b) => a - b);\n findings.push({\n severity: \"blocking\",\n what:\n `Malformed hands or limbs in ${at.length} of ${frames.length} sampled frames ` +\n `(${at.map((s) => `${s}s${sceneAt(windows, s) ? ` → ${sceneAt(windows, s)}` : \"\"}`).join(\", \")}): ` +\n `${anatomy.map((f) => f.what).join(\" \")} ` +\n \"Re-render those beats — keep hands out of frame, or away from the object being held.\",\n });\n }\n return findings;\n}\n\n/** The whole-reel answer, as findings. */\n/**\n * The watch pass's question sheet.\n *\n * Deliberately narrow: only what a still cannot answer. The frame passes already cover\n * anatomy, legibility, the mark and the ending, and asking twice costs a second upload of\n * the whole video for an answer already in hand.\n *\n * The accent question asks for EVIDENCE, not just a verdict. Measured against a native\n * speaker's judgement on three ads, the model said \"peninsular\" where the ear heard Latin\n * American — so this under-reports, and a bare boolean would read as an all-clear it has\n * not earned. Quoting what it heard lets a human check the claim instead of trusting it.\n */\nexport function buildWatchReviewPrompt(opts: { market?: string; script?: string }): string {\n const market = opts.market?.trim();\n return (\n \"Watch and LISTEN to this advertisement, then answer about the things that only exist \" +\n \"in motion and in sound.\\n\" +\n `1. SPOKEN VARIETY. ${market ? `The ad is set in ${market}. ` : \"\"}Does the speaker's accent belong there? ` +\n \"Quote the words you heard that show it — a phonetic marker beats an impression.\\n\" +\n \"2. VOICE AND MOUTH. Does the voice belong to the mouth on screen, in timing and in person?\\n\" +\n \"3. CUTS. Do the shot changes hold, or does something jump, repeat or stutter between them?\\n\" +\n \"4. DEFECTS. List anything else wrong, each with the exact timestamp where it happens — a \" +\n \"moment maps to the shot that produced it, and a defect without one is a complaint rather \" +\n \"than an instruction.\\n\" +\n 'Answer JSON only: {\"spoken_variety_matches\":true|false,\"variety_note\":\"...\",' +\n '\"voice_matches_mouth\":true|false,\"mouth_note\":\"...\",' +\n '\"cuts_are_clean\":true|false,\"cuts_note\":\"...\",' +\n '\"defects\":[{\"at\":\"MM:SS.s\",\"kind\":\"cut|lipsync|audio|visual\",\"what\":\"...\"}]}'\n );\n}\n\n/**\n * What the WATCH pass answers — the questions a still cannot be asked.\n *\n * The frame passes are twelve stills, so everything between them is invisible and the\n * audio is not there at all. Three ads shipped in Latin American Spanish for a market of\n * Spain and the review said nothing, because an accent is not visible in a photograph; the\n * user heard it before the system did. Same for a voice that drifts off the lips, and for a\n * cut that only exists between two sampled moments.\n */\nconst WatchReview = z.object({\n spoken_variety_matches: z.boolean(),\n variety_note: z.string().default(\"\"),\n voice_matches_mouth: z.boolean(),\n mouth_note: z.string().default(\"\"),\n cuts_are_clean: z.boolean(),\n cuts_note: z.string().default(\"\"),\n});\n\n/**\n * The timed defects the watch pass reports, each one placed on the timeline.\n *\n * Asked for with a timestamp because a moment maps to a shot: \"abrupt jump cut at 00:06.8\"\n * becomes \"edit `scenes/s02.json`\", which is the difference between a complaint and an\n * instruction. Measured on a real ad the model returned five cuts and an English audio\n * fragment in a Spanish read, each with its second — none of which twelve stills could have\n * carried, and one of which nobody had noticed at all.\n *\n * No agentic processing involved: the timestamps come from asking for them.\n */\nconst WatchDefects = z.object({\n defects: z\n .array(\n z.object({\n at: z.string().default(\"\"),\n kind: z.string().default(\"\"),\n what: z.string().default(\"\"),\n }),\n )\n .default([]),\n});\n\n/** `MM:SS.s` or plain seconds → seconds, or null when it is neither. */\nexport function parseMoment(at: string): number | null {\n const trimmed = at.trim();\n const clock = /^(\\d+):(\\d+(?:\\.\\d+)?)$/.exec(trimmed);\n if (clock) return Number(clock[1]) * 60 + Number(clock[2]);\n const plain = Number(trimmed.replace(/s$/i, \"\"));\n return Number.isFinite(plain) ? plain : null;\n}\n\nexport function classifyWatchDefects(json: unknown, windows: SceneWindow[]): RenderFinding[] {\n const parsed = WatchDefects.safeParse(json);\n if (!parsed.success) return [];\n return parsed.data.defects\n .filter((d) => d.what.trim())\n .map((d) => {\n const at = parseMoment(d.at);\n const scene = at === null ? undefined : sceneAt(windows, at);\n const when = d.at.trim() ? ` at ${d.at.trim()}` : \"\";\n return {\n severity: \"warning\" as const,\n what: `${d.what.trim()}${when}${d.kind.trim() ? ` (${d.kind.trim()})` : \"\"}.`,\n ...(scene ? { scene } : {}),\n };\n });\n}\n\n/**\n * Whether the finished ad is the one that was ASKED for.\n *\n * Every other pass here grades the render against the spec, and that cannot answer this\n * question: both sides of that comparison came from the same reading of the request, so an\n * ad that executes a misread brief perfectly comes back clean. Four did, in a row, while\n * the person who asked caught a Latin American voice, a mouth dubbed over, a stand-in logo\n * and a customer repairing the panel she was there to praise.\n *\n * So this pass is given the request in the words it was made in, and nothing else about how\n * it was interpreted. Blocking, because an ad that is not what was asked for is not a\n * quality problem — it is the wrong ad, however well it is made.\n */\nconst FidelityReview = z.object({\n delivers_the_ask: z.boolean(),\n missing: z.array(z.string()).default([]),\n contradicted: z.array(z.string()).default([]),\n});\n\n/** The request, in the words it was made in, against what the video actually is. */\nexport function buildFidelityPrompt(brief: string): string {\n return (\n \"Someone asked for an advertisement in these words:\\n\\n\" +\n `\"\"\"${brief.trim()}\"\"\"\\n\\n` +\n \"Watch and LISTEN to the video, then answer only this: does it deliver what they asked \" +\n \"for? Judge the request as it was written — not what would make a good ad, and not what \" +\n \"you would have made. Two lists: what they asked for that is NOT in the video, and what \" +\n \"the video does that CONTRADICTS what they asked for. Quote their own words in each \" +\n \"entry so the answer can be checked. Empty lists when it delivers.\\n\" +\n 'Answer JSON only: {\"delivers_the_ask\":true|false,\"missing\":[\"...\"],\"contradicted\":[\"...\"]}'\n );\n}\n\nexport function classifyFidelityFindings(json: unknown): RenderFinding[] {\n const parsed = FidelityReview.safeParse(json);\n if (!parsed.success) return [];\n const r = parsed.data;\n if (r.delivers_the_ask) return [];\n return [...r.missing, ...r.contradicted]\n .map((entry) => entry.trim())\n .filter(Boolean)\n .map((what) => ({ severity: \"blocking\" as const, what: `Not what was asked for: ${what}.` }));\n}\n\n/** Turn the watch pass's answer into findings. Unreadable answers report nothing. */\nexport function classifyWatchFindings(json: unknown): RenderFinding[] {\n const parsed = WatchReview.safeParse(json);\n if (!parsed.success) return [];\n const r = parsed.data;\n const findings: RenderFinding[] = [];\n if (!r.spoken_variety_matches) {\n findings.push({\n severity: \"blocking\",\n what:\n `The voice speaks the wrong variety of the language${r.variety_note.trim() ? `: ${r.variety_note.trim()}` : \"\"}. ` +\n \"A pinned voice cannot fix this — the re-voicing is speech-to-speech and carries the accent \" +\n \"across, so state the market on the ad and let the clip prompt name the accent.\",\n });\n }\n if (!r.voice_matches_mouth) {\n findings.push({\n severity: \"blocking\",\n what:\n `The voice does not match the mouth${r.mouth_note.trim() ? `: ${r.mouth_note.trim()}` : \"\"}. ` +\n \"A person visibly speaking under a track that is not theirs reads as bad dubbing.\",\n });\n }\n if (!r.cuts_are_clean) {\n findings.push({\n severity: \"warning\",\n what: `The cuts do not hold${r.cuts_note.trim() ? `: ${r.cuts_note.trim()}` : \"\"}.`,\n });\n }\n return findings;\n}\n\nexport function classifyReelFindings(json: unknown): RenderFinding[] {\n const parsed = ReelReview.safeParse(json);\n if (!parsed.success) return [];\n const r = parsed.data;\n const findings: RenderFinding[] = [];\n if (!r.cast_is_consistent) {\n findings.push({\n severity: \"blocking\",\n what:\n `The cast changes between shots${r.cast_note.trim() ? `: ${r.cast_note.trim()}` : \"\"}. ` +\n \"One customer played by several faces reads as stock footage. Ground the frames on one \" +\n \"identity (`baker avatars`) or write the beats so the subject is never the same person twice.\",\n });\n }\n if (!r.brand_mark_visible) {\n findings.push({\n severity: \"warning\",\n what:\n \"No brand mark is visible in any frame. THIS IS NOT A RE-RENDER: the mark is drawn by the \" +\n \"caption composition as an ordinary `<img>`, never generated as a picture — so fix the mark \" +\n \"itself (a bad path, or an SVG whose geometry clips its own wordmark) or the `.brand-mark` / \" +\n \"`.endcard-logo` rules in `<composition>/index.html`, then re-run. Every clip comes back from \" +\n \"the cache and only the composition pass renders again, so it costs seconds and no generation.\",\n });\n }\n if (!r.shows_more_than_a_face) {\n findings.push({\n severity: \"blocking\",\n what:\n `The ad never shows what it is selling${r.shows_note.trim() ? `: ${r.shows_note.trim()}` : \"\"} — every frame is ` +\n \"the same person with only the framing changed. Give the middle beats their own picture (the bill, the roof, \" +\n \"the install, the house) and set `cast: false` on them.\",\n });\n }\n if (!r.ends_on_a_close) {\n findings.push({\n severity: \"warning\",\n what:\n `The video does not close${r.ending_note.trim() ? `: ${r.ending_note.trim()}` : \"\"} — it runs on ` +\n \"and stops. Give the last beat a call to action, and keep its shot short enough that the \" +\n \"picture ends with the read.\",\n });\n }\n return findings;\n}\n\n/**\n * The local file to review, out of whatever the run returned.\n *\n * A canvas ends in one output or several, and only a video is worth watching —\n * a static ad is the image gate's business, not this one. The LAST video wins:\n * the caption pass and the music mix each emit one, and the finished ad is the\n * one at the end of that chain.\n */\nexport function videoPathFromOutput(output: unknown): string | null {\n const refs = Array.isArray(output) ? output : [output];\n let found: string | null = null;\n for (const ref of refs) {\n const r = ref as { kind?: unknown; path?: unknown; mime?: unknown } | null;\n if (!r || typeof r !== \"object\") continue;\n const isVideo = r.kind === \"video\" || (typeof r.mime === \"string\" && r.mime.startsWith(\"video/\"));\n if (isVideo && typeof r.path === \"string\" && r.path) found = r.path;\n }\n return found;\n}\n\n/**\n * What the finished audio measures, as opposed to what it sounds like.\n *\n * A model looking at frames cannot hear, and asking one to judge a mix would be\n * guessing at something ffmpeg answers exactly. These are the failures that are\n * arithmetic: an ad with no audio at all, a mix so quiet nobody hears the bed, and one\n * pushed into the ceiling so the voice distorts.\n *\n * Measured on real renders: a music bed sitting at -41 dB was completely inaudible\n * under a continuous voiceover and read as \"no music\"; a healthy ad of the same length\n * measured -19.3 dB mean and -1.5 dB peak.\n */\n/** One `volumedetect` reading, in dB. `-inf` — real digital silence — survives as -Infinity. */\nfunction parseVolume(volumedetect: string, key: \"mean_volume\" | \"max_volume\"): number {\n const raw = new RegExp(`${key}:\\\\s*(-?(?:inf|[\\\\d.]+)) dB`).exec(volumedetect)?.[1];\n if (raw === undefined) return Number.NaN;\n if (raw === \"-inf\") return Number.NEGATIVE_INFINITY;\n return Number.parseFloat(raw);\n}\n\nexport function classifyAudio(volumedetect: string, hasAudioStream: boolean): RenderFinding[] {\n if (!hasAudioStream) {\n return [\n {\n severity: \"blocking\",\n what: \"The finished video has no audio track at all — no voice, no music, nothing. An ad that plays silent in a feed is not an ad.\",\n },\n ];\n }\n // `-inf` is a value ffmpeg really returns — it is what a stream of pure zeroes measures.\n // Read with a digits-only pattern both numbers came back NaN, every threshold below was\n // skipped, and a completely silent ad passed the audio review with nothing to report.\n const mean = parseVolume(volumedetect, \"mean_volume\");\n const max = parseVolume(volumedetect, \"max_volume\");\n const findings: RenderFinding[] = [];\n if (mean === Number.NEGATIVE_INFINITY || max === Number.NEGATIVE_INFINITY) {\n return [\n {\n severity: \"blocking\",\n what: \"The finished video has an audio track, but it is completely silent — digital zero from start to end. An ad that plays silent in a feed is not an ad.\",\n },\n ];\n }\n if (Number.isFinite(mean) && mean < -30) {\n findings.push({\n severity: \"warning\",\n what:\n `The mix is very quiet (${mean} dB average). On a phone at half volume this is close to silence — ` +\n \"check the music bed is not ducked into nothing under the voice.\",\n });\n }\n if (Number.isFinite(max) && max > -0.5) {\n findings.push({\n severity: \"warning\",\n what:\n `The mix peaks at ${max} dB, which is at or over the ceiling — the voice will distort on a phone speaker. ` +\n \"Bring the loudest track down a few dB.\",\n });\n }\n return findings;\n}\n\n/** What the agent is told when the reviewer could not run. */\nexport const RENDER_REVIEW_UNAVAILABLE =\n \"The render was NOT reviewed — no model gateway key in this environment. Watch it yourself before sending it on.\";\n\n/** Concurrent vision calls. Thirteen at once loses answers to the rate limit. */\nconst REVIEW_CONCURRENCY = 3;\n\n/** `Promise.all` with a ceiling, preserving order. */\nasync function mapWithLimit<T, R>(items: T[], limit: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const out = new Array<R>(items.length);\n let next = 0;\n await Promise.all(\n Array.from({ length: Math.min(limit, items.length) }, async () => {\n for (let i = next++; i < items.length; i = next++) {\n out[i] = await fn(items[i] as T);\n }\n }),\n );\n return out;\n}\n\nconst execFileAsync = promisify(execFile);\n\n/** What ffmpeg measures about the finished mix. Never throws: an unmeasurable mix is\n * reported as nothing rather than as a fault. */\nasync function measureAudio(video: string): Promise<RenderFinding[]> {\n const streams = await execFileAsync(\n \"ffprobe\",\n [\"-v\", \"error\", \"-select_streams\", \"a\", \"-show_entries\", \"stream=codec_type\", \"-of\", \"csv=p=0\", video],\n { timeout: 20_000 },\n ).catch(() => null);\n if (!streams) return [];\n const hasAudio = streams.stdout.trim().length > 0;\n if (!hasAudio) return classifyAudio(\"\", false);\n const vol = await execFileAsync(\"ffmpeg\", [\"-i\", video, \"-map\", \"0:a:0\", \"-af\", \"volumedetect\", \"-f\", \"null\", \"-\"], {\n timeout: 120_000,\n maxBuffer: 8 * 1024 * 1024,\n }).catch((e: { stderr?: string }) => ({ stdout: \"\", stderr: e.stderr ?? \"\" }));\n // volumedetect reports on stderr, and ffmpeg exits non-zero on the null muxer.\n return classifyAudio(vol.stderr ?? \"\", true);\n}\n\n/** Seconds of picture, or null when the file cannot be probed. */\nasync function videoDuration(video: string): Promise<number | null> {\n try {\n const { stdout } = await execFileAsync(\n \"ffprobe\",\n [\"-v\", \"error\", \"-show_entries\", \"format=duration\", \"-of\", \"default=nw=1:nk=1\", video],\n { timeout: 20_000 },\n );\n const seconds = Number.parseFloat(stdout.trim());\n return Number.isFinite(seconds) && seconds > 0 ? seconds : null;\n } catch {\n return null;\n }\n}\n\n/** One OpenRouter completion, or null when the gateway could not answer. */\ntype ReviewContentPart =\n | { type: \"text\"; text: string }\n | { type: \"image_url\"; image_url: { url: string } }\n | { type: \"video_url\"; video_url: { url: string } };\n\n/**\n * One call to the gateway, which is the only place this module talks to a model.\n *\n * `usage: { include: true }` is what makes the reply carry `usage.cost`, and carrying it\n * is why there is no price list on either side of this. Both passes come through here so\n * they cannot drift into two different ideas of what a call costs.\n *\n * Returns null on anything that is not a parseable answer — no key, a non-200, a reply\n * that is not JSON. The caller reports that as \"nobody looked\", never as \"clean\".\n */\n/** How much the model reasons before answering: `minimal` for a transcription, the model's own default for a judgment. */\nexport type GatewayReasoning = { effort: \"minimal\" | \"low\" | \"medium\" | \"high\" };\n\nasync function askGateway(\n model: string,\n parts: ReviewContentPart[],\n timeoutMs: number,\n spent: UsageSink,\n reasoning?: GatewayReasoning,\n): Promise<unknown> {\n const key = openRouterKey();\n if (!key) return null;\n try {\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", authorization: `Bearer ${key}` },\n body: JSON.stringify({\n model,\n messages: [{ role: \"user\", content: parts }],\n usage: { include: true },\n ...(reasoning ? { reasoning } : {}),\n }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) return null;\n const body = (await response.json()) as { choices?: { message?: { content?: string } }[] };\n const usage = usageFrom(model, body);\n if (usage) spent(usage);\n const text = body.choices?.[0]?.message?.content;\n return text ? parseReviewJson(text) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The same pass, given the VIDEO instead of stills.\n *\n * Inline rather than an upload-and-poll: an ad is seconds long, so a data URL costs one\n * round trip where the Files API costs three. Skipped above the cap rather than truncated\n * — a review of the first half reported as a review is the failure this module exists to\n * stop. `WATCH_MAX_BYTES` is ours, not the gateway's: OpenRouter documents no hard inline\n * limit, only \"compress when possible\", and a number we picked is easier to reason about\n * than one nobody states. Probed at the cap rather than assumed — a 51.6MB file is a\n * 68.8MB request body and came back 200 in 45s, so the timeout below is the binding\n * constraint long before the size is.\n */\nasync function askVideo(video: string, prompt: string, spent: UsageSink): Promise<unknown> {\n let bytes: Buffer;\n try {\n bytes = await readFile(video);\n } catch {\n return null;\n }\n if (bytes.byteLength > WATCH_MAX_BYTES) return null;\n return await askGateway(\n RENDER_WATCH_MODEL,\n [\n { type: \"text\", text: prompt },\n { type: \"video_url\", video_url: { url: `data:video/mp4;base64,${bytes.toString(\"base64\")}` } },\n ],\n 600_000,\n spent,\n );\n}\n\nasync function ask(images: string[], prompt: string, spent: UsageSink): Promise<unknown> {\n return await askGateway(\n RENDER_REVIEW_MODEL,\n [\n { type: \"text\", text: prompt },\n ...images.map(\n (b64): ReviewContentPart => ({ type: \"image_url\", image_url: { url: `data:image/jpeg;base64,${b64}` } }),\n ),\n ],\n 180_000,\n spent,\n );\n}\n\n/**\n * One question about finished STILL images (base64 JPEG), through the same gateway and\n * accounting as the video review. Null when no model could answer, which the caller\n * reports as \"nobody looked\", never as \"clean\".\n */\nexport async function askAboutStills(\n images: string[],\n prompt: string,\n onSpend?: (usage: ReviewUsage) => void,\n): Promise<unknown> {\n if (!hasOpenRouterKey()) return null;\n return await ask(images, prompt, (usage) => onSpend?.(usage));\n}\n\n/**\n * One question about STILL images (base64 JPEG) to `model`, text first, through the same\n * gateway and accounting. Null when no model could answer.\n */\nexport async function askAboutImages(\n model: string,\n images: string[],\n prompt: string,\n onSpend?: (usage: ReviewUsage) => void,\n /** How long the answer may take: a long one (a spec written back) needs more than a verdict. */\n timeoutMs = 60_000,\n reasoning?: GatewayReasoning,\n): Promise<unknown> {\n if (!hasOpenRouterKey()) return null;\n return await askGateway(\n model,\n [\n { type: \"text\", text: prompt },\n ...images.map(\n (b64): ReviewContentPart => ({ type: \"image_url\", image_url: { url: `data:image/jpeg;base64,${b64}` } }),\n ),\n ],\n timeoutMs,\n (usage) => onSpend?.(usage),\n reasoning,\n );\n}\n\n/**\n * One text-only question to `model`, through the same gateway and accounting. Null when no\n * model could answer, which the caller reports as \"nobody looked\", never as a pass.\n */\nexport async function askAboutText(\n model: string,\n prompt: string,\n onSpend?: (usage: ReviewUsage) => void,\n): Promise<unknown> {\n if (!hasOpenRouterKey()) return null;\n return await askGateway(model, [{ type: \"text\", text: prompt }], 60_000, (usage) => onSpend?.(usage));\n}\n\n/**\n * Look at the finished video and report what is wrong with it.\n *\n * Returns null when the review could not run at all — no key, no ffmpeg, no readable\n * video — which the caller reports as \"nobody looked\", never as \"clean\". The\n * difference is the whole point: an unwatched render was reported as good.\n */\nexport async function reviewRenderedVideo(opts: {\n video: string;\n subject?: string;\n script?: string;\n /** Where the ad is set — the watch pass judges the accent against it. */\n market?: string;\n /**\n * The request, in the words it was made in. Not the spec, and not a summary: the point of\n * this pass is to compare the video against something that did NOT come from the same\n * reading of the ask, and a summary written by the reader is that reading again.\n */\n brief?: string;\n /** Scene timings, so a finding can name the file that produced the shot. */\n windows?: SceneWindow[];\n /**\n * Called once per model call that returned a token count, so the caller can carry the\n * spend home. A review that could not run reports nothing, because it spent nothing.\n */\n onSpend?: (usage: ReviewUsage) => void;\n}): Promise<RenderFinding[] | null> {\n if (!hasOpenRouterKey()) return null;\n const duration = await videoDuration(opts.video);\n if (duration === null) return null;\n const spent: UsageSink = (usage) => opts.onSpend?.(usage);\n\n const dir = await mkdtemp(path.join(tmpdir(), \"baker-review-\"));\n try {\n const grabbed: { at: number; image: string }[] = [];\n for (const [i, at] of reviewFrameTimes(duration, RENDER_REVIEW_FRAMES).entries()) {\n const out = path.join(dir, `f${String(i).padStart(2, \"0\")}.jpg`);\n try {\n await execFileAsync(\"ffmpeg\", buildFrameGrabArgs(opts.video, at, out), { timeout: 30_000 });\n grabbed.push({ at, image: await readFile(out, { encoding: \"base64\" }) });\n } catch {\n // A frame that will not grab is not a reason to abandon the review — the rest\n // of the sample still answers every question being asked.\n }\n }\n if (grabbed.length === 0) return null;\n\n // The whole-reel question first and alone. Fired alongside twelve per-frame\n // calls it lost to the rate limit every time, and `ask` returns null on a\n // failure — so the run that found fourteen frame defects silently reported\n // nothing about the cast, the missing mark or the dead ending.\n const reel = await ask(\n grabbed.map((f) => f.image),\n buildReelReviewPrompt({ subject: opts.subject, script: opts.script }),\n spent,\n );\n const framePrompt = buildFrameReviewPrompt({ subject: opts.subject });\n const perFrame = await mapWithLimit(grabbed, REVIEW_CONCURRENCY, async (f) => ({\n at: f.at,\n json: await ask([f.image], framePrompt, spent),\n }));\n\n // Every answer coming back null means the model was unreachable, not that the\n // render is clean — the caller has to be able to tell those apart.\n const unread = perFrame.filter((f) => f.json === null).length;\n if (unread === perFrame.length && reel === null) return null;\n // The pass that WATCHES, run after the stills rather than beside them: it uploads the\n // whole video, and firing it alongside twelve frame calls is what lost the reel answer\n // to the rate limit before. Its findings are the ones a photograph cannot carry — the\n // accent, the voice against the mouth, the cuts.\n const watch = await askVideo(\n opts.video,\n buildWatchReviewPrompt({ market: opts.market, script: opts.script }),\n spent,\n );\n // Asked separately from the defect passes, because they are different judgements: one\n // is \"is this well made\", this one is \"is this the ad they asked for\". A well-made ad\n // of the wrong brief passes every other check here.\n const fidelity = opts.brief?.trim() ? await askVideo(opts.video, buildFidelityPrompt(opts.brief), spent) : null;\n const findings = [\n ...classifyFrameFindings(perFrame, opts.windows ?? []),\n ...classifyReelFindings(reel),\n ...classifyWatchFindings(watch),\n ...classifyWatchDefects(watch, opts.windows ?? []),\n ...classifyFidelityFindings(fidelity),\n // Measured, not judged: a model looking at frames cannot hear the mix.\n ...(await measureAudio(opts.video)),\n ];\n findings.push(\n ...unreviewedNotes({\n watched: watch !== null,\n fidelityAsked: Boolean(opts.brief?.trim()),\n fidelityRead: fidelity !== null,\n unread,\n frames: perFrame.length,\n }),\n );\n return findings;\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n}\n\n/**\n * Each scene's slot on the timeline, read from the blueprint beside a canvas.\n *\n * Returns an empty list when the canvas has no split blueprint — a hand-built graph, a\n * static ad — and the findings then carry timestamps only, which is what they did\n * before. Never throws: a review that cannot name the file is still worth having.\n */\n/**\n * What the review has to be told about the ad, read from the blueprint beside the canvas.\n *\n * Every question the two whole-reel passes ask is relative to something: the accent is\n * judged against a MARKET, the cast against a SCRIPT, the frames against an advertiser.\n * Handed none of them, `buildWatchReviewPrompt` still asked \"does the speaker's accent\n * belong there?\" with no antecedent for \"there\" — and `classifyWatchFindings` still turned\n * the answer into a BLOCKING finding. A verdict against nothing is worse than no verdict,\n * and the accent is the exact failure this pass was added for.\n *\n * Read rather than passed down because the caller is `canvas run`, which is handed a graph\n * and no brief. `prompt.json` sits in the same directory `sceneWindowsBeside` already\n * reads, and is the one artefact that knows all three.\n *\n * Returns an empty context for a hand-built canvas — the prompts then drop the clauses\n * that have no antecedent, which is what they did before.\n */\nexport async function reviewContextBeside(\n canvasPath: string,\n): Promise<{ market?: string; subject?: string; script?: string }> {\n const raw = await readFile(path.join(path.dirname(canvasPath), \"prompt.json\"), \"utf-8\").catch(() => null);\n if (!raw) return {};\n try {\n const blueprint = JSON.parse(raw) as {\n global?: { market?: unknown; transcript?: unknown; branding?: { name?: unknown } };\n };\n const global = blueprint.global ?? {};\n const text = (value: unknown): string | undefined =>\n typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n return {\n ...(text(global.market) ? { market: text(global.market) } : {}),\n ...(text(global.branding?.name) ? { subject: text(global.branding?.name) } : {}),\n ...(text(global.transcript) ? { script: text(global.transcript) } : {}),\n };\n } catch {\n // A blueprint that will not parse is the run's problem, not the review's.\n return {};\n }\n}\n\nexport async function sceneWindowsBeside(canvasPath: string): Promise<SceneWindow[]> {\n const dir = path.join(path.dirname(canvasPath), \"scenes\");\n const files = await readdir(dir).catch(() => null);\n if (!files) return [];\n const windows: SceneWindow[] = [];\n for (const name of files.filter((f) => f.endsWith(\".json\")).sort()) {\n const raw = await readFile(path.join(dir, name), \"utf-8\").catch(() => null);\n if (!raw) continue;\n try {\n const scene = JSON.parse(raw) as { start_s?: unknown; end_s?: unknown };\n if (typeof scene.start_s === \"number\" && typeof scene.end_s === \"number\") {\n windows.push({ file: `scenes/${name}`, startS: scene.start_s, endS: scene.end_s });\n }\n } catch {\n // A scene file that will not parse is the run's problem, not the review's.\n }\n }\n return windows;\n}\n\n/**\n * What nobody looked at, said out loud.\n *\n * A partial review reported as a complete one is the failure this whole module exists to\n * stop. `askVideo` returns null on a timeout, an HTTP failure, a reply it could not parse\n * — and deterministically for a finished video over the inline cap; every one of those\n * contributed zero findings, which reads as \"the audio and the cuts are fine\". They are\n * unchecked, not fine, and they are the findings a photograph cannot carry.\n */\nfunction unreviewedNotes(state: {\n watched: boolean;\n fidelityAsked: boolean;\n fidelityRead: boolean;\n unread: number;\n frames: number;\n}): RenderFinding[] {\n const notes: RenderFinding[] = [];\n if (!state.watched) {\n notes.push({\n severity: \"warning\",\n what: \"Nobody watched the finished video end to end — the accent, the voice against the mouth and the cuts are unchecked. The frame-by-frame review still ran.\",\n });\n }\n if (state.fidelityAsked && !state.fidelityRead) {\n notes.push({\n severity: \"warning\",\n what: \"The video was not checked against what you asked for — only against whether it is well made. A well-made ad of the wrong brief passes every other check here.\",\n });\n }\n if (state.unread > 0) {\n notes.push({\n severity: \"warning\",\n what: `${state.unread} of ${state.frames} frames could not be reviewed — that part of the video is unchecked.`,\n });\n }\n return notes;\n}\n","/**\n * Where every word and the logo sit in a finished static ad, read by a vision model that\n * answers with boxes.\n *\n * Bench E10 (`.context/static-review/BENCH.md`) compared the Gemini box reader with\n * tesseract and PaddleOCR on renders and on synthetic images with the copy at known places:\n * Gemini read word F1 0.994 / 1.000 with box IoU 0.937 in 4.3 s (about $0.005 an image);\n * Paddle reached F1 0.83 and tesseract 0.49, so no free reader passed the 0.95 gate.\n *\n * One box shape leaves this module, whatever the reader: the text as printed and its\n * rectangle as fractions of the frame, origin top-left. The copy check and the band check\n * never learn how a box was read.\n */\nimport { TEXT_BOX_READER_MODEL } from \"@baker/canvas-contract\";\nimport sharp from \"sharp\";\nimport { z } from \"zod\";\nimport { askAboutImages, type ReviewUsage } from \"./render-review.ts\";\n\nexport type TextBox = { text: string; kind: \"text\" | \"logo\"; x0: number; y0: number; x1: number; y1: number };\n\n/** Gemini's `box_2d` is [ymin, xmin, ymax, xmax] on a 0-1000 grid over the whole image. */\nconst GRID = 1000;\n\nconst ReaderItem = z.object({\n text: z.string(),\n kind: z.enum([\"text\", \"logo\"]),\n box_2d: z.array(z.number()).length(4),\n});\nconst ReaderAnswer = z.object({ items: z.array(ReaderItem) });\n\nconst fraction = (value: number) => Math.min(1, Math.max(0, value / GRID));\n\n/**\n * The reader's answer as boxes; null when the answer is not the shape asked for, which the\n * caller reports as \"not checked\", never as \"no text\". Pure.\n */\nexport function textBoxesFromReader(answer: unknown): TextBox[] | null {\n const parsed = ReaderAnswer.safeParse(answer);\n if (!parsed.success) return null;\n return parsed.data.items.map(({ text, kind, box_2d }) => {\n const [ymin = 0, xmin = 0, ymax = 0, xmax = 0] = box_2d;\n return {\n text,\n kind,\n x0: fraction(Math.min(xmin, xmax)),\n y0: fraction(Math.min(ymin, ymax)),\n x1: fraction(Math.max(xmin, xmax)),\n y1: fraction(Math.max(ymin, ymax)),\n };\n });\n}\n\n/**\n * The reader's instruction: the one bench E10 measured (`scripts/creative-evals/ocr.mjs`),\n * with the JSON shape spelled out because the gateway call asks for no schema. Misspellings\n * are kept as printed: a reader that corrects them would pass the very mistakes it is for.\n * Columns are read apart (audited Sept 2026: a two-column icon row read row by row made every\n * line read the other column's words); `readingOrder` regroups whatever order comes back.\n */\nexport const TEXT_BOX_PROMPT = [\n \"Find every line of visible text in this image and every logo mark.\",\n \"For each, return: `text` exactly as rendered, character for character (keep misspellings, doubled or missing letters and garbled glyphs as they appear; never correct or complete them; for a logo, its wordmark text or an empty string); `kind` text or logo; `box_2d` the tight box around it as [ymin, xmin, ymax, xmax] on a 0-1000 grid over the whole image.\",\n \"One entry per line of each column: a headline wrapped over two lines is two entries, and where text sits in columns, cards or bubbles side by side, each one's lines are their own entries. Never join text from two columns into one entry. List the text block by block in reading order, each block's lines top to bottom. Pseudo-text that is not readable as letters is left out. No text: an empty list.\",\n 'Reply with only this JSON: {\"items\":[{\"text\":\"...\",\"kind\":\"text|logo\",\"box_2d\":[ymin,xmin,ymax,xmax]}]}',\n].join(\"\\n\");\n\n/** Longest edge the reader is sent: the size E10 was measured at. Boxes are fractions, so any size reads the same frame. */\nconst READ_EDGE = 1536;\n\nexport type TextBoxReading = { status: \"read\"; boxes: TextBox[] } | { status: \"not_checked\" };\n\n/**\n * Every line of text and every logo mark in `image`, with its box. `not_checked` when no\n * reader could answer (no gateway key, an outage, an answer out of shape, twice): the\n * checks built on it then say they did not run, and the review falls back to the model\n * that looks at the ad. It never reads as \"no text\".\n */\nexport async function readTextBoxes(\n image: Buffer,\n opts: { onSpend?: (usage: ReviewUsage) => void } = {},\n): Promise<TextBoxReading> {\n let jpeg: string;\n try {\n const resized = await sharp(image)\n .resize(READ_EDGE, READ_EDGE, { fit: \"inside\", withoutEnlargement: true })\n .jpeg({ quality: 90 })\n .toBuffer();\n jpeg = resized.toString(\"base64\");\n } catch {\n return { status: \"not_checked\" };\n }\n for (let attempt = 0; attempt < 2; attempt++) {\n // A transcription, not a judgment: left to think, a read took up to 125 s and pieced together a\n // line the frame cuts (dev, Oct 2026); minimal reasoning read the same lines in 13-17 s.\n const answer = await askAboutImages(TEXT_BOX_READER_MODEL, [jpeg], TEXT_BOX_PROMPT, opts.onSpend, 60_000, {\n effort: \"minimal\",\n });\n const boxes = textBoxesFromReader(answer);\n if (boxes) return { status: \"read\", boxes };\n }\n return { status: \"not_checked\" };\n}\n","import { z } from \"zod\";\nimport type { ExecCtx } from \"../../engine/context.ts\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { callBackendExec } from \"../remote/delegate.ts\";\nimport { imageGenerateNode } from \"../remote/image.ts\";\nimport { MIN_EXTEND_SCALE, mergeExtended, placeForExtend } from \"./lib/extend-fit.ts\";\nimport type { ReviewUsage } from \"./lib/render-review.ts\";\nimport { boxesToPlace, planSafeFit } from \"./lib/safe-fit.ts\";\nimport { bytesOf } from \"./lib/static-review.ts\";\nimport { readTextBoxes } from \"./lib/text-boxes.ts\";\n\nconst Usage = z\n .object({ model: z.string(), inputTokens: z.number(), outputTokens: z.number(), costUsd: z.number() })\n .strict();\nconst Fit = z.object({ scale: z.number(), x: z.number(), y: z.number() }).strict();\n/** The image model's render shape, which the grey canvas it paints is made in (\"9:16\", \"3:4\"). */\nconst RenderRatio = z.string().regex(/^\\d+(?:\\.\\d+)?:\\d+(?:\\.\\d+)?$/);\n/** The ad's copy (prompt.json's lines and their roles): the fit moves the ad for these and its logo only. */\nconst Copy = z.array(z.object({ text: z.string().min(1), role: z.string().optional() }).strict());\n\nexport const staticPlaceNode = defineNode({\n id: \"static_place\",\n version: \"1.0.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Place a finished static ad's words and logo where no interface covers them and nothing runs off the frame, for an image model to paint the margins: reads where every line and the logo sit, shrinks the ad (to 60% at most) and sets it inside the size's safe area. `placed` keeps the margins clear (for `static_extend_merge`); `shown` is the same inside the model's render shape with grey margins, to paint. `fit` is null, and both are the ad unchanged, when nothing sits outside.\",\n when_to_use:\n \"Added by a static ad's autofix round (place-and-extend) before an `image_generate` that paints the margins and a `static_extend_merge`; never scaffolded. One box-reader call (about $0.005).\",\n inputs: z.object({ image: ImageRef }).strict(),\n params: z\n .object({\n format: z.string().min(1),\n placements: z.array(z.string().min(1)).optional(),\n render_ratio: RenderRatio,\n copy: Copy.optional(),\n })\n .strict(),\n outputs: z\n .object({\n placed: ImageRef,\n shown: ImageRef,\n fit: Fit.nullable(),\n reason: z.enum([\"not_read\", \"inside_band\", \"too_far\"]).optional(),\n usage: z.array(Usage).optional(),\n })\n .strict(),\n outputKinds: { placed: \"image\", shown: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 5 }),\n async execute({ inputs, params, ctx }) {\n const unchanged = { placed: inputs.image, shown: inputs.image, fit: null };\n const source = inputs.image.path ?? inputs.image.url;\n if (!source) return { ...unchanged, reason: \"not_read\" as const };\n const bytes = await bytesOf(source);\n const usage: ReviewUsage[] = [];\n const reading = await readTextBoxes(bytes, { onSpend: (u) => usage.push(u) });\n const spend = usage.length > 0 ? { usage } : {};\n if (reading.status !== \"read\") return { ...unchanged, reason: \"not_read\" as const, ...spend };\n const boxes = boxesToPlace(reading.boxes, params.format, {\n ...(params.placements ? { placements: params.placements } : {}),\n ...(params.copy ? { copy: params.copy } : {}),\n });\n const fit = planSafeFit(boxes, params.format, {\n ...(params.placements ? { placements: params.placements } : {}),\n minScale: MIN_EXTEND_SCALE,\n contain: true,\n });\n if (!fit) {\n // Inside the band already, or too far past it for a shrink to clear: the size is laid out again instead.\n const loose = planSafeFit(boxes, params.format, {\n ...(params.placements ? { placements: params.placements } : {}),\n minScale: 0,\n });\n return { ...unchanged, reason: loose ? (\"too_far\" as const) : (\"inside_band\" as const), ...spend };\n }\n const { placed, shown } = await placeForExtend(bytes, fit, params.render_ratio);\n ctx.log(\n `placed ${params.format} at scale ${fit.scale.toFixed(3)}, offset ${fit.x.toFixed(3)}, ${fit.y.toFixed(3)}`,\n );\n const [placedRef, shownRef] = await Promise.all([\n ctx.assets.ingestBytes({ bytes: placed, kind: \"image\", mime: \"image/png\" }),\n ctx.assets.ingestBytes({ bytes: shown, kind: \"image\", mime: \"image/png\" }),\n ]);\n return {\n placed: placedRef as z.infer<typeof ImageRef>,\n shown: shownRef as z.infer<typeof ImageRef>,\n fit,\n ...spend,\n };\n },\n});\n\nexport const staticExtendMergeNode = defineNode({\n id: \"static_extend_merge\",\n version: \"1.1.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Have the image model paint a placed static ad's margins (`static_place`'s `shown`, with `paint`, an `image_generate`'s params) and lay the ad (`placed`) back over the painting: the ad's own pixels where it sits, the painting only in the margins, blended at their seam. Same pixel size as the ad. An ad that was never placed (`shown` is `placed`) passes on as it is, and nothing is painted or billed. A painting already made can be wired as `image` instead of `paint`.\",\n when_to_use:\n \"Added by a static ad's autofix round after a `static_place`; never scaffolded. One picture at the painter's price, only for a size that was placed.\",\n inputs: z.object({ placed: ImageRef, shown: ImageRef.optional(), image: ImageRef.optional() }).strict(),\n params: z.object({ render_ratio: RenderRatio, paint: z.record(z.string(), z.unknown()).optional() }).strict(),\n outputs: z.object({ image: ImageRef }).strict(),\n outputKinds: { image: \"image\" },\n cost: ({ params }) =>\n params.paint ? (imageGenerateNode.cost?.({ params: params.paint as never }) ?? NO_COST) : NO_COST,\n async execute({ inputs, params, ctx }) {\n const painting = inputs.image ?? (await paintMargins(inputs, params.paint, ctx));\n const painted = painting?.path ?? painting?.url;\n const placed = inputs.placed.path ?? inputs.placed.url;\n if (!painted || !placed) return { image: inputs.placed };\n const merged = await mergeExtended(await bytesOf(painted), await bytesOf(placed), params.render_ratio);\n const image = await ctx.assets.ingestBytes({ bytes: merged, kind: \"image\", mime: \"image/png\" });\n return { image: image as z.infer<typeof ImageRef> };\n },\n});\n\nconst NO_COST = { credits: 0, seconds_estimate: 2 };\n\n/** The painter's picture of the margins `shown` leaves, or null for an ad that was never placed. */\nasync function paintMargins(\n inputs: { placed: z.infer<typeof ImageRef>; shown?: z.infer<typeof ImageRef> },\n paint: Record<string, unknown> | undefined,\n ctx: ExecCtx,\n): Promise<z.infer<typeof ImageRef> | null> {\n const { shown, placed } = inputs;\n if (!paint || !shown || shown.sha256 === placed.sha256) return null;\n const params = imageGenerateNode.params.parse(paint);\n const out = await callBackendExec({\n nodeType: imageGenerateNode.id,\n nodeVersion: imageGenerateNode.version,\n params,\n inputs: { reference: [shown] },\n outputKinds: { images: \"image\" },\n ctx,\n });\n const images = out.images;\n return Array.isArray(images) ? ((images[0] as z.infer<typeof ImageRef> | undefined) ?? null) : null;\n}\n","import { IMAGE_GENERATE_MODELS, IMAGE_TIER_CREDITS, imagePictureCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\nimport { registryParamShape } from \"./registry-params.ts\";\n\n// Imported, never re-listed. This used to be a local copy of the roster, which\n// silently went stale the moment a model was added to the contract: the registry\n// priced and validated the new model while this enum still rejected it, so every\n// canvas using it failed with a confusing `INVALID_PARAMS` naming the OLD set.\n// The video node has always imported its roster; this now matches.\nconst imageModelEnum = z.enum(IMAGE_GENERATE_MODELS as unknown as [string, ...string[]]);\n\n// Permissive union of every param accepted by any curated model, derived from the\n// registry (`registryParamShape`), which is also the strict per-model gate: it rejects\n// params a specific model doesn't support and validates enum values. Spelled out here,\n// the `quality` list once stopped at `high` while gpt-image-2.5 grew `xhigh` and `max`,\n// and every ad `scaffold-ad` wrote failed validation before a single node ran.\nconst ImageGenerateParams = z\n .object({\n model: imageModelEnum,\n ...registryParamShape(\"image_generate\", {\n prompt: z.string().min(1),\n // Recraft v4 vector controls, sent as the provider's own `controls` block. The\n // registry types them as JSON; this is their shape.\n rgb_colors: z.array(z.array(z.number().int().min(0).max(255))),\n background_rgb_color: z.array(z.number().int().min(0).max(255)),\n }),\n })\n .strict();\n\n/**\n * A picture asked at the top quality step bills about what Nano Banana Pro's does: bench E5\n * measured gpt-image-2.5 at `max`, with the chosen ad and the logo as references, at $0.14 (9:16)\n * to $0.21 (3:4) a picture (.context/static-review/BENCH.md § \"E5 — true per-format adaptation\").\n */\nconst PRO_QUALITY = \"max\";\n\n/** The quote of one picture: the Pro image models and the top quality step at Pro's rate, the rest at the plain rate. */\nfunction creditsPerImage(params: { model?: unknown; quality?: unknown }): number {\n return params.quality === PRO_QUALITY ? IMAGE_TIER_CREDITS.pro : imagePictureCredits(String(params.model));\n}\n\n/** The `n` a node asks for. The registry-derived shape types it only as one of many params. */\nfunction imagesPerCall(params: object): number {\n return \"n\" in params && typeof params.n === \"number\" ? params.n : 1;\n}\n\nexport const imageGenerateNode = delegated({\n id: \"image_generate\",\n version: \"2.2.0\",\n category: \"image\",\n summary:\n \"Generate images for ad creatives. Curated model set: GPT Image 2, Gemini 3.1 Flash Image Preview, Gemini 3 Pro Image, Recraft v4.1 Pro Vector. Per-model param support comes from the canvas-engine model registry.\",\n when_to_use:\n \"Use for hero shots, product photography, illustrations, and vector logos. `recraft/recraft-v4.1-pro-vector` for crisp vector / logo work; `openai/gpt-image-2` for photorealistic and in-image text (NOTE: it has no `image_size`, and no 4:5 / 5:4 — route portrait ad formats through Gemini or `image_aspect_adapt`); Gemini variants for fast iteration, extreme ratios, and editing via the `reference` input. `reference` accepts ONE image or an ARRAY of images — wire several to combine references in a single generation (e.g. a subject sheet + a font specimen + the original ad). Every reference is forwarded to the model in array order. `n` (only on models that declare it, e.g. `openai/gpt-image-2.5-sunburst`) returns several candidates of the same brief from one call, billed per image like separate calls; wire the whole `images` array into `image_select` to keep the best.\",\n // `reference` is one image or an ordered array of images. The backend forwards\n // each as a separate `image_url` to the provider (OpenRouter accepts many).\n inputs: z.object({ reference: z.union([ImageRef, z.array(ImageRef).min(1)]).optional() }).loose(),\n params: ImageGenerateParams,\n outputs: z.object({ images: z.array(ImageRef).min(1) }).strict(),\n outputKinds: { images: \"image\" },\n // Per image: `n` asks one call for several, and the provider bills each (measured: n=3\n // cost exactly 3x n=1, prompt tokens included). Usage-billed, so this is only the pre-run\n // quote; the charge is the provider's reported cost.\n cost: ({ params }) => ({\n credits: creditsPerImage(params) * imagesPerCall(params),\n seconds_estimate: 10,\n }),\n});\n","import { MODEL_REGISTRY, type NodeKind, type ParamSchema } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\n\n/**\n * A managed node's param schema, derived from the model registry rather than listed\n * by hand: the PERMISSIVE union of every param any model of `kind` declares. It\n * answers \"is this a param at all, and of the right shape\"; which model takes which\n * param, and which enum value, stays the registry validator's job\n * (`validateModelParams`), because a node schema cannot branch on the model.\n *\n * Listed by hand, this drifted: Seedance 2.5 declared `watermark` and the backend\n * forwarded it, while the node's strict schema refused it before it left the CLI.\n *\n * `overrides` replaces the derived schema for a param, for shapes the registry types\n * as `json` (the registry only knows it is JSON) or where the node wants a stricter\n * floor than the union (a non-empty prompt). A param is required only when every\n * model requires it.\n */\nexport function registryParamShape(\n kind: NodeKind,\n overrides: Readonly<Record<string, z.ZodType>> = {},\n): Record<string, z.ZodType> {\n const specs = Object.values(MODEL_REGISTRY[kind]);\n const byParam = new Map<string, ParamSchema[]>();\n for (const spec of specs) {\n for (const [name, schema] of Object.entries(spec.params)) {\n byParam.set(name, [...(byParam.get(name) ?? []), schema]);\n }\n }\n const shape: Record<string, z.ZodType> = {};\n for (const [name, schemas] of byParam) {\n const type = overrides[name] ?? unionOf(schemas);\n const requiredByAll = specs.every((spec) => spec.required.includes(name));\n shape[name] = requiredByAll ? type : type.optional();\n }\n return shape;\n}\n\nfunction unionOf(schemas: readonly ParamSchema[]): z.ZodType {\n const kinds = [...new Set(schemas.map((schema) => schema.kind))];\n if (kinds.length > 1) {\n const [first, second, ...rest] = kinds.map((k) => unionOf(schemas.filter((s) => s.kind === k)));\n return z.union([first as z.ZodType, second as z.ZodType, ...rest]);\n }\n switch (kinds[0]) {\n case \"string\":\n return stringUnion(schemas as Extract<ParamSchema, { kind: \"string\" }>[]);\n case \"number\":\n return numberUnion(schemas as Extract<ParamSchema, { kind: \"number\" }>[]);\n case \"boolean\":\n return z.boolean();\n default:\n return z.unknown();\n }\n}\n\n/** Every value some model enumerates, or any string once one model leaves it open. */\nfunction stringUnion(schemas: Extract<ParamSchema, { kind: \"string\" }>[]): z.ZodType {\n if (schemas.some((schema) => !schema.enum)) return z.string();\n const values = [...new Set(schemas.flatMap((schema) => schema.enum ?? []))];\n return z.enum(values as [string, ...string[]]);\n}\n\n/** Every value some model enumerates, or any number once one model leaves it open. */\nfunction numberUnion(schemas: Extract<ParamSchema, { kind: \"number\" }>[]): z.ZodType {\n if (schemas.some((schema) => !schema.enum)) return z.number();\n const values = [...new Set(schemas.flatMap((schema) => schema.enum ?? []))].sort((a, b) => a - b);\n return z.literal(values);\n}\n","/**\n * The autofix's place-and-extend: when a size's words or logo sit where an interface covers them\n * (Reels' footer) or run off its edge, code shrinks and places the finished ad where nothing covers\n * it (`planSafeFit` with `MIN_EXTEND_SCALE`), the image model paints only the margins that leaves,\n * and code lays the ad's own pixels back over its painting. Why: told to move a button above 72%\n * of the height, the model moved it from 91% to 86% in two rounds (Oct 2026 bench); it keeps what\n * is there well, and moves it badly. `placeForExtend` and `mergeExtended` only transform bytes.\n */\nimport sharp from \"sharp\";\nimport { plainEdge, type Raw, type SafeFit } from \"./safe-fit.ts\";\n\n/**\n * The smallest scale place-and-extend takes: the words stay readable at a glance and the ad stays\n * the ad; past it the size is laid out again instead. Bench, Oct 2026: a re-laid 9:16 fills its\n * height (3%-93%), and clearing Reels took 0.65-0.67; its type is set larger than the 4:5's, so at\n * 0.6 its words are still the 4:5's size.\n */\nexport const MIN_EXTEND_SCALE = 0.6;\n\n/** The margins the model is asked to paint, in a grey nothing in an ad is. */\nconst GREY_LEVEL = 128;\nconst GREY = { r: GREY_LEVEL, g: GREY_LEVEL, b: GREY_LEVEL };\n\n/** How far into the ad its edge blends into the painting, as a share of the frame's width. */\nconst FEATHER = 0.012;\n\ntype Box = { width: number; height: number; left: number; top: number };\n\nfunction ratioOf(ratio: string): number {\n const [w = 1, h = 1] = ratio.split(\":\").map(Number);\n return w / h;\n}\n\n/** The model's render shape around a frame: the frame centred in it, the rest to paint. */\nfunction renderCanvas(frame: { width: number; height: number }, ratio: string): Box {\n const r = ratioOf(ratio);\n const f = frame.width / frame.height;\n if (Math.abs(r - f) < 1e-3) return { width: frame.width, height: frame.height, left: 0, top: 0 };\n if (r < f) {\n const height = Math.round(frame.width / r);\n return { width: frame.width, height, left: 0, top: Math.round((height - frame.height) / 2) };\n }\n const width = Math.round(frame.height * r);\n return { width, height: frame.height, left: Math.round((width - frame.width) / 2), top: 0 };\n}\n\n/**\n * The ad shrunk and set as `fit` says, at its own pixel size: `placed` with clear margins, for the\n * merge; `shown`, the same inside the model's render shape (`ratio`) with grey margins, to paint.\n */\nexport async function placeForExtend(\n image: Buffer,\n fit: SafeFit,\n ratio: string,\n): Promise<{ placed: Buffer; shown: Buffer }> {\n const { width = 0, height = 0 } = await sharp(image).metadata();\n const sw = Math.max(1, Math.round(width * fit.scale));\n const sh = Math.max(1, Math.round(height * fit.scale));\n const px = Math.round(fit.x * width);\n const py = Math.round(fit.y * height);\n const left = Math.max(0, -px);\n const top = Math.max(0, -py);\n const w = Math.min(sw, width - px) - left;\n const h = Math.min(sh, height - py) - top;\n const shrunk = await sharp(image)\n .removeAlpha()\n .resize(sw, sh, { fit: \"fill\", kernel: \"lanczos3\" })\n .extract({ left, top, width: w, height: h })\n .raw()\n .toBuffer({ resolveWithObject: true });\n const raw: Raw = { data: shrunk.data, width: w, height: h, channels: shrunk.info.channels };\n const margin = { left: Math.max(0, px), top: Math.max(0, py), right: 0, bottom: 0 };\n margin.right = width - margin.left - w;\n margin.bottom = height - margin.top - h;\n const carried = await sharp(shrunk.data, { raw: { width: w, height: h, channels: shrunk.info.channels } })\n .extend({ ...margin, extendWith: \"copy\" })\n .ensureAlpha()\n .raw()\n .toBuffer();\n const shownPixels = clearBusyMargins(carried, { width, height }, keptBox(raw, margin, { width, height }));\n const placed = await sharp(carried, { raw: { width, height, channels: 4 } })\n .png()\n .toBuffer();\n const canvas = renderCanvas({ width, height }, ratio);\n const frame = await sharp(shownPixels, { raw: { width, height, channels: 3 } })\n .png()\n .toBuffer();\n const shown = await sharp({ create: { width: canvas.width, height: canvas.height, channels: 3, background: GREY } })\n .composite([{ input: frame, left: canvas.left, top: canvas.top }])\n .png()\n .toBuffer();\n return { placed, shown };\n}\n\ntype Margins = { left: number; top: number; right: number; bottom: number };\n\n/**\n * What of the frame stays as placed: the ad, and each margin whose edge is plain ground, carried\n * on by code exactly; a margin beside a scene's edge is left to paint.\n */\nfunction keptBox(ad: Raw, margin: Margins, frame: { width: number; height: number }): Bounds {\n const plain = (side: keyof Margins) => margin[side] === 0 || plainEdge(ad, side);\n return {\n x0: plain(\"left\") ? 0 : margin.left,\n y0: plain(\"top\") ? 0 : margin.top,\n x1: plain(\"right\") ? frame.width - 1 : margin.left + ad.width - 1,\n y1: plain(\"bottom\") ? frame.height - 1 : margin.top + ad.height - 1,\n };\n}\n\n/** `rgba` cleared (alpha 0) outside `keep`, and the picture the model is shown: the same, grey where cleared. */\nfunction clearBusyMargins(rgba: Buffer, frame: { width: number; height: number }, keep: Bounds): Buffer {\n const shown = Buffer.alloc(frame.width * frame.height * 3, GREY_LEVEL);\n for (let y = keep.y0; y <= keep.y1; y++) {\n for (let x = keep.x0; x <= keep.x1; x++) {\n const i = y * frame.width + x;\n rgba.copy(shown, i * 3, i * 4, i * 4 + 3);\n }\n }\n for (let i = 0; i < frame.width * frame.height; i++) {\n const x = i % frame.width;\n const y = Math.floor(i / frame.width);\n rgba[i * 4 + 3] = x >= keep.x0 && x <= keep.x1 && y >= keep.y0 && y <= keep.y1 ? 255 : 0;\n }\n return shown;\n}\n\n/**\n * The model's painting (`extended`, of the `shown` canvas) under the ad's own pixels (`placed`):\n * the painting only where `placed` is clear, blended over `FEATHER` at the ad's edges inside the\n * frame. `placed` with no clear pixel was never placed, and passes on as it is.\n */\nexport async function mergeExtended(extended: Buffer, placed: Buffer, ratio: string): Promise<Buffer> {\n const ad = await sharp(placed).ensureAlpha().raw().toBuffer({ resolveWithObject: true });\n const { width, height } = ad.info;\n const bounds = placedBounds(ad.data, width, height);\n if (!bounds) return sharp(placed).removeAlpha().png().toBuffer();\n const canvas = renderCanvas({ width, height }, ratio);\n const paint = await sharp(extended)\n .removeAlpha()\n .resize(canvas.width, canvas.height, { fit: \"fill\" })\n .extract({ left: canvas.left, top: canvas.top, width, height })\n .raw()\n .toBuffer();\n const weight = edgeWeight(bounds, width, height, Math.max(1, Math.round(FEATHER * width)));\n const out = Buffer.alloc(width * height * 3);\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const i = y * width + x;\n const w = weight(x, y);\n for (let c = 0; c < 3; c++) {\n out[i * 3 + c] = Math.round((ad.data[i * 4 + c] ?? 0) * w + (paint[i * 3 + c] ?? 0) * (1 - w));\n }\n }\n }\n return sharp(out, { raw: { width, height, channels: 3 } })\n .png()\n .toBuffer();\n}\n\ntype Bounds = { x0: number; y0: number; x1: number; y1: number };\n\n/** Where the placed ad's pixels are (its opaque box), or null when nothing of the frame is clear: it was never placed. */\nfunction placedBounds(rgba: Buffer, width: number, height: number): Bounds | null {\n const box: Bounds = { x0: width, y0: height, x1: -1, y1: -1 };\n let clear = false;\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n if ((rgba[(y * width + x) * 4 + 3] ?? 0) === 0) {\n clear = true;\n continue;\n }\n box.x0 = Math.min(box.x0, x);\n box.y0 = Math.min(box.y0, y);\n box.x1 = Math.max(box.x1, x);\n box.y1 = Math.max(box.y1, y);\n }\n }\n return clear && box.x1 >= 0 ? box : null;\n}\n\n/**\n * How much of the ad a pixel takes: 1 inside it, 0 in the margins, rising over `feather` pixels at\n * an edge of the ad inside the frame (one on the frame's own edge has nothing to meet).\n */\nfunction edgeWeight(b: Bounds, width: number, height: number, feather: number): (x: number, y: number) => number {\n const ramp = (d: number, onFrame: boolean) => (onFrame ? 1 : Math.min(1, (d + 1) / feather));\n return (x, y) =>\n x < b.x0 || x > b.x1 || y < b.y0 || y > b.y1\n ? 0\n : Math.min(\n ramp(x - b.x0, b.x0 === 0),\n ramp(b.x1 - x, b.x1 === width - 1),\n ramp(y - b.y0, b.y0 === 0),\n ramp(b.y1 - y, b.y1 === height - 1),\n );\n}\n","/**\n * The correction that brings a finished 9:16's words and logo inside the band Stories and\n * Reels leave clear, without asking the image model again: shrink the whole picture a little\n * and shift it, then carry its plain edge into the uncovered margin (never a busy one).\n *\n * Why here and not in a re-render: the image model cannot move an element by a few points on\n * request (a `--fix` asking for \"8% up\" moved it about 1%), and agents looped 2-17 renders on\n * it. `planSafeFit` is pure; `fitPicture` only transforms bytes.\n */\nimport sharp from \"sharp\";\nimport { blockingArea, blockingPlacements } from \"../../../scaffold/platform-safe-area.ts\";\nimport { bandViolations, FRAME_CUT } from \"./band-check.ts\";\nimport { type CopyLine, roleKey, shownBoxes, withWordmark } from \"./must-show.ts\";\nimport type { TextBox } from \"./text-boxes.ts\";\n\n/**\n * How far inside the band the fit aims, on every side: the reader draws its boxes a little\n * differently on each read, and the run review reads the fitted picture again.\n */\nexport const FIT_HEADROOM = 0.01;\n\n/**\n * The smallest scale a fit takes. Measured on the 20 cached bench 9:16s: a shrink to 0.85 or\n * more, its margin filled from the picture's own edge under a feathered seam, still looked like\n * the picture as rendered; below it the filled edges show. 19 of 20 cleared\n * the Reels band at 0.82 or more, most at 0.93 or more.\n */\nexport const MIN_FIT_SCALE = 0.85;\n\n/** Where the shrunk picture goes: its scale, and its top-left corner as fractions of the frame. */\nexport type SafeFit = { scale: number; x: number; y: number };\n\n/**\n * The boxes a fit moves the ad for. Given the ad's copy, only its logo and the copy that must be\n * seen (`must-show.ts`): never what the scene prints, a line printed again in it, or a supporting\n * line an interface covers (one the frame cuts is placed). Without it (a canvas from before), every box read.\n */\nexport function boxesToPlace(\n boxes: readonly TextBox[],\n format: string,\n opts: { placements?: readonly string[]; copy?: readonly CopyLine[] } = {},\n): TextBox[] {\n if (!opts.copy) return [...boxes];\n const placements = blockingPlacements(opts.placements);\n const past = (box: TextBox) => bandViolations([box], format, { placements })[0];\n // A supporting line the frame cuts is lost all the same: the review blocks it, so it is placed.\n const cut = (box: TextBox) => (past(box)?.clearance ?? 1) <= FRAME_CUT;\n const logoWords = opts.copy.filter((l) => roleKey(l.role) === \"logo\").map((l) => l.text);\n return shownBoxes(withWordmark(boxes, logoWords), opts.copy, (box) => !past(box))\n .filter((shown) => !shown.supporting || cut(shown.box))\n .map((shown) => shown.box);\n}\n\n/**\n * The fit that clears `format`'s band for `placements`, or null when nothing reaches past it or\n * clearing it would take a shrink below `minScale` (`MIN_FIT_SCALE` unless given: a fit whose\n * margins are painted, not carried from the edge, may go further).\n */\nexport function planSafeFit(\n boxes: readonly TextBox[],\n format: string,\n opts: { placements?: readonly string[]; minScale?: number; contain?: boolean } = {},\n): SafeFit | null {\n const area = blockingArea(format, opts.placements);\n // Checked as the review checks it, Reels' buttons included where they block.\n if (bandViolations(boxes, format, { placements: blockingPlacements(opts.placements) }).length === 0) return null;\n // A line that reaches down beside the buttons keeps clear of them: the whole picture is fitted inside.\n const rail = area.rail && boxes.some((b) => b.y1 > (area.rail?.from ?? 1)) ? area.rail.width : 0;\n const union = {\n x0: Math.min(...boxes.map((b) => b.x0)),\n y0: Math.min(...boxes.map((b) => b.y0)),\n x1: Math.max(...boxes.map((b) => b.x1)),\n y1: Math.max(...boxes.map((b) => b.y1)),\n };\n const band = {\n x0: area.left + FIT_HEADROOM,\n y0: area.top + FIT_HEADROOM,\n x1: 1 - Math.max(area.right, rail) - FIT_HEADROOM,\n y1: 1 - area.bottom - FIT_HEADROOM,\n };\n const scale = Math.min(1, (band.y1 - band.y0) / (union.y1 - union.y0), (band.x1 - band.x0) / (union.x1 - union.x0));\n if (opts.contain) return containedFit(union, band, opts.minScale ?? MIN_FIT_SCALE, scale);\n if (scale < (opts.minScale ?? MIN_FIT_SCALE)) return null;\n return {\n scale,\n x: offset(scale, union.x0, union.x1, band.x0, band.x1),\n y: offset(scale, union.y0, union.y1, band.y0, band.y1),\n };\n}\n\n/** How finely `containedFit` steps the scale down. */\nconst CONTAIN_STEP = 0.005;\n\ntype Span = { x0: number; y0: number; x1: number; y1: number };\n\n/**\n * The largest fit, from `from` down to `minScale`, that lands the boxes' `union` inside `band`\n * with the whole picture inside the frame: nothing of it shifted off an edge, its margins all\n * painted. Each axis starts where the picture moves least from where it was. Null when none does.\n */\nfunction containedFit(union: Span, band: Span, minScale: number, from: number): SafeFit | null {\n const start = (scale: number, lo0: number, hi0: number, b0: number, b1: number): number | null => {\n const lo = Math.max(0, b0 - scale * lo0);\n const hi = Math.min(1 - scale, b1 - scale * hi0);\n if (lo > hi + 1e-9) return null;\n // Anchored where it was when it can be (an edge-to-edge picture keeps its top), else nearest.\n return Math.min(hi, Math.max(lo, 0));\n };\n for (let scale = Math.min(1, from); scale >= minScale - 1e-9; scale -= CONTAIN_STEP) {\n const x = start(scale, union.x0, union.x1, band.x0, band.x1);\n const y = start(scale, union.y0, union.y1, band.y0, band.y1);\n if (x !== null && y !== null) {\n // Centred across: a picture shrunk on both sides reads as the same picture, not pushed aside.\n const cx = Math.min(\n Math.min(1 - scale, band.x1 - scale * union.x1),\n Math.max(Math.max(0, band.x0 - scale * union.x0), (1 - scale) / 2),\n );\n return { scale, x: cx, y };\n }\n }\n return null;\n}\n\n/**\n * Where the shrunk picture starts on one axis: centred, unless that leaves the boxes' span\n * outside the band, then the nearest start that puts it inside (the middle of the range when\n * no start does).\n */\nfunction offset(scale: number, from: number, to: number, bandFrom: number, bandTo: number): number {\n const lo = bandFrom - scale * from;\n const hi = bandTo - scale * to;\n if (lo > hi) return (lo + hi) / 2;\n return Math.min(hi, Math.max(lo, (1 - scale) / 2));\n}\n\n/** How deep into the picture an edge must stay the same to be carried on, as a share of the frame's side. */\nconst PLAIN_DEPTH = 0.03;\n/** The most a pixel may differ from the one at the edge, over that depth, summed over its channels. */\nconst PLAIN_DEPTH_STEP = 36;\n/** A jump along the edge this large (summed over channels) is one thing's border crossing it. */\nconst PLAIN_JUMP = 60;\n/** The most borders an edge may cross and still read as plain ground: a two-tone split, never a row of cards. */\nconst PLAIN_JUMPS = 2;\n\nexport type Raw = { data: Buffer; width: number; height: number; channels: number };\n\n/**\n * Whether the picture's `side` is plain ground that can be carried outward unchanged: it looks\n * the same `PLAIN_DEPTH` in as at the edge, and at most `PLAIN_JUMPS` borders cross it. Pure.\n */\nexport function plainEdge(img: Raw, side: \"top\" | \"bottom\" | \"left\" | \"right\"): boolean {\n const along = side === \"top\" || side === \"bottom\" ? img.width : img.height;\n const across = side === \"top\" || side === \"bottom\" ? img.height : img.width;\n const depth = Math.max(1, Math.round(PLAIN_DEPTH * across));\n const at = (i: number, d: number) => {\n const inward = side === \"top\" || side === \"left\" ? d : across - 1 - d;\n const [x, y] = side === \"top\" || side === \"bottom\" ? [i, inward] : [inward, i];\n return (y * img.width + x) * img.channels;\n };\n const step = (a: number, b: number) => {\n let sum = 0;\n for (let c = 0; c < Math.min(3, img.channels); c++)\n sum += Math.abs((img.data[a + c] ?? 0) - (img.data[b + c] ?? 0));\n return sum;\n };\n let jumps = 0;\n for (let i = 0; i < along; i++) {\n for (let d = 1; d <= depth; d++) if (step(at(i, 0), at(i, d)) > PLAIN_DEPTH_STEP) return false;\n if (i > 0 && step(at(i - 1, 0), at(i, 0)) > PLAIN_JUMP) jumps++;\n }\n return jumps <= PLAIN_JUMPS;\n}\n\n/**\n * `image` with `fit` applied, at the same pixel size: the picture shrunk (Lanczos) and set\n * with its top-left at (`x`, `y`) of the frame, what falls off the frame cut, and each margin\n * it uncovers filled by carrying its edge on, or null when an edge it would carry is not plain\n * ground. Tier 1, Oct 2026: a margin filled from a busy edge, mirrored and blurred, read as \"a\n * blurred duplicate\" and \"the faded thing at the bottom\", worse than a line Reels covers. PNG out.\n */\nexport async function fitPicture(image: Buffer, fit: SafeFit): Promise<Buffer | null> {\n const { width = 0, height = 0 } = await sharp(image).metadata();\n const sw = Math.max(1, Math.round(width * fit.scale));\n const sh = Math.max(1, Math.round(height * fit.scale));\n const px = Math.round(fit.x * width);\n const py = Math.round(fit.y * height);\n // The part of the shrunk picture that lands on the frame.\n const left = Math.max(0, -px);\n const top = Math.max(0, -py);\n const w = Math.min(sw, width - px) - left;\n const h = Math.min(sh, height - py) - top;\n const shrunk = await sharp(image)\n .removeAlpha()\n .resize(sw, sh, { fit: \"fill\", kernel: \"lanczos3\" })\n .extract({ left, top, width: w, height: h })\n .raw()\n .toBuffer({ resolveWithObject: true });\n const pad = { left: Math.max(0, px), top: Math.max(0, py) };\n const extend = { ...pad, right: width - pad.left - w, bottom: height - pad.top - h };\n const raw: Raw = { data: shrunk.data, width: w, height: h, channels: shrunk.info.channels };\n const sides = ([\"top\", \"bottom\", \"left\", \"right\"] as const).filter((side) => extend[side] > 0);\n if (!sides.every((side) => plainEdge(raw, side))) return null;\n return sharp(shrunk.data, { raw: { width: w, height: h, channels: shrunk.info.channels } })\n .extend({ ...extend, extendWith: \"copy\" })\n .png()\n .toBuffer();\n}\n","import { readFile } from \"node:fs/promises\";\nimport { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { MASTER_SHAPES } from \"../../scaffold/static-sizes.ts\";\nimport { ImageRef, JsonRef } from \"../../schema/refs.ts\";\nimport type { ReviewUsage } from \"./lib/render-review.ts\";\nimport { judgeTie, prefilterVariants } from \"./lib/variant-prefilter.ts\";\n\nconst Dropped = z.object({ index: z.number().int().min(0), reasons: z.array(z.string()) }).strict();\nconst Usage = z\n .object({ model: z.string(), inputTokens: z.number(), outputTokens: z.number(), costUsd: z.number() })\n .strict();\n\nasync function readSpec(ref: { path?: string } | undefined): Promise<unknown> {\n if (!ref?.path) return null;\n try {\n return JSON.parse(await readFile(ref.path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nexport const staticPrefilterNode = defineNode({\n id: \"static_prefilter\",\n version: \"1.0.0\",\n category: \"image\",\n location: \"local\",\n // Reads each variant's pixels itself, from its file or its durable URL, so the variants\n // are not downloaded for it.\n passthroughRefs: true,\n summary:\n \"Rank a static ad's variants before the judge by the deterministic checks (blocking defects, copy read back, the brand's logo found, the button's words, words inside the safe area) and pass on only the best-ranked, so the judge only breaks an exact tie. Outputs them in an order seeded by the pictures (never the render order), `order` (which variant each one is) and `dropped` with each set-aside variant's reasons.\",\n when_to_use:\n \"Scaffolded by `baker canvas scaffold-static-ad` between the variants and the judge; wire the variants' whole `images` list and the ad spec (`$ref:prompt.asset`). Never leaves the judge empty: when every variant fails, the one with the fewest defects passes as `flagged`, and the review names what is wrong. A variant the reader could not read passes unchecked.\",\n inputs: z.object({ images: z.array(ImageRef).min(1), spec: JsonRef.optional() }).strict(),\n // `master_shape` and `placements` are the ones the canvas's sizes were planned with, so the\n // band the variants are checked against is the band every size is cut to keep.\n params: z\n .object({\n formats: z.array(z.string().min(1)).min(1),\n master_shape: z.enum(MASTER_SHAPES).optional(),\n placements: z.array(z.string().min(1)).optional(),\n // The judge, asked only on a real tie: its model, and the criteria compiled from prompt.json.\n judge_model: z.string().min(1).optional(),\n judge_prompt: z.string().min(1).optional(),\n // What the checks read of prompt.json, projected on every run (`staticChecksOf`); without it, the `spec` input.\n checks: z\n .object({\n kind: z.literal(\"static_checks\"),\n lines: z.array(\n z.object({ text: z.string(), lettering: z.string(), role: z.string().optional() }).passthrough(),\n ),\n buttons: z.array(z.string()),\n brand: z.string().optional(),\n logo: z.boolean(),\n })\n .optional(),\n })\n .strict(),\n outputs: z\n .object({\n images: z.array(ImageRef).min(1),\n // Which variant (its place among the inputs) each of `images` is, so the judge's \"Image k\" maps back.\n order: z.array(z.number().int().min(0)).optional(),\n // The picture every size is made from: the checks' first, or on a tie the judge's.\n image: ImageRef.optional(),\n // Which variant it is (its place among the inputs).\n chosen: z.number().int().min(0).optional(),\n // The judge's ranking lines, when a tie was put to it.\n reasoning: z.string().optional(),\n dropped: z.array(Dropped),\n flagged: Dropped.optional(),\n // What the reader's calls cost, as the gateway reported it (the run record carries it home).\n usage: z.array(Usage).optional(),\n })\n .strict(),\n outputKinds: { images: \"image\", image: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 1 }),\n async execute({ inputs, params }) {\n const variants = inputs.images.map((image, index) => ({\n index,\n sha256: image.sha256,\n ...(image.path ? { path: image.path } : {}),\n ...(image.url ? { url: image.url } : {}),\n }));\n const usage: ReviewUsage[] = [];\n const result = await prefilterVariants(variants, params.checks ?? (await readSpec(inputs.spec)), params.formats, {\n ...(params.master_shape ? { masterShape: params.master_shape } : {}),\n ...(params.placements ? { placements: params.placements } : {}),\n onSpend: (u) => usage.push(u),\n });\n const order = result.kept.filter((i) => inputs.images[i] !== undefined);\n const kept = order.flatMap((i) => (inputs.images[i] ? [inputs.images[i]] : []));\n const shown = kept.length > 0 ? order : inputs.images.map((_, i) => i);\n const tie =\n shown.length > 1 && params.judge_model && params.judge_prompt\n ? await judgeTie(\n shown.map((index) => ({ index, ...variants[index] })),\n { model: params.judge_model, prompt: params.judge_prompt, onSpend: (u) => usage.push(u) },\n )\n : null;\n const chosen = tie?.chosen ?? shown[0] ?? 0;\n return {\n images: kept.length > 0 ? kept : inputs.images,\n order: shown,\n ...(inputs.images[chosen] ? { image: inputs.images[chosen] } : {}),\n chosen,\n ...(tie ? { reasoning: tie.reasoning } : {}),\n dropped: result.dropped,\n ...(result.flagged ? { flagged: result.flagged } : {}),\n ...(usage.length > 0 ? { usage } : {}),\n };\n },\n});\n","/**\n * The deterministic checks a static ad's variants pass before the judge sees them: the slot\n * the variants node feeds (`static_prefilter`, role `variant_prefilter`).\n *\n * Each variant's words and logo are read with their boxes (`checkStaticImage`): a line\n * missing, misspelt or joined by words nobody asked for, or any word or the logo where the\n * review of the finished sizes would block it (outside the band a cut master keeps clear;\n * under a placement's interface or cut by the frame on a relaid-out one), is a blocking defect. The variants are then ranked by the checks (blocking\n * defects, lines printed exactly, the brand's logo found, the button's words, the band) and\n * only the best-ranked go on, so the judge only breaks an exact tie. Bench E9\n * (.context/static-review/BENCH.md § \"E9 — judge calibrated to the user\"): no judge rubric or\n * model agreed with the person beyond chance, and the judge partly reads position, so the tied\n * variants reach it in an order seeded by the pictures, never the render order.\n *\n * Never leaves the judge with nothing: when every variant fails, the one with the fewest\n * defects is kept and `flagged` with them, so the round still has something to show and the\n * review names what is wrong. A variant the reader could not read (an outage) is kept\n * unchecked, tied with the best: the review of the finished sizes falls back to the review model.\n */\nimport { createHash } from \"node:crypto\";\nimport { AD_IMAGE_MODEL } from \"@baker/canvas-contract\";\nimport sharp from \"sharp\";\nimport type { SafeArea } from \"../../../scaffold/platform-safe-area.ts\";\nimport { readAdSpec } from \"../../../scaffold/static-ad-spec.ts\";\nimport {\n DEFAULT_MASTER_SHAPE,\n type MasterShape,\n planStaticFormats,\n type Size,\n} from \"../../../scaffold/static-sizes.ts\";\nimport { bandViolations } from \"./band-check.ts\";\nimport { type ExpectedLine, normalizeCopy, textMismatch } from \"./copy-match.ts\";\nimport { askAboutImages, type ReviewUsage } from \"./render-review.ts\";\nimport { bytesOf, checkStaticImage } from \"./static-review.ts\";\nimport type { TextBox, TextBoxReading } from \"./text-boxes.ts\";\n\n/** One variant as the node hands it over: its position among the variants and where its pixels are. */\nexport type VariantImage = { index: number; path?: string; url?: string; sha256: string };\n\nexport type PrefilterResult = {\n /** Indexes of the variants the judge breaks the tie between, in the seeded order it sees them. */\n kept: number[];\n /** Every variant set aside, with the reasons in product words (its blocking defects, else what the best ones have). */\n dropped: Array<{ index: number; reasons: string[] }>;\n /** Every variant failed: the one kept anyway, with what is wrong with it. */\n flagged?: { index: number; reasons: string[] };\n /** Variants kept because the reader could not read them. */\n unchecked?: number[];\n};\n\n/**\n * Boxes read on a picture of `from` pixels, placed on the `to` frame it becomes: the master\n * node fills the frame and crops the overflow about the centre (`-resize WxH^ -gravity\n * center -extent WxH`), so a variant rendered at a nearby supported ratio loses a sliver at\n * two edges. Pure.\n */\nexport function coverFitBoxes(boxes: readonly TextBox[], from: Size, to: Size): TextBox[] {\n const scale = Math.max(to.width / from.width, to.height / from.height);\n const offX = (from.width * scale - to.width) / 2;\n const offY = (from.height * scale - to.height) / 2;\n const x = (f: number) => (f * from.width * scale - offX) / to.width;\n const y = (f: number) => (f * from.height * scale - offY) / to.height;\n return boxes.map((b) => ({ ...b, x0: x(b.x0), y0: y(b.y0), x1: x(b.x1), y1: y(b.y1) }));\n}\n\n/**\n * How the checks rank one variant, most important first, each a count (lower is better):\n * blocking defects, lines not printed exactly, the brand's logo not found (when the ad carries\n * one), the button's words not printed exactly, words or the logo outside the band.\n */\ntype Rank = readonly [blocking: number, linesOff: number, noLogo: number, noButton: number, outOfBand: number];\n\ntype Checked =\n | { index: number; blocking: string[]; rank: Rank; shortfalls: string[] }\n | { index: number; blocking: null };\n\nconst NO_LOGO = \"The brand's logo was not found on it.\";\n\n/** What one variant's boxes say against the spec, as its rank and what it lacks beyond its blocking defects. Pure. */\nfunction rankOf(\n boxes: readonly TextBox[],\n blocking: readonly string[],\n want: {\n lines: readonly ExpectedLine[];\n buttons: ReadonlySet<string>;\n brand?: string;\n logo: boolean;\n format: string;\n grade: BandGrade;\n },\n): { rank: Rank; shortfalls: string[] } {\n const allowed = want.brand ? [want.brand] : [];\n const off = textMismatch(want.lines, boxes, { allowed }).lines;\n const brand = want.brand ? normalizeCopy(want.brand) : null;\n const logoFound = boxes.some((b) => b.kind === \"logo\" || (brand !== null && normalizeCopy(b.text) === brand));\n const noLogo = want.logo && !logoFound ? 1 : 0;\n const noButton = off.some((l) => want.buttons.has(l.text)) ? 1 : 0;\n const outOfBand = bandViolations(boxes, want.format, want.grade).length;\n return {\n rank: [blocking.length, off.length, noLogo, noButton, outOfBand],\n shortfalls: noLogo ? [NO_LOGO] : [],\n };\n}\n\n/**\n * What a variant's boxes are held against. On the cut shapes, the band every size cut from the\n * master keeps clear. On `relayout` the master is the lead format delivered as it is and every\n * other size is laid out anew, so it is graded by its own placement exactly as the review of the\n * finished sizes grades it (close to a feed image's edge is a note there, not a defect).\n */\ntype BandGrade = { band: SafeArea } | { placements?: readonly string[] };\n\nconst isButton = (line: { role?: string; look?: string }): boolean =>\n /cta|button/i.test(`${line.role ?? \"\"} ${line.look ?? \"\"}`);\n\n/**\n * The part of the ad spec the variant checks read: its lines, which are buttons, the brand's\n * name and whether a logo is drawn. Projected into the canvas (`params.checks`) so an edit of\n * anything else in prompt.json (a note, the concept's transfer note) changes no step.\n */\nexport type StaticChecks = {\n kind: \"static_checks\";\n lines: ExpectedLine[];\n buttons: string[];\n brand?: string;\n logo: boolean;\n};\n\n/** The checks a spec (or an already-projected `StaticChecks`) asks for; null when unreadable. Pure. */\nexport function staticChecksOf(spec: unknown): StaticChecks | null {\n if (spec && typeof spec === \"object\" && (spec as { kind?: unknown }).kind === \"static_checks\")\n return spec as StaticChecks;\n try {\n const parsed = readAdSpec(spec);\n // A native disguise carries the logo as its sender and no big button (C6): neither is ranked.\n const native = parsed.brand_presence === \"native\";\n return {\n kind: \"static_checks\",\n lines: parsed.copy.map((l) => ({ text: l.text, lettering: l.lettering, ...(l.role ? { role: l.role } : {}) })),\n buttons: native ? [] : parsed.copy.filter(isButton).map((l) => l.text),\n ...(parsed.brand?.name ? { brand: parsed.brand.name } : {}),\n logo: parsed.logo === true && !native,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Check each variant against the ad spec (prompt.json's content or its projected `StaticChecks`, null when unreadable) and the\n * formats it will be cut to. `read` is the box reader (the gateway's vision model unless given);\n * `masterShape` and `placements` must be the ones the canvas was planned with.\n */\nexport async function prefilterVariants(\n variants: readonly VariantImage[],\n spec: unknown,\n formats: readonly string[],\n opts: {\n read?: (image: Buffer) => Promise<TextBoxReading>;\n masterShape?: MasterShape;\n placements?: readonly string[];\n onSpend?: (usage: ReviewUsage) => void;\n } = {},\n): Promise<PrefilterResult> {\n const everything: PrefilterResult = {\n kept: seededOrder(\n variants.map((v) => v.index),\n variants,\n ),\n dropped: [],\n };\n const checks = spec === null || spec === undefined ? null : staticChecksOf(spec);\n if (!checks) return everything;\n const { lines, brand, logo } = checks;\n const buttons = new Set(checks.buttons);\n if (formats.length === 0 || variants.length === 0) return everything;\n // The model only sets the render ratio, which this does not read; the band is the model's too.\n const plan = planStaticFormats(AD_IMAGE_MODEL, formats, {\n masterShape: opts.masterShape ?? DEFAULT_MASTER_SHAPE,\n ...(opts.placements ? { placements: opts.placements } : {}),\n });\n const format = plan.master.format;\n const grade: BandGrade =\n plan.masterShape === \"relayout\" ? (plan.placements ? { placements: plan.placements } : {}) : { band: plan.band };\n const checked = await Promise.all(\n variants.map(async (variant): Promise<Checked> => {\n const source = variant.path ?? variant.url;\n if (!source) return { index: variant.index, blocking: null };\n try {\n const bytes = await bytesOf(source);\n const { width = 0, height = 0 } = await sharp(bytes).metadata();\n const check = await checkStaticImage(bytes, {\n format,\n lines,\n allowed: brand ? [brand] : [],\n ...grade,\n toFrame: (boxes) => coverFitBoxes(boxes, { width, height }, plan.master.size),\n ...(opts.read ? { read: opts.read } : {}),\n ...(opts.onSpend ? { onSpend: opts.onSpend } : {}),\n });\n if (check.status === \"not_checked\") return { index: variant.index, blocking: null };\n const prefix = `${format}: `;\n const blocking = check.findings\n .filter((f) => f.severity === \"blocking\")\n .map((f) => (f.what.startsWith(prefix) ? f.what.slice(prefix.length) : f.what));\n const want = { lines, buttons, ...(brand ? { brand } : {}), logo, format, grade };\n return { index: variant.index, blocking, ...rankOf(check.boxes, blocking, want) };\n } catch {\n return { index: variant.index, blocking: null };\n }\n }),\n );\n const outcome = prefilterOutcome(checked);\n return { ...outcome, kept: seededOrder(outcome.kept, variants) };\n}\n\n/**\n * The variants the judge compares, in an order drawn from the pictures themselves (each one's\n * sha256 hashed with all of theirs): the same pictures always come in the same order, and that\n * order owes nothing to which was rendered first. Bench E9: the judge partly reads position,\n * crediting \"Image 1\" with the same feature whichever picture it is. Pure.\n */\nfunction seededOrder(indexes: readonly number[], variants: readonly VariantImage[]): number[] {\n const shaOf = new Map(variants.map((v) => [v.index, v.sha256] as const));\n const seed = [...variants.map((v) => v.sha256)].sort().join(\":\");\n const key = (index: number) =>\n createHash(\"sha256\")\n .update(`${seed}|${shaOf.get(index) ?? index}`)\n .digest(\"hex\");\n return [...indexes].sort((a, b) => (key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : a - b));\n}\n\nconst compareRanks = (a: Rank, b: Rank): number => {\n for (let i = 0; i < a.length; i++) {\n const d = (a[i] ?? 0) - (b[i] ?? 0);\n if (d !== 0) return d;\n }\n return 0;\n};\n\n/**\n * Which variants go on to the judge, from each one's checks (blocking null = not read): the\n * ones the checks rank best, so the judge only breaks an exact tie; a variant the reader could\n * not read ties with the best that was read. Pure.\n */\nfunction prefilterOutcome(checked: readonly Checked[]): PrefilterResult {\n const unchecked = checked.filter((c) => c.blocking === null).map((c) => c.index);\n const read = checked.flatMap((c) => (c.blocking === null ? [] : [c]));\n const extra = unchecked.length > 0 ? { unchecked } : {};\n const clean = read.filter((c) => c.blocking.length === 0);\n if (clean.length === 0 && unchecked.length === 0) {\n const best = [...read].sort((a, b) => compareRanks(a.rank, b.rank) || a.index - b.index)[0];\n if (!best) return { kept: [], dropped: [], ...extra };\n const dropped = read.filter((c) => c.index !== best.index).map((c) => ({ index: c.index, reasons: c.blocking }));\n return { kept: [best.index], dropped, flagged: { index: best.index, reasons: best.blocking }, ...extra };\n }\n const top = [...clean].sort((a, b) => compareRanks(a.rank, b.rank))[0]?.rank;\n const kept = checked\n .filter((c) => c.blocking === null || (top !== undefined && compareRanks(c.rank, top) === 0))\n .map((c) => c.index);\n const dropped = read\n .filter((c) => !kept.includes(c.index))\n .map((c) => ({ index: c.index, reasons: c.blocking.length > 0 ? c.blocking : c.shortfalls }));\n return { kept, dropped, ...extra };\n}\n\n/** The judge's answer on a tie: its ranking lines (`judgeBrief`), and the image number it puts first. */\ntype JudgeAnswer = { reasoning: string; best: number };\n\nfunction judgeAnswerOf(raw: unknown): JudgeAnswer | null {\n const answer = raw as { reasoning?: unknown; best?: unknown } | null;\n const best = Number(answer?.best);\n if (typeof answer?.reasoning !== \"string\" || !Number.isInteger(best)) return null;\n return { reasoning: answer.reasoning, best };\n}\n\n/**\n * The judge, asked only when the checks leave a real tie (bench, Sept 2026: it decided 5 of 59\n * runs, and every other run paid a node for nothing): the tied variants shown in the seeded\n * order, the criteria `judgeBrief` compiled from the brief. Returns which of `tied` it puts\n * first and its reasoning; null when it did not answer (the caller keeps the checks' first).\n */\nexport async function judgeTie(\n tied: ReadonlyArray<{ index: number; path?: string; url?: string }>,\n opts: {\n model: string;\n prompt: string;\n onSpend?: (usage: ReviewUsage) => void;\n ask?: (model: string, images: string[], prompt: string) => Promise<unknown>;\n },\n): Promise<{ chosen: number; reasoning: string } | null> {\n const jpegs: string[] = [];\n for (const variant of tied) {\n const source = variant.path ?? variant.url;\n if (!source) return null;\n try {\n const bytes = await bytesOf(source);\n jpegs.push(\n (await sharp(bytes).resize(1024, 1024, { fit: \"inside\" }).jpeg({ quality: 85 }).toBuffer()).toString(\"base64\"),\n );\n } catch {\n return null;\n }\n }\n const prompt = `${opts.prompt}\\n\\nThe candidates are Image 1 to Image ${jpegs.length}, in the order shown. Reply with only this JSON: {\"reasoning\":\"<the ranking lines, then one sentence>\",\"best\":<the number of the image that wins>}`;\n const ask =\n opts.ask ?? ((model: string, images: string[], text: string) => askAboutImages(model, images, text, opts.onSpend));\n for (let attempt = 0; attempt < 2; attempt++) {\n const answer = judgeAnswerOf(await ask(opts.model, jpegs, prompt));\n const winner = answer ? tied[answer.best - 1] : undefined;\n if (answer && winner) return { chosen: winner.index, reasoning: answer.reasoning };\n }\n return null;\n}\n","import { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport { blockingArea } from \"../../scaffold/platform-safe-area.ts\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { bandViolations } from \"./lib/band-check.ts\";\nimport type { ReviewUsage } from \"./lib/render-review.ts\";\nimport { boxesToPlace, fitPicture, planSafeFit } from \"./lib/safe-fit.ts\";\nimport { bytesOf } from \"./lib/static-review.ts\";\nimport { readTextBoxes } from \"./lib/text-boxes.ts\";\n\nconst Usage = z\n .object({ model: z.string(), inputTokens: z.number(), outputTokens: z.number(), costUsd: z.number() })\n .strict();\nconst Fit = z.object({ scale: z.number(), x: z.number(), y: z.number() }).strict();\n/** The ad's copy (prompt.json's lines and their roles): the fit moves the ad for these and its logo only. */\nconst Copy = z.array(z.object({ text: z.string().min(1), role: z.string().optional() }).strict());\n\nexport const staticSafeFitNode = defineNode({\n id: \"static_safe_fit\",\n version: \"1.0.0\",\n category: \"image\",\n location: \"local\",\n summary:\n \"Bring a finished static ad's words and logo inside the band its placement leaves clear (the Stories header, the Reels footer) without rendering again: reads where every line and the logo sit, then shrinks the picture a little (to 85% at most) and shifts it, carrying its plain edge into the uncovered margin. Outputs the same pixel size; `fit` is the scale and position used, or null with `reason` when the picture is passed on unchanged.\",\n when_to_use:\n \"Scaffolded by `baker canvas scaffold-static-ad` after every 9:16 size (`safe_<size>`, role `static_safe_fit`). Free, besides one box-reader call (about $0.005, carried on the run record's review spend). A layout too tall or wide for the band at 85%, or whose edge is busy (cards, leaves, a scene that would smear), is passed on unchanged: the review still names what Reels or Stories covers, and the layout itself has to change (shorter copy, fewer lines).\",\n inputs: z.object({ image: ImageRef }).strict(),\n // `placements` are the ones the canvas's sizes were planned with (the ad set's), so the band\n // the fit clears is the one the review holds the size to.\n params: z\n .object({ format: z.string().min(1), placements: z.array(z.string().min(1)).optional(), copy: Copy.optional() })\n .strict(),\n outputs: z\n .object({\n image: ImageRef,\n fit: Fit.nullable(),\n // Why the picture passed unchanged: the reader could not read it, nothing was outside the\n // band, clearing it needed more than the smallest shrink that still looks native, or the\n // edge it would carry outward is not plain ground.\n reason: z.enum([\"not_read\", \"inside_band\", \"too_far\", \"busy_edge\"]).optional(),\n // What the reader's call cost, as the gateway reported it (the run record carries it home).\n usage: z.array(Usage).optional(),\n })\n .strict(),\n outputKinds: { image: \"image\" },\n cost: () => ({ credits: 0, seconds_estimate: 5 }),\n async execute({ inputs, params, ctx }) {\n const source = inputs.image.path ?? inputs.image.url;\n if (!source) return { image: inputs.image, fit: null, reason: \"not_read\" as const };\n const bytes = await bytesOf(source);\n const usage: ReviewUsage[] = [];\n const reading = await readTextBoxes(bytes, { onSpend: (u) => usage.push(u) });\n const spend = usage.length > 0 ? { usage } : {};\n if (reading.status !== \"read\") return { image: inputs.image, fit: null, reason: \"not_read\" as const, ...spend };\n const placements = params.placements ? { placements: params.placements } : {};\n const boxes = boxesToPlace(reading.boxes, params.format, {\n ...placements,\n ...(params.copy ? { copy: params.copy } : {}),\n });\n const fit = planSafeFit(boxes, params.format, placements);\n if (!fit) {\n const outside =\n bandViolations(boxes, params.format, { band: blockingArea(params.format, params.placements) }).length > 0;\n return {\n image: inputs.image,\n fit: null,\n reason: outside ? (\"too_far\" as const) : (\"inside_band\" as const),\n ...spend,\n };\n }\n const fitted = await fitPicture(bytes, fit);\n if (!fitted) return { image: inputs.image, fit: null, reason: \"busy_edge\" as const, ...spend };\n ctx.log(`fit ${params.format} at scale ${fit.scale.toFixed(3)}, offset ${fit.x.toFixed(3)}, ${fit.y.toFixed(3)}`);\n const image = await ctx.assets.ingestBytes({ bytes: fitted, kind: \"image\", mime: \"image/png\" });\n return { image: image as z.infer<typeof ImageRef>, fit, ...spend };\n },\n});\n","import { z } from \"zod\";\nimport { defineNode } from \"../../engine/define.ts\";\n\nexport const textNode = defineNode({\n id: \"text\",\n version: \"1.0.0\",\n category: \"data\",\n location: \"local\",\n summary: \"A literal text value. Use for prompts, descriptions, copy.\",\n inputs: z.object({}).strict(),\n params: z.object({ value: z.string() }).strict(),\n outputs: z.object({ text: z.string() }).strict(),\n cost: () => ({ credits: 0, seconds_estimate: 0 }),\n execute: ({ params }) => Promise.resolve({ text: params.value }),\n});\n","import { z } from \"zod\";\nimport { defineNode, type StaticTiming } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { getToolVersion, runCli } from \"./lib/cli-runner.ts\";\n\nconst FFMPEG_BIN = \"ffmpeg\";\n\nconst evenPixels = (axis: string) =>\n z\n .number()\n .int()\n .positive()\n .refine((n) => n % 2 === 0, `${axis} must be even: libx264 yuv420p rejects odd sizes`);\n\nconst Transition = z\n .object({\n type: z\n .string()\n .regex(/^[a-z]+$/, \"an ffmpeg xfade transition name, e.g. fade, dissolve, zoomin, slideleft\")\n .describe(\"ffmpeg xfade transition name: fade, dissolve, zoomin, wipeleft, slideleft, …\"),\n duration_s: z.number().positive().describe(\"Overlap length in seconds (0.4 is the scaffold default).\"),\n })\n .strict();\n\nconst ConcatClip = z\n .object({\n duration_s: z\n .number()\n .positive()\n .describe(\n \"Seconds this clip occupies on the timeline. A clip with a `transition` must carry that much EXTRA \" +\n \"footage past it (the overlap eats it), so the joined length is the sum of the clip durations.\",\n ),\n length: z\n .enum([\"source\", \"trim\", \"still\"])\n .default(\"source\")\n .describe(\n \"How the clip gets its length. `source`: play the input as rendered (it must already be duration_s \" +\n \"long, plus the transition tail). `trim`: cut duration_s (+ tail) out of a longer input, starting at \" +\n \"`offset_s`. `still`: the input is an IMAGE held on screen for duration_s (+ tail).\",\n ),\n offset_s: z.number().min(0).optional().describe(\"`trim` only: where the window starts in the input, seconds.\"),\n fit: z\n .enum([\"fill\", \"pad\"])\n .default(\"fill\")\n .describe(\n \"`fill` scales to cover the frame and center-crops (footage). `pad` fits inside and letterboxes in \" +\n \"black (a real screenshot or designed graphic, whose edges must never be cropped).\",\n ),\n transition: Transition.optional().describe(\n \"The transition OUT of this clip into the next one. Absent = a hard cut.\",\n ),\n continues_frame: z\n .boolean()\n .optional()\n .describe(\n \"True when this clip's first frame repeats the previous clip's last frame (a long shot split in \" +\n \"two). `seam_dedup` drops the duplicate without shortening the timeline.\",\n ),\n })\n .strict()\n .refine((c) => c.offset_s === undefined || c.length === \"trim\", {\n message: \"offset_s only applies to a `trim` clip\",\n path: [\"offset_s\"],\n });\n\nexport const VideoConcatParams = z\n .object({\n width: evenPixels(\"width\").describe(\"Output width in pixels (even). Every clip is normalized onto it.\"),\n height: evenPixels(\"height\").describe(\"Output height in pixels (even).\"),\n fps: z.number().int().min(1).max(120).default(30).describe(\"Output frame rate every clip is resampled to.\"),\n seam_dedup: z\n .enum([\"head\", \"tail\", \"off\"])\n .default(\"head\")\n .describe(\n \"Which copy of a repeated splice frame survives on a `continues_frame` clip: `head` drops the \" +\n \"continuing clip's first frame, `tail` drops the previous clip's last frame, `off` keeps both.\",\n ),\n clips: z.array(ConcatClip).min(1).describe(\"The clips in timeline order; clip i is wired as `inputs.c<i>`.\"),\n })\n .strict();\nexport type VideoConcatParamsT = z.infer<typeof VideoConcatParams>;\n/** One clip as a canvas writes it (defaults unfilled). */\nexport type VideoConcatClipInput = z.input<typeof ConcatClip>;\ntype ConcatClipT = VideoConcatParamsT[\"clips\"][number];\n\nconst VideoConcatInputs = z.record(z.string(), z.custom<AssetRef>());\nconst VideoConcatOutputs = z.object({ video: z.custom<AssetRef>() }).strict();\n\n/** Seconds of input footage a clip consumes: its timeline length plus any transition tail. */\nfunction inputLengthS(clip: ConcatClipT): number {\n return clip.duration_s + (clip.transition?.duration_s ?? 0);\n}\n\n/** True when any boundary blends, which switches the join from one concat to a fold of xfades. */\nfunction blends(params: VideoConcatParamsT): boolean {\n return params.clips.length > 1 && params.clips.some((c) => c.transition);\n}\n\n/**\n * The per-input normalize → length chain. Normalizing forces ONE raster before the\n * join (concat hard-fails on size/SAR mismatches and silently mis-times on fps or\n * timebase mismatches); the length cut runs AFTER normalize so seam-dedup's\n * frame-indexed ops always see the output frame rate.\n */\nfunction clipOps(clip: ConcatClipT, params: VideoConcatParamsT): string {\n const { width: w, height: h, fps } = params;\n const scale =\n clip.fit === \"pad\"\n ? `scale=${w}:${h}:force_original_aspect_ratio=decrease,pad=${w}:${h}:(ow-iw)/2:(oh-ih)/2:color=black`\n : `scale=${w}:${h}:force_original_aspect_ratio=increase,crop=${w}:${h}`;\n const normalize = `${scale},format=yuv420p,fps=${fps},setsar=1,settb=AVTB`;\n const len = inputLengthS(clip).toFixed(3);\n if (clip.length === \"trim\") {\n const offset = clip.offset_s ?? 0;\n const start = offset > 0 ? `start=${offset.toFixed(3)}:` : \"\";\n return `${normalize},trim=${start}duration=${len},setpts=PTS-STARTPTS`;\n }\n if (clip.length === \"still\") return `${normalize},trim=duration=${len}`;\n return normalize;\n}\n\n/**\n * The ops that drop a duplicated splice frame for clip `i`, or null. Either way the\n * clip's last frame is clone-padded one frame so its timeline length is unchanged:\n * the audio and overlays are placed at absolute seconds and must never slide.\n */\nfunction seamOps(params: VideoConcatParamsT, i: number): string | null {\n const pad = `tpad=stop_mode=clone:stop_duration=${(1 / params.fps).toFixed(6)}`;\n if (params.seam_dedup === \"head\" && i > 0 && params.clips[i]?.continues_frame)\n return `trim=start_frame=1,setpts=PTS-STARTPTS,${pad}`;\n if (params.seam_dedup === \"tail\" && params.clips[i + 1]?.continues_frame)\n return `reverse,trim=start_frame=1,setpts=PTS-STARTPTS,reverse,${pad}`;\n return null;\n}\n\nfunction inputChain(params: VideoConcatParamsT, i: number): string {\n const seam = seamOps(params, i);\n return `[${i}:v]${clipOps(params.clips[i] as ConcatClipT, params)}${seam ? `,${seam}` : \"\"}[c${i}]`;\n}\n\n/** Input flags placed before a clip's `-i`: a still loops, decoding exactly its length. */\nfunction inputFlags(clip: ConcatClipT): string[] {\n return clip.length === \"still\" ? [\"-loop\", \"1\", \"-t\", inputLengthS(clip).toFixed(3)] : [];\n}\n\n/**\n * Fold the clips left: a blended boundary becomes an `xfade` whose offset lands where\n * the overlap eats the outgoing clip's extra tail, a hard cut a two-input concat.\n */\nfunction blendedJoins(params: VideoConcatParamsT): string[] {\n const { clips } = params;\n const joins: string[] = [];\n let cur = \"c0\";\n let accLen = inputLengthS(clips[0] as ConcatClipT);\n for (let k = 0; k < clips.length - 1; k++) {\n const transition = (clips[k] as ConcatClipT).transition;\n const next = `c${k + 1}`;\n const out = k === clips.length - 2 ? \"v\" : `j${k + 1}`;\n const nextLen = inputLengthS(clips[k + 1] as ConcatClipT);\n if (transition) {\n const offset = Math.max(0, accLen - transition.duration_s);\n joins.push(\n `[${cur}][${next}]xfade=transition=${transition.type}:duration=${transition.duration_s.toFixed(3)}:offset=${offset.toFixed(3)}[${out}]`,\n );\n accLen = accLen - transition.duration_s + nextLen;\n } else {\n joins.push(`[${cur}][${next}]concat=n=2:v=1[${out}]`);\n accLen += nextLen;\n }\n cur = out;\n }\n return joins;\n}\n\n/**\n * The ffmpeg argv the node runs. Pure and deterministic, so the scaffold's timeline\n * and this compilation can be checked without ffmpeg.\n */\nexport function compileVideoConcatArgs(params: VideoConcatParamsT): string[] {\n const inputs = params.clips.flatMap((clip, i) => [...inputFlags(clip), \"-i\", `{{in.c${i}}}`]);\n const chains = params.clips.map((_, i) => inputChain(params, i));\n const joins = blends(params)\n ? blendedJoins(params)\n : [`${params.clips.map((_, i) => `[c${i}]`).join(\"\")}concat=n=${params.clips.length}:v=1:a=0[v]`];\n return [\n ...inputs,\n \"-filter_complex\",\n [...chains, ...joins].join(\";\"),\n \"-map\",\n \"[v]\",\n \"-r\",\n String(params.fps),\n \"-c:v\",\n \"libx264\",\n \"-crf\",\n \"18\",\n \"-preset\",\n \"slow\",\n \"-pix_fmt\",\n \"yuv420p\",\n \"{{out.video}}\",\n ];\n}\n\n/**\n * The joined length and raster, before anything runs. A `trim` or `still` clip's\n * footage is exactly its input length; a `source` clip is as long as what is wired\n * in, so an unsized input leaves the total unknown rather than guessed. Each blended\n * boundary then overlaps by its transition.\n */\nexport function videoConcatTiming(\n params: VideoConcatParamsT,\n inputs: Readonly<Record<string, StaticTiming | undefined>>,\n): StaticTiming {\n const raster = { width: params.width, height: params.height };\n let total = 0;\n for (const [i, clip] of params.clips.entries()) {\n const len = clip.length === \"source\" ? inputs[`c${i}`]?.duration_s : inputLengthS(clip);\n if (len === undefined) return raster;\n total += len;\n }\n if (blends(params)) {\n for (const clip of params.clips.slice(0, -1)) total -= clip.transition?.duration_s ?? 0;\n }\n return { ...raster, duration_s: Math.round(total * 1000) / 1000 };\n}\n\n/** The wired slots a clip list needs, in order: `c0`, `c1`, … */\nfunction expectedSlots(params: VideoConcatParamsT): string[] {\n return params.clips.map((_, i) => `c${i}`);\n}\n\nexport const videoConcatNode = defineNode({\n id: \"video_concat\",\n version: \"1.0.0\",\n category: \"video\",\n location: \"local\",\n summary:\n \"Join clips and stills into one continuous video: every input is normalized onto one raster (size, frame rate, square pixels, pixel format), cut or held to its timeline length, and joined with hard cuts or xfade transitions. The output's length and size are known before it runs.\",\n when_to_use:\n \"Use to assemble a video's picture from its per-scene clips (the scaffold's `spine`). Start here: \" +\n '`{ \"type\": \"video_concat\", \"inputs\": { \"c0\": \"$ref:s0_clip.video\", \"c1\": \"$ref:s1_start.images#0\" }, ' +\n '\"params\": { \"width\": 1080, \"height\": 1920, \"clips\": [ { \"duration_s\": 3.2, \"length\": \"trim\", ' +\n '\"transition\": { \"type\": \"fade\", \"duration_s\": 0.4 } }, { \"duration_s\": 1.5, \"length\": \"still\" } ] } }`. ' +\n \"Wire clip i as `inputs.c<i>`. `length: trim` cuts a longer generated clip to its scene (optionally from \" +\n \"`offset_s`), `still` holds an image, `source` plays the input as rendered. `fit: pad` letterboxes a \" +\n \"screenshot instead of cropping it. `canvas validate` checks the summed length against the timeline, so \" +\n \"edit `duration_s` here, never an ffmpeg argv. Reach for a raw `ffmpeg` node only for a join this cannot express.\",\n inputs: VideoConcatInputs,\n params: VideoConcatParams,\n outputs: VideoConcatOutputs,\n outputKinds: { video: \"video\" },\n cost: () => ({ credits: 0, seconds_estimate: 30 }),\n staticTiming: ({ params, inputs }) => videoConcatTiming(params, inputs),\n\n validateExtra({ rawParams, rawInputs }) {\n const parsed = VideoConcatParams.safeParse(rawParams ?? {});\n if (!parsed.success) return []; // shape errors already surfaced by the param validator\n const wired = Object.keys((rawInputs as Record<string, unknown>) ?? {});\n const expected = expectedSlots(parsed.data);\n const missing = expected.filter((slot) => !wired.includes(slot));\n const extra = wired.filter((slot) => !expected.includes(slot));\n if (missing.length === 0 && extra.length === 0) return [];\n return [\n {\n path: \"inputs\",\n message: `wire exactly one input per clip (${expected.join(\", \")})${missing.length > 0 ? `; missing ${missing.join(\", \")}` : \"\"}${extra.length > 0 ? `; not a clip: ${extra.join(\", \")}` : \"\"}`,\n },\n ];\n },\n\n async cacheKeyExtras() {\n return { ffmpeg_version: await getToolVersion(FFMPEG_BIN) };\n },\n\n async execute({ inputs, params, ctx }) {\n const missing = expectedSlots(params).filter((slot) => !inputs[slot]);\n if (missing.length > 0) throw new Error(`video_concat: no input wired for ${missing.join(\", \")}`);\n return (await runCli({\n bin: FFMPEG_BIN,\n args: compileVideoConcatArgs(params),\n inputs: inputs as Record<string, AssetRef>,\n outputs: { video: { kind: \"video\", ext: \"mp4\" } },\n ctx,\n })) as { video: AssetRef };\n },\n});\n","import { estimateNodeCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef, TextRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst AudioVoiceConvertParams = z\n .object({\n model: z.literal(\"elevenlabs/eleven_multilingual_sts_v2\"),\n /** Target voice id. Splice an upstream `voice_select` via `\"{{voice_ref}}\"`. */\n voice: z.string().min(1),\n output_format: z.string().optional(),\n /** Strip the source clip's background noise before re-voicing. */\n remove_background_noise: z.boolean().optional(),\n })\n .strict();\n\nexport const audioVoiceConvertNode = delegated({\n id: \"audio_voice_convert\",\n version: \"1.0.0\",\n category: \"audio\",\n summary:\n \"Voice Changer / speech-to-speech via ElevenLabs (eleven_multilingual_sts_v2). Re-voices an existing audio clip in a TARGET voice while preserving timing/prosody.\",\n when_to_use:\n 'Use to normalize a generator-chosen voice (e.g. a Seedance talking-head clip\\'s native audio) into ONE consistent brand voice across every scene — the cadence is preserved so any lip-sync stays valid. Wire `inputs.voice_ref: $ref:<voice_select>.voice_id` and set `params.voice: \"{{voice_ref}}\"`.',\n inputs: z\n .object({\n audio: AudioRef,\n voice_ref: TextRef.optional(),\n })\n .strict(),\n params: AudioVoiceConvertParams,\n outputs: z.object({ audio: AudioRef }).strict(),\n outputKinds: { audio: \"audio\" },\n // Priced by the shared contract per minute of source audio. The quote cannot see\n // the clip's length, so it assumes one minute; the charge uses the real length.\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"audio_voice_convert\", params }) ?? 1,\n seconds_estimate: 20,\n }),\n});\n","import { ELEVENLABS_MAX_TEXT_CHARS, ELEVENLABS_OUTPUT_FORMATS, estimateNodeCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef, JsonRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst DialogueInput = z.object({\n text: z.string().min(1),\n voice_id: z.string().min(1),\n});\n\nconst DIALOGUE_MODELS = [\"elevenlabs/eleven_v3\"] as const;\n\nconst DialogueParams = z\n .object({\n model: z.enum(DIALOGUE_MODELS),\n /**\n * Ordered list of lines, each tagged with the voice that should speak it.\n * Up to 10 unique voice_ids; total text across all lines should stay under\n * ~2000 characters for best quality (ElevenLabs guidance).\n */\n inputs: z.array(DialogueInput).min(1).max(50),\n language_code: z.string().optional(),\n /** ElevenLabs voice/model settings passthrough (e.g. `{ stability: 0.5 }`). */\n settings: z.record(z.string(), z.unknown()).optional(),\n seed: z.number().int().min(0).max(4_294_967_295).optional(),\n apply_text_normalization: z.enum([\"auto\", \"on\", \"off\"]).optional(),\n /**\n * When true, hits `/v1/text-to-dialogue/with-timestamps` and emits a\n * separate `timestamps` output — character-level alignment plus\n * per-voice segment markers usable for captions, lipsync, or\n * beat-matched cuts in ad creatives.\n */\n with_timestamps: z.boolean().optional(),\n output_format: z.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),\n })\n .strict()\n // Above this the backend's $10 cost cap always rejects at dispatch.\n .refine((p) => p.inputs.reduce((sum, line) => sum + line.text.length, 0) <= ELEVENLABS_MAX_TEXT_CHARS, {\n message: `total dialogue text exceeds ${ELEVENLABS_MAX_TEXT_CHARS} characters`,\n path: [\"inputs\"],\n });\n\nexport const dialogueNode = delegated({\n id: \"dialogue\",\n version: \"1.1.0\",\n category: \"audio\",\n summary:\n \"Multi-voice dialogue / VO with ElevenLabs Eleven v3. Each line is tagged with a `voice_id`, so you can render two-character scripts (e.g. ad VO + customer testimonial reaction) in a single call. Setting `with_timestamps: true` adds character-level alignment for caption rendering and lipsync-friendly cuts.\",\n when_to_use:\n \"Use for any ad creative or website video VO that needs more than narration — interviews, two-actor scripts, character ads, testimonial reads. For single-voice flat reads the existing `tts` node is cheaper and simpler; reach for `dialogue` when you need multiple speakers in one stitched track or word-level timing for downstream lipsync / captions.\",\n inputs: z.object({}).loose(),\n params: DialogueParams,\n outputs: z.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),\n outputKinds: { audio: \"audio\", timestamps: \"json\" },\n // Priced by the shared contract, the same per-character rate the backend charges.\n cost: ({ params }) => ({ credits: estimateNodeCredits({ nodeType: \"dialogue\", params }) ?? 1, seconds_estimate: 8 }),\n});\n","import {\n AD_FORMAT_PLATFORMS,\n type AdFormatPlatform,\n imagePictureCredits,\n OPENROUTER_IMAGE_SIZES,\n resolveAdaptFormats,\n} from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { ASPECT_ADAPT_FORMATS } from \"../../scaffold/static-sizes.ts\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\n// GA ids first; the preview ids stay accepted so canvases written with them re-run.\nconst ASPECT_ADAPT_MODELS = [\n \"google/gemini-3-pro-image\",\n \"google/gemini-3.1-flash-image\",\n \"google/gemini-3-pro-image-preview\",\n \"google/gemini-3.1-flash-image-preview\",\n] as const;\n\n// The adaptation prompt is baked into the backend handler (consistency-first,\n// direction-aware recomposition); `guidance` appends hints, never replaces it.\n// Targets come from EITHER an explicit `formats` list OR a `platform` preset\n// (Meta / LinkedIn / Google / TikTok / YouTube) that expands to the canonical\n// on-grid set — exactly one of the two, enforced here (the registry can't).\nconst ImageAspectAdaptParams = z\n .object({\n model: z.enum(ASPECT_ADAPT_MODELS),\n formats: z\n .array(z.enum(ASPECT_ADAPT_FORMATS))\n .min(1)\n .max(6)\n .refine((formats) => new Set(formats).size === formats.length, { message: \"formats must be unique\" })\n .optional(),\n platform: z.enum(AD_FORMAT_PLATFORMS).optional(),\n guidance: z.string().min(1).optional(),\n // The exact lines the ad carries: the reframe keeps each, spelled as given, once.\n copy: z.array(z.string().min(1)).min(1).max(20).optional(),\n // Per target format, the fractions of each edge the platform's interface covers: words,\n // logo and button stay inside. The static scaffold sets both from prompt.json and `staticSafeArea`.\n safe_areas: z\n .record(\n z.string(),\n z.object({ top: z.number(), bottom: z.number(), left: z.number(), right: z.number() }).strict(),\n )\n .optional(),\n image_size: z.enum(OPENROUTER_IMAGE_SIZES).optional(),\n })\n .strict()\n .refine((p) => (p.formats === undefined) !== (p.platform === undefined), {\n message: \"provide exactly one of `formats` (explicit ratios) or `platform` (a placement preset)\",\n path: [\"formats\"],\n });\n\nexport const imageAspectAdaptNode = delegated({\n id: \"image_aspect_adapt\",\n version: \"1.0.0\",\n category: \"image\",\n summary:\n \"Adapt ONE creative into multiple aspect ratios (Meta: 9:16 stories, 1:1 feed, 4:5, 16:9…) in a single step. Give it either an explicit `formats` list OR a `platform` preset (meta_feed, meta_feed_stories, meta_all, linkedin, google_demandgen, tiktok, youtube) that expands to that platform's canonical set. AI recomposes the layout per format — identical subject, text, logos, colors, and style; the scene is extended/restructured, never stretched or cropped. Formats that already match the source ratio pass through unchanged at zero cost. Outputs are ordered exactly as the resolved format list.\",\n when_to_use:\n \"Use after a hero creative exists (image_generate, ingest, image_search) to fan it out to every placement format — wire the creative into `source` and set EITHER `formats` (exact ratios) OR `platform` (e.g. `meta_feed_stories`). Cost is estimated per format; formats matching the source ratio are free pass-throughs. Off-grid platform targets (e.g. Google 1.91:1) are not rendered here — crop them from the nearest ratio with `baker images crop`. Pick `google/gemini-3.1-flash-image` (Nano Banana 2) while iterating, `google/gemini-3-pro-image` (Nano Banana Pro) for final-quality adaptation.\",\n inputs: z.object({ source: ImageRef }).loose(),\n params: ImageAspectAdaptParams,\n outputs: z.object({ images: z.array(ImageRef).min(1) }).strict(),\n outputKinds: { images: \"image\" },\n cost: ({ params }) => {\n const p = params as { model?: string; formats?: string[]; platform?: AdFormatPlatform } | undefined;\n const count = Math.max(1, resolveAdaptFormats(p ?? {}).length);\n return {\n credits: count * imagePictureCredits(String(p?.model)),\n seconds_estimate: 30 + 30 * count,\n };\n },\n});\n","import { estimateNodeCredits, modelIdsAccepted } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst ImageBackgroundRemoveParams = z\n .object({\n // `fal/birefnet-v2` is the legacy id stored canvases carry. It stays the default for one\n // release: every backend accepts it, while `replicate/birefnet` needs the backend that ships with\n // this CLI, and the two deploy independently.\n model: z.enum(modelIdsAccepted(\"replicate/birefnet\")).optional().default(\"fal/birefnet-v2\"),\n operating_resolution: z.enum([\"1024x1024\", \"2048x2048\", \"2304x2304\"]).optional(),\n // DEPRECATED, accepted so stored canvases still parse, and ignored: the model this\n // node runs (`men1scus/birefnet`) takes only an image and a resolution, has no\n // variant and returns no mask. `mask_only: true` still returns the cutout.\n model_variant: z\n .enum([\"General Use (Light)\", \"General Use (Heavy)\", \"Matting\", \"Portrait\", \"DIS\", \"HRSOD\", \"COD\"])\n .optional()\n .meta({ deprecated: true, description: \"Ignored: the model has no variants.\" }),\n mask_only: z\n .boolean()\n .optional()\n .meta({ deprecated: true, description: \"Ignored: the model returns the cutout only, never a mask.\" }),\n })\n .strict();\n\nexport const imageBackgroundRemoveNode = delegated({\n id: \"image_background_remove\",\n version: \"1.0.0\",\n category: \"image\",\n summary:\n \"Remove the background from an image and return a transparent PNG. Powered by BiRefNet (`men1scus/birefnet` on Replicate).\",\n when_to_use:\n \"Use to extract subjects from photos for use as overlays in hyperframe compositions, product shots, or compositing pipelines. \" +\n \"It returns the alpha-cut image only; there is no mask output and no model variant to pick.\",\n inputs: z\n .object({\n image: ImageRef,\n })\n .strict(),\n params: ImageBackgroundRemoveParams,\n outputs: z.object({ image: ImageRef }).strict(),\n outputKinds: { image: \"image\" },\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"image_background_remove\", params }) ?? 1,\n seconds_estimate: 5,\n }),\n});\n","import { z } from \"zod\";\nimport { ImageRef, JsonRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst IMAGE_DESCRIBE_MODELS = [\"~google/gemini-pro-latest\", \"~google/gemini-flash-latest\"] as const;\n\nexport const imageDescribeNode = delegated({\n id: \"image_describe\",\n version: \"1.3.0\",\n category: \"vision\",\n summary:\n \"Reverse-engineer an image into an exhaustive, replication-grade JSON description: who the advertiser is and what they sell (source_context), composition, non-person subjects with expression/treatment, deeply detailed people, brand-identified logos (named by brand, not appearance), camera optics, lighting, color palette WITH per-color brand-ownership (brand vs borrowed-functional) and purpose, materials, visible text, ad signals (proof badges/CTA/price), the persuasion engine (ad_intent), style, post-processing.\",\n when_to_use:\n 'Use to turn a reference image into a structured blueprint you can inject into downstream prompts via `{{slot}}` — e.g. restyle a competitor ad onto your own product, lock a look across a series, or feed exact palette/lighting into image_generate. Purpose-built for market adaptation: logos are identified by brand (\"Trustpilot\", never \"green star\"), people and animals carry expression/emotion/intent detail, and each color is tagged brand vs borrowed-functional so a recolor can keep the reds/yellows that do a job. The extraction prompt is baked in; use `focus` to emphasise aspects and `context` to pass known provenance (advertiser, category, market) so source_context and color ownership are grounded. Pick `~google/gemini-pro-latest` for the densest extraction (recommended for ad / market-adaptation passes), `~google/gemini-flash-latest` for cheap/fast passes. The output is rich — raise `max_tokens` (e.g. 8000+) for dense ads so the JSON isn\\'t truncated.',\n inputs: z.object({ image: ImageRef }).loose(),\n params: z\n .object({\n model: z.enum(IMAGE_DESCRIBE_MODELS),\n focus: z.string().optional(),\n context: z.string().optional(),\n temperature: z.number().min(0).max(2).optional(),\n max_tokens: z.number().int().positive().optional(),\n })\n .strict(),\n outputs: z.object({ description: JsonRef }).strict(),\n outputKinds: { description: \"json\" },\n cost: () => ({ credits: 2, seconds_estimate: 10 }),\n});\n","import { z } from \"zod\";\nimport { ImageRef, JsonRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst ImageLayerizeParams = z\n .object({\n /**\n * What to separate. Read as extraction instructions, not as a generation\n * prompt — \"keep the bottle and its shadow as one layer\", not \"make it blue\".\n */\n instructions: z.string().max(600).optional(),\n })\n .strict();\n\n/**\n * Three outputs rather than one, because the useful thing here is plural.\n *\n * `layers` is the answer to \"give me this picture in layers\": an array of\n * transparent PNGs, bottom to top, each placeable on its own. `base` is the flat\n * picture kept underneath, so a composition of the layers always covers the\n * frame — the model separates what was VISIBLE and does not repaint what sat\n * behind a subject, so the cutouts alone leave gaps. `manifest` carries what the\n * pixels cannot: each layer's box, and for recovered copy its exact wording,\n * font, size and colour, which is what a hyperframe composition needs to place a\n * layer or re-set a headline in the original's type.\n *\n * Shaped after `video_deconstruct`, which emits a frames array plus a transcript\n * JSON for the same reason.\n */\nexport const imageLayerizeNode = delegated({\n id: \"image_layerize\",\n version: \"1.0.0\",\n category: \"image\",\n summary:\n \"Split an image into editable layers: a transparent PNG per element, plus any copy baked into the picture recovered as real text with its typography. Powered by Seedream 5.0 Pro Layerize, with Ideogram lifting the text off first.\",\n when_to_use:\n \"Use when you need ONE element of a picture on its own (a product with no background, a logo lockup), or when a shot is right but its copy is wrong and regenerating would change the picture too. \" +\n \"The manifest gives you each layer's box and, for text, its exact wording, font, size and colour — enough to re-set a headline in the original's type. \" +\n \"For a plain subject cutout with no text involved, `image_background_remove` is cheaper and one call.\",\n inputs: z\n .object({\n image: ImageRef,\n })\n .strict(),\n params: ImageLayerizeParams,\n outputs: z\n .object({\n layers: z.array(ImageRef),\n base: ImageRef,\n manifest: JsonRef,\n })\n .strict(),\n outputKinds: { layers: \"image\", base: \"image\", manifest: \"json\" },\n // Billed per returned layer (2-17) plus an optional text pass; the backend\n // charges the real count on completion. This is the pre-run estimate.\n cost: () => ({ credits: 30, seconds_estimate: 100 }),\n});\n","import { imagePictureCredits, OPENROUTER_IMAGE_SIZES } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\n// GA ids first; the preview ids stay accepted so canvases written with them re-run.\nconst REFERENCE_SHEET_MODELS = [\n \"google/gemini-3-pro-image\",\n \"google/gemini-3.1-flash-image\",\n \"google/gemini-3-pro-image-preview\",\n \"google/gemini-3.1-flash-image-preview\",\n] as const;\n\n// Permissive union of every param accepted by any curated model. The model\n// registry (`@baker/canvas-contract`) is the strict per-model gate. The sheet\n// prompt itself is baked into the backend handler — `prompt_override` is the\n// only full-bypass lever.\nconst ImageReferenceSheetParams = z\n .object({\n model: z.enum(REFERENCE_SHEET_MODELS),\n subject_description: z.string().min(1),\n // `location` = a set/room shown from several camera ANGLES (not a rotated subject),\n // so a multi-scene shoot keeps one consistent set.\n subject_type: z.enum([\"character\", \"person\", \"product\", \"location\"]),\n views: z.array(z.string().min(1)).min(2).max(8).optional(),\n style: z.string().optional(),\n prompt_override: z.string().min(1).optional(),\n aspect_ratio: z\n .enum([\"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"4:5\", \"5:4\", \"21:9\", \"1:4\", \"4:1\", \"1:8\", \"8:1\"])\n .optional(),\n image_size: z.enum(OPENROUTER_IMAGE_SIZES).optional(),\n })\n .strict();\n\nexport const imageReferenceSheetNode = delegated({\n id: \"image_reference_sheet\",\n version: \"1.0.0\",\n category: \"image\",\n summary:\n \"Fuse 1–6 images of a single subject (person, character, product, or location/set) into ONE multi-view reference sheet — a labeled grid in consistent style and lighting: a turnaround (FRONT / SIDE / BACK…) for a person/character/product, or several camera angles of the same room (WIDE / REVERSE / DETAIL…) for a location. Curated models: Gemini 3 Pro Image (best fusion + labels), Gemini 3.1 Flash Image (cheap iteration).\",\n when_to_use:\n \"Use before image_generate / video_generate when a subject must stay consistent across many creatives — wire the `sheet` output into their `reference` input instead of re-describing the subject per prompt. `subject_description` should be the exact wording you reuse downstream. Pick `google/gemini-3-pro-image` for final 6-view sheets at 2K+, `google/gemini-3.1-flash-image` while iterating.\",\n inputs: z.object({ references: z.array(ImageRef).min(1).max(6) }).loose(),\n params: ImageReferenceSheetParams,\n outputs: z.object({ sheet: ImageRef }).strict(),\n outputKinds: { sheet: \"image\" },\n cost: ({ params }) => ({\n credits: imagePictureCredits(String((params as { model?: string } | undefined)?.model)),\n seconds_estimate: 30,\n }),\n});\n","import { z } from \"zod\";\nimport { ImageRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst ImageSearchParams = z\n .object({\n prompt: z.string().min(1),\n count: z.number().int().min(1).max(20).default(5),\n })\n .strict();\n\nexport const imageSearchNode = delegated({\n id: \"image_search\",\n version: \"1.0.0\",\n category: \"image\",\n summary:\n \"Agentic image search across Google Images, free stock photography (Pexels and Pixabay), and Pinterest. An LLM agent picks the search tools and queries, selects the best matches, and the results are downloaded into canvas assets.\",\n when_to_use:\n \"Use to gather real-world reference or inspiration images for a prompt (e.g. several photos of an australian shepherd) so a later step or the user can pick the best one. Not for creating new imagery — use image_generate for that.\",\n inputs: z.object({}).loose(),\n params: ImageSearchParams,\n outputs: z.object({ images: z.array(ImageRef).min(1) }).strict(),\n outputKinds: { images: \"image\" },\n cost: ({ params }) => ({ credits: Math.ceil(2 + params.count / 2), seconds_estimate: 30 }),\n});\n","import { z } from \"zod\";\nimport { ImageRef, TextRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst IMAGE_SELECT_MODELS = [\"~google/gemini-flash-latest\", \"~google/gemini-pro-latest\"] as const;\n\nexport const imageSelectNode = delegated({\n id: \"image_select\",\n version: \"1.0.0\",\n category: \"vision\",\n summary:\n \"Pick the best `count` images out of the candidates with a vision LLM, judged against a prompt. Outputs a passthrough subset of the input refs (no new pixels) plus the model's comparative reasoning. A single candidate passes through unjudged, free and without reasoning.\",\n when_to_use:\n \"Use after an image_generate run with `n` > 1 (wire its whole `images` array), several image_generate variants, or any pool of images, to keep only the strongest before expensive downstream steps — video generation, reference sheets, final delivery. `count` fixes the output size, so `images#0`…`images#count-1` are always safe to wire. Pick `~google/gemini-flash-latest` for cheap/fast picks and `~google/gemini-pro-latest` for harder aesthetic judgement.\",\n inputs: z.object({ images: z.array(ImageRef).min(1) }).loose(),\n params: z\n .object({\n model: z.enum(IMAGE_SELECT_MODELS),\n prompt: z.string().min(1),\n count: z.number().int().min(1).default(1),\n temperature: z.number().min(0).max(2).optional(),\n max_tokens: z.number().int().positive().optional(),\n })\n .strict(),\n // No `reasoning` when a lone candidate passed through: there was no judgement to explain.\n outputs: z.object({ images: z.array(ImageRef).min(1), reasoning: TextRef.optional() }).strict(),\n outputKinds: { images: \"image\", reasoning: \"text\" },\n cost: () => ({ credits: 1, seconds_estimate: 5 }),\n // Arity is only knowable at validate time when `images` is a literal array\n // of refs; a single `$ref:` string to an upstream array output is deferred\n // to the runtime Zod parse and the backend's own arity check.\n validateExtra: ({ rawParams, rawInputs }) => {\n const issues: Array<{ path: string; message: string }> = [];\n const images = (rawInputs as { images?: unknown } | undefined)?.images;\n if (!Array.isArray(images)) return issues;\n if (images.length === 0) {\n issues.push({ path: \"inputs.images\", message: \"image_select needs at least 1 image wired\" });\n }\n const count = (rawParams as { count?: unknown } | undefined)?.count;\n if (typeof count === \"number\" && count > images.length) {\n issues.push({\n path: \"params.count\",\n message: `count ${count} exceeds the ${images.length} images wired into \\`images\\``,\n });\n }\n return issues;\n },\n});\n","import {\n DEFAULT_MUSIC_SECONDS,\n ELEVENLABS_MAX_MUSIC_LENGTH_MS,\n ELEVENLABS_OUTPUT_FORMATS,\n estimateNodeCredits,\n} from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef, JsonRef, VideoRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst MUSIC_MODELS = [\"elevenlabs/music-v1\", \"elevenlabs/video-background-music-v1\"] as const;\n\nconst MusicParams = z\n .object({\n model: z.enum(MUSIC_MODELS),\n /** Free-form prompt. Used by `elevenlabs/music-v1` (compose-detailed). */\n prompt: z.string().optional(),\n /**\n * Structured composition plan (intro / hook / verse / outro sections with\n * per-section styles + durations). Mutually exclusive with `prompt`.\n */\n composition_plan: z.record(z.string(), z.unknown()).optional(),\n /** Target length when using `prompt`. 3000–454545ms (capped by the $10 per-node cost limit). */\n music_length_ms: z.number().int().min(3000).max(ELEVENLABS_MAX_MUSIC_LENGTH_MS).optional(),\n seed: z.number().int().optional(),\n /** Prompt mode only — forces an instrumental (no vocals) track. */\n force_instrumental: z.boolean().optional(),\n /** composition_plan only — honor exact section durations. */\n respect_sections_durations: z.boolean().optional(),\n /** Emit word-level timestamps alongside the audio. */\n with_timestamps: z.boolean().optional(),\n /**\n * video-to-music only — short description of the desired score\n * (\"upbeat synth, fast cuts, 80s\") used to bias the model.\n *\n * The `describe` text below is the one piece of craft this node can enforce\n * nowhere else: a bed under somebody talking must be ONE steady character.\n * A description that narrated the speaker's emotional arc (\"quiet and tense\n * while she is overwhelmed, opening as she walks, resolving warm and\n * confident\") came back as a film score and the client's verdict was \"la\n * musica que has elegido es muy dramática\".\n */\n description: z\n .string()\n .max(1000)\n .optional()\n .describe(\n \"The score you want, in sound (instruments, tempo, texture) — not in feelings. Under speech, name ONE \" +\n \"steady character and forbid movement: no build, no drop, no climax, no key change, nothing that \" +\n \"'follows the arc'. This model WATCHES the cut, so it will score the footage's emotion unless told \" +\n \"not to — asking it to track a speaker's emotional arc is how a testimonial bed comes back dramatic. \" +\n \"A bed is furniture; the voice carries the story.\",\n ),\n /** video-to-music only — up to 10 style tags. */\n tags: z\n .array(z.string())\n .max(10)\n .optional()\n .describe(\n \"Style tags. Under speech, prefer tags that hold it still (instrumental, understated, steady, no drums) \" +\n \"over tags that move it (cinematic, epic, building, resolving, trailer).\",\n ),\n output_format: z.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),\n })\n .strict();\n\nexport const musicNode = delegated({\n id: \"music\",\n // 1.2.0: the ElevenLabs engine moved to `music_v2` (ELEVENLABS_MUSIC_ENGINE).\n // The bump is not cosmetic — nodeVersion is part of the canvas cache key, so\n // without it a re-run would serve the v1 bed the switch was made to replace.\n version: \"1.2.0\",\n category: \"audio\",\n summary:\n \"Generate music for ad creatives and website video content. `elevenlabs/music-v1` composes from a text prompt or structured composition plan; `elevenlabs/video-background-music-v1` scores an existing video clip provided via `inputs.video`.\",\n when_to_use:\n \"Use to produce background music or a full score for video ads, hero-section reels, or any motion content. Prefer the video-to-music model when you already have a cut and want music timed to it; use compose-detailed when you have only a brief or want section-level control (intro / hook / outro). Pair the resulting audio with `video_generate` or `video_lipsync` at compose time.\",\n inputs: z.object({ video: VideoRef.optional() }).loose(),\n params: MusicParams,\n outputs: z.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),\n outputKinds: { audio: \"audio\", timestamps: \"json\" },\n // Priced by the shared contract: per second of audio, 30s when no length is pinned.\n cost: ({ params }) => {\n const seconds = params.music_length_ms ? Math.ceil(params.music_length_ms / 1000) : DEFAULT_MUSIC_SECONDS;\n return { credits: estimateNodeCredits({ nodeType: \"music\", params }) ?? 1, seconds_estimate: seconds + 15 };\n },\n // Per-model semantics the registry can't express: compose needs exactly one\n // of prompt/composition_plan, and a video wired into compose is dead weight.\n validateExtra: ({ rawParams, rawInputs }) => {\n const issues: Array<{ path: string; message: string }> = [];\n const params = (rawParams ?? {}) as { model?: unknown; prompt?: unknown; composition_plan?: unknown };\n if (params.model !== \"elevenlabs/music-v1\") return issues;\n const hasPrompt = params.prompt !== undefined;\n const hasPlan = params.composition_plan !== undefined;\n if (hasPrompt === hasPlan) {\n issues.push({\n path: \"params\",\n message: \"elevenlabs/music-v1 needs exactly one of `prompt` or `composition_plan`\",\n });\n }\n if ((rawInputs as { video?: unknown } | undefined)?.video !== undefined) {\n issues.push({\n path: \"inputs.video\",\n message:\n \"elevenlabs/music-v1 ignores `inputs.video` — use elevenlabs/video-background-music-v1 to score a video\",\n });\n }\n return issues;\n },\n});\n","import { DEFAULT_SOUND_EFFECT_SECONDS, ELEVENLABS_OUTPUT_FORMATS, estimateNodeCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst SOUND_EFFECT_MODELS = [\"elevenlabs/eleven_text_to_sound_v2\"] as const;\n\nconst SoundEffectParams = z\n .object({\n model: z.enum(SOUND_EFFECT_MODELS),\n /** Prompt describing the SFX (\"metal door slam\", \"soft UI tap\", \"ocean waves\"). */\n text: z.string().min(1),\n /**\n * Target length in seconds. 0.5–30. Leave unset to let the model pick the\n * natural length for the described effect.\n */\n duration_seconds: z.number().min(0.5).max(30).optional(),\n /**\n * 0–1. Higher = stick closer to the prompt at the cost of variety; lower\n * = let the model interpret more freely. Defaults to 0.3 on the provider.\n */\n prompt_influence: z.number().min(0).max(1).optional(),\n /** Only valid on `eleven_text_to_sound_v2` — produce a seamless loop. */\n loop: z.boolean().optional(),\n output_format: z.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),\n })\n .strict();\n\nexport const soundEffectNode = delegated({\n id: \"sound_effect\",\n version: \"1.1.0\",\n category: \"audio\",\n summary:\n \"Generate short sound effects from a text prompt via ElevenLabs Text-to-Sound. Use for whooshes, impacts, UI clicks, ambient beds, or signature stingers in ad creatives and product videos.\",\n when_to_use:\n \"Reach for this when you need a punch-in SFX layered against `video_generate` or `hyperframe_render` output — e.g. a logo whoosh on a hero shot, a click on a CTA cut, a swelling ambient bed under VO. Set `loop: true` for atmospheric beds that need to tile under longer footage; leave `duration_seconds` unset and the model picks a natural length.\",\n inputs: z.object({}).loose(),\n params: SoundEffectParams,\n outputs: z.object({ audio: AudioRef }).strict(),\n outputKinds: { audio: \"audio\" },\n // Priced by the shared contract: per second of audio, 5s when the model picks the length.\n cost: ({ params }) => {\n const seconds = params.duration_seconds ?? DEFAULT_SOUND_EFFECT_SECONDS;\n return { credits: estimateNodeCredits({ nodeType: \"sound_effect\", params }) ?? 1, seconds_estimate: seconds + 5 };\n },\n});\n","import { z } from \"zod\";\nimport { TextRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst TEXT_GENERATE_MODELS = [\"~google/gemini-flash-latest\", \"~google/gemini-pro-latest\"] as const;\n\nexport const textGenerateNode = delegated({\n id: \"text_generate\",\n version: \"2.2.0\",\n category: \"language\",\n summary: \"Single-turn LLM text generation via OpenRouter. Returns a text response.\",\n when_to_use:\n 'Use for any short text generation step in a canvas — ad copy, hooks, headlines, JSON outputs for downstream nodes. Pick `~google/gemini-flash-latest` for cheap/fast work and `~google/gemini-pro-latest` for harder reasoning. When the output must be JSON for a downstream `{{slot}}` (e.g. the ad-blueprint transform), set `response_format: \"json_object\"` so the model returns clean JSON with no markdown fences or prose. Set `web_search: true` to let the model search the live web first (OpenRouter `:online`) — useful when the transform must adapt copy to the target brand\\'s real facts (current pricing, the trust signals it actually has) rather than guess.',\n inputs: z.object({}).loose(),\n params: z\n .object({\n model: z.enum(TEXT_GENERATE_MODELS),\n prompt: z.string().min(1),\n system: z.string().optional(),\n response_format: z.enum([\"text\", \"json_object\"]).optional(),\n web_search: z.boolean().optional(),\n temperature: z.number().min(0).max(2).optional(),\n max_tokens: z.number().int().positive().optional(),\n })\n .strict(),\n outputs: z.object({ text: TextRef }).strict(),\n outputKinds: { text: \"text\" },\n cost: () => ({ credits: 1, seconds_estimate: 3 }),\n});\n","import { ELEVENLABS_MAX_TEXT_CHARS, ELEVENLABS_OUTPUT_FORMATS, estimateNodeCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef, JsonRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst TTS_MODELS = [\"elevenlabs/eleven_v3\"] as const;\n\n/**\n * ElevenLabs voice settings passthrough. Mirrors the provider's\n * `voice_settings` object.\n */\nconst TtsVoiceSettings = z\n .object({\n stability: z.number().min(0).max(1).optional(),\n similarity_boost: z.number().min(0).max(1).optional(),\n style: z.number().min(0).max(1).optional(),\n use_speaker_boost: z.boolean().optional(),\n speed: z.number().min(0.25).max(4.0).optional(),\n })\n .strict();\n\nconst TtsPronunciationLocator = z\n .object({\n pronunciation_dictionary_id: z.string().min(1),\n version_id: z.string().nullable().optional(),\n })\n .strict();\n\nconst TtsParams = z\n .object({\n model: z.enum(TTS_MODELS),\n text: z.string().min(1).max(ELEVENLABS_MAX_TEXT_CHARS),\n voice: z.string().min(1),\n\n /** Provider output_format (mp3 family only — assets are stored as audio/mpeg). */\n output_format: z.enum(ELEVENLABS_OUTPUT_FORMATS).optional(),\n seed: z.number().int().min(0).max(4_294_967_295).optional(),\n\n // Top-level shortcuts; structured form is `voice_settings`.\n stability: z.number().min(0).max(1).optional(),\n similarity_boost: z.number().min(0).max(1).optional(),\n voice_settings: TtsVoiceSettings.optional(),\n\n /** ISO 639-1 language code. eleven_v3 supports language hints. */\n language_code: z.string().optional(),\n pronunciation_dictionary_locators: z.array(TtsPronunciationLocator).max(3).optional(),\n apply_text_normalization: z.enum([\"auto\", \"on\", \"off\"]).optional(),\n /** Currently Japanese-only. Adds latency. */\n apply_language_text_normalization: z.boolean().optional(),\n\n /**\n * When true, hits `/v1/text-to-speech/{voice_id}/with-timestamps` and\n * adds a `timestamps` output (character-level alignment) for caption\n * rendering, lipsync, and beat-matched cuts.\n */\n with_timestamps: z.boolean().optional(),\n })\n .strict();\n\nexport const ttsNode = delegated({\n id: \"tts\",\n version: \"2.0.0\",\n category: \"audio\",\n summary:\n \"Single-voice text-to-speech via ElevenLabs Eleven v3. Optional character-level timestamps for caption rendering and beat-matched cuts.\",\n when_to_use:\n \"Use for single-speaker VO — ad reads, hero-section narration, product walkthroughs. Reach for `dialogue` when you need multiple voices in one stitched track. Set `with_timestamps: true` when downstream needs character-level alignment (captions, lipsync).\",\n inputs: z.object({}).loose(),\n params: TtsParams,\n outputs: z.object({ audio: AudioRef, timestamps: JsonRef.optional() }).strict(),\n outputKinds: { audio: \"audio\", timestamps: \"json\" },\n // Priced by the shared contract, the same per-character rate the backend charges.\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"tts\", params }) ?? 1,\n seconds_estimate: params.with_timestamps ? 7 : 5,\n }),\n});\n","import { estimateNodeCredits, VIDEO_GENERATE_MODELS } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { ImageRef, VideoRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\nimport { registryParamShape } from \"./registry-params.ts\";\n\n// The accepted model set is the canonical curated roster from the contract — kept\n// in one place so this node schema can never drift from `MODEL_REGISTRY` (which is\n// exactly the drift that let a registered model fail this schema before it).\nconst videoModelEnum = z.enum(VIDEO_GENERATE_MODELS as unknown as [string, ...string[]]);\n\n// Permissive union of every param accepted by any curated model, derived from the\n// registry (`registryParamShape`). The registry gates per-model: e.g. Seedance rejects\n// `negative_prompt`, Veo accepts it, Kling takes `cfg_scale`, only Seedance 2.5 takes\n// `watermark`. Enum values for `duration`/`resolution` are also gated there.\nconst VideoGenerateParams = z\n .object({\n model: videoModelEnum,\n ...registryParamShape(\"video_generate\", { prompt: z.string().min(1) }),\n })\n .strict();\n\nexport const videoGenerateNode = delegated({\n id: \"video_generate\",\n version: \"2.0.0\",\n category: \"video\",\n summary:\n \"Generate video for ad creatives. Curated roster: `google/gemini-omni-1.1-flash` (DEFAULT — native audio + reference images in one call, 360p-4k, 3-10s), `bytedance/seedance-2.0` (identity/product workhorse, NO photoreal human faces), `google/veo-3.1` (photoreal cine ceiling + the human-face model), `google/veo-3.1-fast` (cheap Veo iteration), `kwaivgi/kling-v3.0-pro` (motion-transfer/dynamic). Async with polling.\",\n when_to_use:\n \"`google/gemini-omni-1.1-flash` is the DEFAULT: it renders picture and synchronized audio in one call, and is the only model here that takes reference images alongside a prompt. SET `duration` (3-10s) to the length the beat actually needs — it bills PER SECOND, so leaving it out renders the 10s default and charges for ten seconds of a beat that may need three. SET `resolution` too: `360p` bills a THIRD of `720p` ($0.034 vs $0.101 per second) and is the right tier while you are still choosing between takes — render the drafts at 360p, then re-render the keeper at `720p` (default), `1080p` ($0.152/s) or `4k` ($0.304/s). It accepts only `16:9` or `9:16`, and takes NO `seed`, `negative_prompt` or `generate_audio` param — sending one fails validation, and it always renders audio. Keep the default for ordinary beats; move off it only when a beat needs a length outside 3-10s or a 1:1 frame. Use `bytedance/seedance-2.0` for identity/product output WITHOUT a photoreal human face on camera. Any clip whose frame shows a photoreal human face — a UGC creator, presenter, or testimonial speaker, REAL OR AI-GENERATED — must use `google/veo-3.1`: ByteDance's filter rejects photoreal human faces on Seedance (content_policy_blocked/E005), and a synthetic AI face does NOT clear it. Use `google/veo-3.1-fast` while iterating to keep cost low; `kwaivgi/kling-v3.0-pro` for motion-transfer/hyper-dynamic beats. `baker canvas scaffold-video` auto-routes a photoreal cast to Veo. A Seedance clip that is refused for a face at runtime is NOT moved to another model: one guided only by reference images is re-rendered on the same model through a second route that accepts faces (about twice the price), and one that starts from a frame comes back as content_policy_blocked — re-run that clip on `google/veo-3.1`. Each model gates its own durations, resolutions and aspect ratios in the registry: `baker studio models --kind video --model <id>` shows them.\",\n inputs: z\n .object({\n first_frame: ImageRef.optional(),\n last_frame: ImageRef.optional(),\n reference: ImageRef.optional(),\n /**\n * Reference-to-video: several images the model uses as visual guidance\n * (this person, this product) instead of an exact opening frame. Mutually\n * exclusive with `first_frame` — OpenRouter treats a request carrying both\n * as image-to-video and ignores these, so wire one or the other.\n */\n references: z.array(ImageRef).optional(),\n })\n .loose(),\n params: VideoGenerateParams,\n outputs: z.object({ video: VideoRef }).strict(),\n outputKinds: { video: \"video\" },\n // Priced from the shared contract, so `validate`'s quote and the charge the\n // backend applies come from ONE table. A flat number here under-quoted a\n // 1080p 8s clip by ~10x, and the agent reads this out to the user as \"this\n // will cost N\" before spending it.\n // Reads model, duration, resolution and generate_audio (audio bills at a separate,\n // higher rate on every model that offers it, so omitting it quotes half the cost).\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"video_generate\", params }) ?? 1,\n seconds_estimate: 120,\n }),\n});\n","import { estimateNodeCredits, modelIdsAccepted } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { VideoRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst VideoBackgroundRemoveParams = z\n .object({\n // `fal/veed-video-background-removal` is the legacy id stored canvases carry. It stays the default for one\n // release: every backend accepts it, while `replicate/birefnet-video` needs the backend that ships with\n // this CLI, and the two deploy independently.\n model: z.enum(modelIdsAccepted(\"replicate/birefnet-video\")).optional().default(\"fal/veed-video-background-removal\"),\n // Refines the matte's soft edges against background bleed (the model's `refine_fg`).\n edge_refinement: z.boolean().optional().default(true),\n output_codec: z.enum([\"vp9\", \"h264\"]).optional().default(\"vp9\"),\n })\n .strict();\n\nexport const videoBackgroundRemoveNode = delegated({\n id: \"video_background_remove\",\n version: \"1.0.0\",\n category: \"video\",\n summary:\n \"Remove the background from a video and return a transparent VP9-with-alpha WebM (or H264 RGB+alpha pair). Drops directly into a hyperframe composition as `<video src='...'>` for chroma-keyed picture-in-picture overlays. Powered by BiRefNet Video (`sprited/birefnet-video` on Replicate).\",\n when_to_use:\n \"Use when you need a talking-head or subject to float over a custom background in a hyperframe composition. Pair with hyperframe_render(composition: screencast-with-talker) for screencast-with-narrator videos. \" +\n \"Output is `video/webm` with alpha — feed straight into `<video src>` in a composition.\",\n inputs: z\n .object({\n video: VideoRef,\n })\n .strict(),\n params: VideoBackgroundRemoveParams,\n outputs: z.object({ video: VideoRef }).strict(),\n outputKinds: { video: \"video\" },\n // Priced by the shared contract per 30 frames; the quote assumes the same 30s of\n // footage the charge falls back to when the clip carries no duration.\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"video_background_remove\", params }) ?? 1,\n seconds_estimate: 60,\n }),\n});\n","import { z } from \"zod\";\nimport { ImageRef, JsonRef, VideoRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nconst VIDEO_DECONSTRUCT_MODELS = [\"~google/gemini-flash-latest\", \"~google/gemini-pro-latest\"] as const;\n\nexport const videoDeconstructNode = delegated({\n id: \"video_deconstruct\",\n version: \"1.4.0\",\n category: \"video\",\n summary:\n 'Deconstruct a video into a replication-grade blueprint: scene boundaries, the real start/end frame of every scene (extracted from the video as images), and an exhaustive JSON analysis — per-scene action detail, camera motion, generation-ready frame/motion prompts, overlay text with full typographic style, floating elements, deeply detailed cast (perceived demographics, ethnicity/skin-tone, styling, market-recasting notes), brand-identified logos (named by brand and what they signal, not by appearance, with on-screen timestamps), dialogue with voice descriptions, music spec, SFX list, plus a word-level transcript. `mode:\"index\"` is the cheap structure-first pass: scene boundaries + global blueprint only (one LLM call, no frames).',\n when_to_use:\n 'Use to reverse-engineer a reference video (e.g. a competitor ad) so a new canvas can reproduce or remix it scene by scene. Agent loop: (1) optionally run `mode:\"index\"` to see the structure cheaply (scene count, boundaries, transcript) before planning; (2) run the full deconstruct; (3) read `analysis` and author the reproduction canvas. The blueprint maps 1:1 onto generation nodes: `analysis.scenes[i]` aligns positionally with `start_frames#i`/`end_frames#i`; per scene, `start_frame_prompt`/`end_frame_prompt` feed image_generate (overlay text is excluded from them by contract — recomposite it from `overlays`), `motion_prompt` + the two frames feed video_generate (first_frame/last_frame), `dialogue[].voice_description` casts tts/dialogue voices, `global.music.music_prompt` feeds music, `sfx[].sound_effect_prompt` feeds sound_effect, and `overlays`/`floating_elements` drive an ffmpeg/hyperframe overlay pass. Long videos (over ~8 min single-shot): run `mode:\"index\"` first, then several full nodes IN PARALLEL each with a `start_s`/`end_s` window (≤480s, snap edges to index scene boundaries), and merge by concatenating `analysis.scenes`; over-length errors include suggested windows. Inject fields into downstream prompts via `{{slot}}`. Pick `~google/gemini-pro-latest` for the densest extraction, `~google/gemini-flash-latest` for cheap/fast passes.',\n inputs: z.object({ video: VideoRef }).loose(),\n params: z\n .object({\n model: z.enum(VIDEO_DECONSTRUCT_MODELS),\n mode: z.enum([\"full\", \"index\"]).optional(),\n language: z.string().min(2).max(8).optional(),\n max_scenes: z.number().int().min(1).max(60).optional(),\n focus: z.string().optional(),\n start_s: z.number().min(0).optional(),\n end_s: z.number().positive().optional(),\n // Real visual shot-cut timestamps (absolute seconds), detected locally with\n // ffmpeg before the deconstruct. The backend SNAPS its LLM scene boundaries\n // onto these and SPLITS any scene that spans one, so a scene's frames never\n // straddle a hard cut. `scaffold-video` populates this; omit for LLM-only cuts.\n shot_cuts: z.array(z.number().min(0)).max(200).optional(),\n // The video model's per-clip ceiling (seconds). A shot longer than this is\n // split into seamless continuation sub-scenes (shared splice frame), so long\n // shots reproduce in full instead of being truncated. `scaffold-video` sets\n // the Seedance ceiling (15); omit to disable length splitting.\n max_clip_s: z.number().positive().max(60).optional(),\n // Transcript provider for the blueprint's dialogue/transcript. Default\n // Groq Whisper; \"deepgram\" routes to Nova-3 so words carry punctuation.\n transcriber: z.enum([\"groq\", \"deepgram\"]).optional(),\n })\n .strict(),\n outputs: z\n .object({\n analysis: JsonRef,\n // Absent in mode:\"index\" (structure only, no Mux frame extraction).\n start_frames: z.array(ImageRef).min(1).optional(),\n end_frames: z.array(ImageRef).min(1).optional(),\n transcript: JsonRef,\n })\n .strict(),\n outputKinds: { analysis: \"json\", start_frames: \"image\", end_frames: \"image\", transcript: \"json\" },\n cost: ({ params }) =>\n (params as { mode?: string }).mode === \"index\"\n ? { credits: 3, seconds_estimate: 60 }\n : { credits: 15, seconds_estimate: 300 },\n});\n","import { estimateNodeCredits, modelIdsAccepted } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport { AudioRef, VideoRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\n// `fal/veed-lipsync` is the legacy id stored canvases carry; it is accepted as is\n// and the backend resolves it to the registered id it stands for.\nconst VideoLipsyncParams = z\n .object({\n model: z.enum(modelIdsAccepted(\"replicate/sync-lipsync-2\")),\n })\n .strict();\n\nexport const videoLipsyncNode = delegated({\n id: \"video_lipsync\",\n version: \"1.0.0\",\n category: \"video\",\n summary:\n \"Lip-sync a video to an audio track (`replicate/sync-lipsync-2`: Sync Labs lipsync-2 on Replicate; the legacy id `fal/veed-lipsync` still runs). Billed at $0.40 per minute of the longer input. Runs as a background job.\",\n inputs: z\n .object({\n video: VideoRef,\n audio: AudioRef,\n })\n .strict(),\n params: VideoLipsyncParams,\n outputs: z.object({ video: VideoRef }).strict(),\n outputKinds: { video: \"video\" },\n // Priced by the shared contract per minute of output; the quote cannot see the\n // clips' lengths, so it assumes the same one minute the charge falls back to.\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"video_lipsync\", params }) ?? 1,\n seconds_estimate: 120,\n }),\n});\n","import { mkdtemp, readFile, rm } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { estimateNodeCredits } from \"@baker/canvas-contract\";\nimport { z } from \"zod\";\nimport type { ExecCtx } from \"../../engine/context.ts\";\nimport { defineNode } from \"../../engine/define.ts\";\nimport type { AssetRef } from \"../../schema/refs.ts\";\nimport { AudioRef, VideoRef } from \"../../schema/refs.ts\";\nimport { sha256Hex } from \"../../storage/sha256.ts\";\nimport { uploadAndIngest } from \"../ingest.ts\";\nimport { probeVideo, runFfmpeg } from \"../local/lib/ffmpeg.ts\";\nimport { timeScriptWords } from \"../local/lib/script-word-timing.ts\";\nimport { callBackendExec } from \"./delegate.ts\";\n\nconst VideoTranscribeParams = z\n .object({\n language: z.string().min(2).max(8).optional(),\n // Provider choice is explicit (no env-based silent branching). Default Groq\n // Whisper; \"deepgram\" routes to Deepgram Nova-3, which additionally emits a\n // `rich` JSON output with punctuated words + paragraph/sentence grouping.\n transcriber: z.enum([\"groq\", \"deepgram\"]).optional(),\n keyterms: z\n .array(z.string().min(2).max(60))\n .max(30)\n .optional()\n .describe(\n \"Names the recogniser must not mangle — a product, a person, a phrase the script coins. \" +\n \"The COMPANY'S OWN NAME is always applied server-side, so you never list it: this is for the rest. \" +\n \"Every term is biased into the request AND its spelling enforced on the way out, because a caption \" +\n \"prints a mishearing at full size over the ad (a real cut shipped with the brand name spelled wrong). \" +\n \"Never patch a misspelling inside a caption composition — that fix dies with that one creative.\",\n ),\n script: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"The words the track was asked to say, when there is a script: the transcript's words are then the \" +\n \"script's, spelled as written, at the times the recogniser heard them. A misheard word stops being a \" +\n \"typo on screen, and a word the recogniser added (a filler) is never printed. Aligned locally, never sent.\",\n ),\n })\n .strict();\n\nconst VideoTranscribeInputs = z\n .object({\n // A video (audio auto-extracted locally) OR a bare audio track. The key stays\n // `video` for back-compat; the backend already accepts audio-kind refs on it —\n // the local extraction path has been shipping one for every video input.\n video: z.union([VideoRef, AudioRef]),\n })\n .strict();\n\nconst VideoTranscribeOutputs = z\n .object({\n transcript: z.custom<AssetRef>(),\n // Only emitted by the Deepgram path: full punctuated words + paragraph /\n // sentence grouping with speaker indices. Absent for the default Groq path.\n rich: z.custom<AssetRef>().optional(),\n })\n .strict();\n\ntype Inputs = z.infer<typeof VideoTranscribeInputs>;\ntype Params = z.infer<typeof VideoTranscribeParams>;\ntype Outputs = z.infer<typeof VideoTranscribeOutputs>;\n\nconst AUDIO_EXTRACT_TIMEOUT_MS = 60_000;\n\nexport const videoTranscribeNode = defineNode<Inputs, Params, Outputs>({\n id: \"video_transcribe\",\n version: \"2.4.0\",\n category: \"language\",\n location: \"local\",\n summary:\n 'Transcribe a video or audio track to a word-level JSON transcript. Default `transcriber:\"groq\"` uses ' +\n 'Groq Whisper Large v3 Turbo ($0.04/hr, 10s min); `transcriber:\"deepgram\"` uses Deepgram Nova-3 ' +\n \"($0.0043/min) and additionally emits a `rich` JSON output with punctuated words + paragraph/sentence \" +\n \"grouping (and speaker indices). \" +\n \"Automatically extracts audio locally (mono 16 kHz MP3) before uploading — reduces payload ~100× \" +\n \"and lifts the effective duration limit well beyond Groq's 100 MB file cap. \" +\n \"The `transcript` output is always an array of {text, start, end} entries ready to feed Hyperframes \" +\n \"caption compositions (Deepgram prefers the punctuated word form). Known names are spelled the way the \" +\n \"brand spells them: the company's own name is biased into the request and enforced on the output \" +\n \"automatically, and `keyterms` adds any other name the script depends on.\",\n when_to_use:\n \"Use to generate burned-in captions for a stitched video. Pair with `hyperframe_render` and a captions composition (e.g. `tiktok-captions`) by passing the transcript JSON through `params.variables.transcript`. \" +\n 'Pick `params.language` to filter out non-target speech (e.g. \"es\" for Spanish). ' +\n 'Pick `transcriber:\"deepgram\"` when you want punctuation/paragraphs (read them from the `rich` output) or speaker grouping. ' +\n \"Requires ffmpeg on PATH for audio extraction (falls back to full video upload if unavailable).\",\n inputs: VideoTranscribeInputs,\n params: VideoTranscribeParams,\n outputs: VideoTranscribeOutputs,\n outputKinds: { transcript: \"json\", rich: \"json\" },\n // Priced by the shared contract for the chosen transcriber, assuming a minute of audio.\n cost: ({ params }) => ({\n credits: estimateNodeCredits({ nodeType: \"video_transcribe\", params }) ?? 1,\n seconds_estimate: 30,\n }),\n\n async execute({ inputs, params, ctx }) {\n const audioInput = await tryExtractAudio(inputs, ctx);\n const effectiveInputs = audioInput ?? inputs;\n const { script, ...recogniserParams } = params;\n\n const outputs = (await callBackendExec({\n nodeType: \"video_transcribe\",\n nodeVersion: \"2.4.0\",\n params: recogniserParams,\n inputs: effectiveInputs,\n outputKinds: { transcript: \"json\", rich: \"json\" },\n ctx,\n })) as Outputs;\n if (!script) return outputs;\n return { ...outputs, transcript: await scriptTranscript(outputs.transcript, script, params.language, ctx) };\n },\n});\n\n/**\n * The transcript with the script's words at the recogniser's times (`timeScriptWords`).\n * Read from the recogniser's own output, so what it heard stays on record beside it.\n */\nasync function scriptTranscript(\n heardRef: AssetRef,\n script: string,\n language: string | undefined,\n ctx: ExecCtx,\n): Promise<AssetRef> {\n const local = heardRef.path\n ? heardRef\n : await ctx.assets.ingestRemote({\n kind: \"json\",\n url: String(heardRef.url),\n sha256: heardRef.sha256,\n mime: heardRef.mime,\n });\n const heard = JSON.parse(await readFile(String(local.path), \"utf8\")) as unknown;\n const words = Array.isArray(heard) ? heard.filter(isTimedWord) : [];\n const timed = timeScriptWords(script, words, language);\n ctx.log(`video_transcribe: ${timed.length} script words timed off ${words.length} heard`);\n const bytes = Buffer.from(JSON.stringify(timed));\n const metadata = { ...heardRef.metadata, text_of_record: \"script\", heard_sha256: heardRef.sha256 };\n // Uploaded like the recogniser's own output, so a remote reader can fetch it too; a\n // failed upload still leaves the local file every local renderer reads.\n return uploadAndIngest({ bytes, kind: \"json\", mime: \"application/json\", metadata, ctx }).catch(() =>\n ctx.assets.ingestBytes({ bytes, kind: \"json\", mime: \"application/json\", metadata }),\n );\n}\n\nfunction isTimedWord(value: unknown): value is { text: string; start: number; end: number } {\n const w = value as { text?: unknown; start?: unknown; end?: unknown } | null;\n return Boolean(w) && typeof w?.start === \"number\" && typeof w?.end === \"number\" && w?.text !== undefined;\n}\n\nasync function tryExtractAudio(inputs: Inputs, ctx: ExecCtx): Promise<{ video: AssetRef } | null> {\n const video = inputs.video;\n // A bare audio input is already the small transcribable payload — send it as-is.\n if (!video.path || !video.mime?.startsWith(\"video/\")) return null;\n\n let tmpDir: string | undefined;\n try {\n const probe = await probeVideo(video.path);\n if (!probe.has_audio) {\n ctx.log(\"video_transcribe: no audio track detected, sending full video\");\n return null;\n }\n\n tmpDir = await mkdtemp(path.join(tmpdir(), \"vtx-\"));\n const audioPath = path.join(tmpDir, \"audio.mp3\");\n\n ctx.log(\"video_transcribe: extracting audio (mono 16kHz mp3)\");\n await runFfmpeg(\n [\"-i\", video.path, \"-vn\", \"-ac\", \"1\", \"-ar\", \"16000\", \"-b:a\", \"64k\", \"-f\", \"mp3\", \"-y\", audioPath],\n { timeout_ms: AUDIO_EXTRACT_TIMEOUT_MS },\n );\n\n const bytes = await readFile(audioPath);\n if (bytes.byteLength === 0) {\n ctx.log(\"video_transcribe: extracted audio is empty, sending full video\");\n return null;\n }\n\n const sha = sha256Hex(bytes);\n const mime = \"audio/mpeg\";\n const { putUrl, publicUrl } = await ctx.client.presignAssetUpload(sha, mime, ctx.signal);\n\n const putRes = await fetch(putUrl, {\n method: \"PUT\",\n body: new Uint8Array(bytes),\n headers: { \"Content-Type\": mime },\n signal: ctx.signal,\n });\n if (!putRes.ok) {\n ctx.log(`video_transcribe: audio upload failed (${putRes.status}), sending full video`);\n return null;\n }\n\n const localRef = await ctx.assets.ingestBytes({\n bytes,\n kind: \"audio\",\n mime,\n metadata: { extracted_from: video.sha256 },\n });\n\n const sizeMb = (bytes.byteLength / (1024 * 1024)).toFixed(1);\n ctx.log(`video_transcribe: audio extracted (${sizeMb} MB), uploading`);\n\n return {\n video: { ...localRef, url: publicUrl } as AssetRef,\n };\n } catch (e) {\n ctx.log(`video_transcribe: audio extraction failed, sending full video: ${(e as Error).message}`);\n return null;\n } finally {\n if (tmpDir) {\n await rm(tmpDir, { recursive: true, force: true }).catch((_e) => undefined);\n }\n }\n}\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\ntype VideoProbe = {\n duration_seconds: number;\n codec_name: string;\n profile: string | null;\n level: number | null;\n width: number;\n height: number;\n pix_fmt: string | null;\n fps_num: number;\n fps_den: number;\n has_audio: boolean;\n audio_codec: string | null;\n sample_rate: number | null;\n channels: number | null;\n};\n\ntype FfprobeStream = {\n codec_type?: string;\n codec_name?: string;\n profile?: string;\n level?: number;\n width?: number;\n height?: number;\n pix_fmt?: string;\n r_frame_rate?: string;\n avg_frame_rate?: string;\n sample_rate?: string;\n channels?: number;\n};\n\ntype FfprobeFormat = {\n duration?: string;\n};\n\ntype FfprobeJson = {\n streams?: FfprobeStream[];\n format?: FfprobeFormat;\n};\n\nexport async function probeVideo(filePath: string): Promise<VideoProbe> {\n const { stdout } = await execFileAsync(\n \"ffprobe\",\n [\"-v\", \"error\", \"-show_streams\", \"-show_format\", \"-of\", \"json\", filePath],\n { encoding: \"utf-8\", maxBuffer: 8 * 1024 * 1024 },\n );\n return parseProbeJson(stdout, filePath);\n}\n\nfunction parseProbeJson(stdout: string, filePath: string): VideoProbe {\n let parsed: FfprobeJson;\n try {\n parsed = JSON.parse(stdout) as FfprobeJson;\n } catch {\n throw new Error(`ffprobe: unparseable JSON output for ${filePath}`);\n }\n\n const streams = parsed.streams ?? [];\n const video = streams.find((s) => s.codec_type === \"video\");\n if (!video) throw new Error(`ffprobe: no video stream found in ${filePath}`);\n if (typeof video.width !== \"number\" || typeof video.height !== \"number\") {\n throw new Error(`ffprobe: missing video dimensions for ${filePath}`);\n }\n\n const duration = Number.parseFloat(parsed.format?.duration ?? \"\");\n if (!Number.isFinite(duration) || duration <= 0) {\n throw new Error(`ffprobe: invalid duration for ${filePath}: ${parsed.format?.duration}`);\n }\n\n const audio = streams.find((s) => s.codec_type === \"audio\");\n return buildVideoProbe(video, video.width, video.height, audio, duration);\n}\n\nfunction buildVideoProbe(\n video: FfprobeStream,\n width: number,\n height: number,\n audio: FfprobeStream | undefined,\n duration: number,\n): VideoProbe {\n const fps = parseFrameRate(video.r_frame_rate ?? video.avg_frame_rate ?? \"30/1\");\n return {\n duration_seconds: duration,\n codec_name: video.codec_name ?? \"unknown\",\n profile: video.profile ?? null,\n level: typeof video.level === \"number\" ? video.level : null,\n width,\n height,\n pix_fmt: video.pix_fmt ?? null,\n fps_num: fps.num,\n fps_den: fps.den,\n has_audio: audio !== undefined,\n audio_codec: audio?.codec_name ?? null,\n sample_rate: audio?.sample_rate ? Number.parseInt(audio.sample_rate, 10) : null,\n channels: typeof audio?.channels === \"number\" ? audio.channels : null,\n };\n}\n\nfunction parseFrameRate(rate: string): { num: number; den: number } {\n const match = rate.match(/^(\\d+)\\/(\\d+)$/);\n if (match) {\n const num = Number.parseInt(match[1] ?? \"0\", 10);\n const den = Number.parseInt(match[2] ?? \"0\", 10);\n if (den > 0) return { num, den };\n }\n const flat = Number.parseFloat(rate);\n if (Number.isFinite(flat) && flat > 0) return { num: Math.round(flat * 1000), den: 1000 };\n return { num: 30, den: 1 };\n}\n\ntype RunFfmpegOpts = {\n timeout_ms: number;\n log?: (msg: string) => void;\n};\n\nexport async function runFfmpeg(args: readonly string[], opts: RunFfmpegOpts): Promise<void> {\n try {\n await execFileAsync(\"ffmpeg\", args, {\n timeout: opts.timeout_ms,\n maxBuffer: 64 * 1024 * 1024,\n });\n } catch (e) {\n const stderr = (e as { stderr?: string }).stderr ?? \"\";\n const stdout = (e as { stdout?: string }).stdout ?? \"\";\n const detail = stderr || stdout || (e as Error).message;\n throw new Error(`ffmpeg failed:\\n${detail.slice(-4000)}`);\n }\n}\n","/**\n * Integers spelled as words in the language they are spoken in.\n *\n * Two readers: native (Seedance) dialogue, where a bare \"6930\" in a French line is read\n * in English unless it is spelled out, and caption timing, where the script writes \"9\"\n * and the recogniser hears \"nueve\". One speller per language the blueprint can resolve\n * (the same set as its ISO table), via n2words, tree-shaken per language.\n */\nimport { toCardinal as nwAr } from \"n2words/ar-SA\";\nimport { toCardinal as nwDe } from \"n2words/de-DE\";\nimport { toCardinal as nwEn } from \"n2words/en-US\";\nimport { toCardinal as nwEs } from \"n2words/es-ES\";\nimport { toCardinal as nwFr } from \"n2words/fr-FR\";\nimport { toCardinal as nwHi } from \"n2words/hi-IN\";\nimport { toCardinal as nwIt } from \"n2words/it-IT\";\nimport { toCardinal as nwJa } from \"n2words/ja-JP\";\nimport { toCardinal as nwKo } from \"n2words/ko-KR\";\nimport { toCardinal as nwNl } from \"n2words/nl-NL\";\nimport { toCardinal as nwPl } from \"n2words/pl-PL\";\nimport { toCardinal as nwPt } from \"n2words/pt-PT\";\n\nconst INTEGER_SPELLERS: Record<string, (n: number) => string> = {\n fr: nwFr,\n es: nwEs,\n en: nwEn,\n de: nwDe,\n it: nwIt,\n pt: nwPt,\n nl: nwNl,\n pl: nwPl,\n ar: nwAr,\n ja: nwJa,\n ko: nwKo,\n hi: nwHi,\n};\n\n/** Whether integers can be spelled in this ISO 639-1 language. */\nexport function canSpellNumbers(langCode: string): boolean {\n return langCode in INTEGER_SPELLERS;\n}\n\n/**\n * Spell a non-negative integer into `langCode` words, or return the original digits\n * when the language is unsupported or the spelling fails.\n */\nexport function spellNumber(langCode: string | undefined, n: number): string {\n const spell = langCode ? INTEGER_SPELLERS[langCode] : undefined;\n if (!spell || !Number.isFinite(n)) return String(n);\n try {\n return spell(n);\n } catch {\n return String(n);\n }\n}\n","/**\n * Captions that say the script, timed by the voice that read it.\n *\n * A spoken ad's captions came straight from transcribing its rendered voice track, so a\n * misheard word (\"llueve\" for \"nueve\", a brand name bent into a common word) became a typo\n * burned into the ad at full size. The script is what the voice was asked to say and what\n * the viewer should read; the transcript only knows WHEN each word was said.\n *\n * So the script is the text of record and the transcript is the clock: the two word\n * sequences are aligned (a global alignment, tolerant of misrecognitions, case, accents,\n * punctuation and numbers spoken as words), each script word takes the timestamps of the\n * transcript words it lines up with, and a script word nothing lines up with (the\n * recogniser dropped it) is placed between its neighbours. Words the recogniser added\n * (a filler, a stutter) are never printed.\n *\n * Pure: script and transcript in, timed words out.\n */\nimport { spellNumber } from \"../../../lib/spell-number.ts\";\nimport type { CaptionWord } from \"./caption-groups.ts\";\n\n/** How long a word is taken to last when there is no neighbour on one side to bound it. */\nconst UNBOUNDED_WORD_S = 0.3;\n\n/** Alignment scores: an exact word, a near one, a different one in the same place, a skip. */\nconst EXACT = 3;\nconst NEAR = 2;\nconst SUBSTITUTE = -1;\nconst SKIP = -2;\n/** How alike two words must be to count as the same word heard slightly wrong. */\nconst NEAR_SIMILARITY = 0.7;\n\n/** One comparable token and the word it came from. */\ntype Token = { key: string; word: number };\n\n/** Lower-case, accents and punctuation stripped: \"Días.\" and \"dias\" compare equal. */\nfunction normalize(word: string): string {\n return word\n .normalize(\"NFD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]/gu, \"\");\n}\n\n/**\n * The tokens a word is compared by. A plain integer is compared as the words it is spoken\n * as, so \"9\" in the script lines up with \"nueve\" in the transcript, and the other way round.\n */\nfunction tokensOf(word: string, index: number, language: string | undefined): Token[] {\n const key = normalize(word);\n if (!key) return [];\n if (language && /^\\d{1,9}$/.test(key)) {\n const spoken = spellNumber(language, Number.parseInt(key, 10));\n if (spoken !== key) {\n const parts = spoken\n .split(/[\\s-]+/)\n .map(normalize)\n .filter(Boolean);\n if (parts.length > 0) return parts.map((part) => ({ key: part, word: index }));\n }\n }\n return [{ key, word: index }];\n}\n\n/** Levenshtein similarity in [0, 1]. */\nfunction similarity(a: string, b: string): number {\n if (a === b) return 1;\n const longest = Math.max(a.length, b.length);\n if (longest === 0) return 1;\n let prev = Array.from({ length: b.length + 1 }, (_, j) => j);\n for (let i = 1; i <= a.length; i++) {\n const row = [i];\n for (let j = 1; j <= b.length; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n row[j] = Math.min((prev[j] ?? 0) + 1, (row[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);\n }\n prev = row;\n }\n return 1 - (prev[b.length] ?? longest) / longest;\n}\n\nfunction pairScore(a: string, b: string): number {\n if (a === b) return EXACT;\n return similarity(a, b) >= NEAR_SIMILARITY ? NEAR : SUBSTITUTE;\n}\n\ntype Grid = number[][];\n\nconst at = (grid: Grid, i: number, j: number): number => grid[i]?.[j] ?? 0;\n\n/** The diagonal step's score: script token `i` lined up with transcript token `j` (1-based). */\nfunction diagonal(grid: Grid, script: Token[], transcript: Token[], i: number, j: number): number {\n return at(grid, i - 1, j - 1) + pairScore(script[i - 1]?.key ?? \"\", transcript[j - 1]?.key ?? \"\");\n}\n\n/** Needleman-Wunsch's score grid over the two token sequences. */\nfunction scoreGrid(script: Token[], transcript: Token[]): Grid {\n const grid: Grid = Array.from({ length: script.length + 1 }, (_, i) =>\n Array.from({ length: transcript.length + 1 }, (_, j) => (i === 0 ? j * SKIP : i * SKIP)),\n );\n for (let i = 1; i <= script.length; i++) {\n const row = grid[i] as number[];\n for (let j = 1; j <= transcript.length; j++) {\n row[j] = Math.max(diagonal(grid, script, transcript, i, j), at(grid, i - 1, j) + SKIP, at(grid, i, j - 1) + SKIP);\n }\n }\n return grid;\n}\n\n/**\n * For each script token, the transcript token it lines up with (a match or a substitution),\n * or -1 when it is skipped.\n */\nfunction align(script: Token[], transcript: Token[]): number[] {\n const grid = scoreGrid(script, transcript);\n const pairs = new Array<number>(script.length).fill(-1);\n let i = script.length;\n let j = transcript.length;\n while (i > 0 && j > 0) {\n const here = at(grid, i, j);\n if (here === diagonal(grid, script, transcript, i, j)) {\n pairs[i - 1] = j - 1;\n i--;\n j--;\n } else if (here === at(grid, i - 1, j) + SKIP) {\n i--;\n } else {\n j--;\n }\n }\n return pairs;\n}\n\ntype Span = { start: number; end: number };\n\nconst round = (s: number): number => Math.round(s * 1000) / 1000;\n\n/** Each script word's span: from the first to the last transcript word its tokens lined up with. */\nfunction pairedSpans(\n wordCount: number,\n scriptTokens: Token[],\n heardTokens: Token[],\n pairs: number[],\n transcript: readonly CaptionWord[],\n): Array<Span | null> {\n const spans: Array<Span | null> = new Array(wordCount).fill(null);\n pairs.forEach((heardIndex, t) => {\n const token = scriptTokens[t];\n const said = heardIndex >= 0 ? transcript[heardTokens[heardIndex]?.word ?? -1] : undefined;\n if (!token || !said) return;\n const current = spans[token.word];\n spans[token.word] = current\n ? { start: Math.min(current.start, said.start), end: Math.max(current.end, said.end) }\n : { start: said.start, end: said.end };\n });\n return spans;\n}\n\n/** Spread the run of untimed words `[first, end)` across the gap their neighbours leave. */\nfunction fillRun(spans: Array<Span | null>, first: number, end: number): void {\n const before = first > 0 ? spans[first - 1] : null;\n const after = end < spans.length ? spans[end] : null;\n const count = end - first;\n const from = before ? before.end : Math.max(0, (after?.start ?? 0) - count * UNBOUNDED_WORD_S);\n const to = after ? Math.max(after.start, from) : from + count * UNBOUNDED_WORD_S;\n const each = (to - from) / count;\n for (let w = first; w < end; w++) spans[w] = { start: from + (w - first) * each, end: from + (w - first + 1) * each };\n}\n\n/**\n * A word the recogniser dropped sits between the neighbours it was said between, a run of\n * them sharing that gap evenly. At either end it takes a nominal length past its neighbour.\n */\nfunction fillDropped(spans: Array<Span | null>): Span[] {\n const filled = [...spans];\n let i = 0;\n while (i < filled.length) {\n let end = i;\n while (end < filled.length && !filled[end]) end++;\n if (end > i) fillRun(filled, i, end);\n i = end + 1;\n }\n return filled.map((span) => span ?? { start: 0, end: 0 });\n}\n\n/**\n * The script's words, each timed off the transcript. `language` (ISO 639-1) lets a numeral\n * on either side line up with the same number spoken as words. An empty transcript (nothing\n * was heard) gives no captions, as it did before.\n */\nexport function timeScriptWords(script: string, transcript: readonly CaptionWord[], language?: string): CaptionWord[] {\n const words = script.split(/\\s+/).filter(Boolean);\n if (words.length === 0 || transcript.length === 0) return [];\n const scriptTokens = words.flatMap((word, i) => tokensOf(word, i, language));\n const heardTokens = transcript.flatMap((w, i) => tokensOf(String(w.text ?? \"\"), i, language));\n const pairs = align(scriptTokens, heardTokens);\n const spans = fillDropped(pairedSpans(words.length, scriptTokens, heardTokens, pairs, transcript));\n return words.map((text, i) => ({ text, start: round(spans[i]?.start ?? 0), end: round(spans[i]?.end ?? 0) }));\n}\n","import { z } from \"zod\";\nimport { JsonRef, TextRef } from \"../../schema/refs.ts\";\nimport { delegated } from \"./delegate.ts\";\n\nexport const voiceSelectNode = delegated({\n id: \"voice_select\",\n // 1.1.0: a pinned voice used to come back as an inline `{ kind, text }` with no sha256\n // and no mime, which every consumer taking the id as an INPUT rejected —\n // `audio_voice_convert` died on `voice_ref.sha256: expected string, received undefined`.\n // The backend now uploads it as a real asset like the ranked branch, and the version has\n // to move with it: the cache key is built from this string, so entries written under\n // 1.0.0 kept replaying the broken shape long after the fix was deployed. A node whose\n // OUTPUT shape changes has to bump, not only one whose behaviour does.\n // 1.2.0: `language` is a hard constraint — a language no voice on the account speaks\n // fails the node instead of casting a voice of another language, and a gender nobody\n // in the language has is relaxed with a printed warning. Bumped so a 1.1.0 cache entry\n // holding a wrong-language cast stops replaying.\n version: \"1.2.0\",\n category: \"audio\",\n summary:\n 'Cast an ElevenLabs voice from a natural-language description (e.g. \"warm, authoritative female narrator, American accent\"). Lists the account\\'s voices and ranks them against the brief, emitting the best `voice_id` as a bare-string text asset plus a ranked `candidates` JSON.',\n when_to_use:\n 'Use to turn a voice description (e.g. from a `video_deconstruct` blueprint\\'s `voice_description`) into a usable ElevenLabs voice id, then feed it into a `tts` node by wiring `inputs.voice_ref: $ref:<this>.voice_id` and setting `params.voice: \"{{voice_ref}}\"` — the engine splices the id in at run time. Review `candidates` (json) to pick a different voice. Optional `gender`/`age`/`accent`/`language` hints sharpen the ranking. `language` is strict: when no voice on the account speaks it the node fails before anything is voiced (add a voice in that language, or pass its `voice_id`). A `gender` nobody in that language has is dropped and `baker canvas run` prints a `[warn]` naming the voice cast instead.',\n inputs: z.object({}).loose(),\n params: z\n .object({\n description: z.string().min(1),\n gender: z.string().optional(),\n age: z.string().optional(),\n accent: z.string().optional(),\n language: z.string().optional(),\n /**\n * A voice already chosen — an Avatar's pinned one. Given, nothing is cast: the\n * ranking is skipped and this id is emitted as-is.\n */\n voice_id: z.string().min(1).max(120).optional(),\n limit: z.number().int().min(1).max(20).optional(),\n })\n .strict(),\n outputs: z.object({ voice_id: TextRef, candidates: JsonRef }).strict(),\n outputKinds: { voice_id: \"text\", candidates: \"json\" },\n cost: () => ({ credits: 0, seconds_estimate: 5 }),\n});\n","import { z } from \"zod\";\nimport { priceNode } from \"../engine/credits.ts\";\nimport type { AnyNodeDefinition } from \"../engine/define.ts\";\nimport type { NodeRegistry } from \"../engine/registry.ts\";\n\ntype CatalogEntry = {\n id: string;\n version: string;\n category: string;\n summary: string;\n when_to_use?: string;\n location: \"local\" | \"remote\";\n inputs: unknown;\n params: unknown;\n outputs: unknown;\n cost_estimate_credits?: number;\n runtime_estimate_seconds?: number;\n};\n\nexport type Catalog = {\n schema: \"baker-canvas-catalog/2\";\n generated_at: string;\n categories: Record<string, string[]>;\n entries: CatalogEntry[];\n};\n\nexport function generateCatalog(registry: NodeRegistry, opts: { generatedAt?: string } = {}): Catalog {\n const entries: CatalogEntry[] = registry\n .all()\n .map((def) => {\n const cost = catalogCost(def);\n return {\n id: def.id,\n version: def.version,\n category: def.category,\n summary: def.summary,\n when_to_use: def.when_to_use,\n location: def.location,\n inputs: z.toJSONSchema(def.inputs, { unrepresentable: \"any\" }),\n params: z.toJSONSchema(def.params, { unrepresentable: \"any\" }),\n outputs: z.toJSONSchema(def.outputs, { unrepresentable: \"any\" }),\n cost_estimate_credits: cost?.credits,\n runtime_estimate_seconds: cost?.seconds_estimate,\n };\n })\n .sort((a, b) => a.id.localeCompare(b.id));\n\n return {\n schema: \"baker-canvas-catalog/2\",\n generated_at: opts.generatedAt ?? new Date().toISOString(),\n categories: groupIds(entries),\n entries,\n };\n}\n\n/** How much of the catalog to print: an index by default, full schemas on request. */\nexport type CatalogViewOptions = {\n node?: string;\n category?: string;\n full?: boolean;\n};\n\ntype CatalogIndex = {\n ok: true;\n schema: Catalog[\"schema\"];\n categories: Record<string, string[]>;\n nodes: Record<string, string>;\n hints: string[];\n};\n\ntype CatalogSchemas = {\n ok: true;\n schema: Catalog[\"schema\"];\n categories: Record<string, string[]>;\n entries: CatalogEntry[];\n};\n\ntype CatalogRefusal = {\n ok: false;\n error: { code: \"NOT_FOUND\" | \"VALIDATION_ERROR\"; message: string; fix: string };\n};\n\nexport type CatalogView = CatalogIndex | CatalogSchemas | CatalogRefusal;\n\nconst INDEX_SUMMARY_MAX = 160;\nconst sentences = new Intl.Segmenter(\"en\", { granularity: \"sentence\" });\n\n/**\n * One line that says what a node is for: its summary's first sentence, cut at a\n * word when that sentence runs long. The index exists to pick a node, and the\n * rest of the summary travels with the node's full schema.\n */\nfunction oneLine(summary: string): string {\n const first = sentences.segment(summary)[Symbol.iterator]().next().value?.segment.trim() ?? summary;\n if (first.length <= INDEX_SUMMARY_MAX) return first;\n const cut = first.slice(0, INDEX_SUMMARY_MAX);\n const lastSpace = cut.lastIndexOf(\" \");\n return `${(lastSpace > 0 ? cut.slice(0, lastSpace) : cut).replace(/[\\s,;:—-]+$/u, \"\")}…`;\n}\n\nfunction listIds(value: string | undefined): string[] {\n return (value ?? \"\")\n .split(\",\")\n .map((id) => id.trim())\n .filter((id) => id.length > 0);\n}\n\n/**\n * The part of the catalog a caller asked for.\n *\n * Every node's full JSON Schema is ~160 KB and grows with the registry, while a\n * caller almost always wants one node. So the default is an index — each node's\n * id and one line, grouped by category — and the schemas come by name\n * (`node`), by category with `full`, or all at once with `full` alone.\n */\nexport function catalogView(catalog: Catalog, opts: CatalogViewOptions): CatalogView {\n const knownIds = catalog.entries.map((e) => e.id);\n const knownCategories = Object.keys(catalog.categories);\n\n if (opts.category !== undefined && !knownCategories.includes(opts.category)) {\n return {\n ok: false,\n error: {\n code: \"NOT_FOUND\",\n message: `No node category \"${opts.category}\".`,\n fix: `Pass one of: ${knownCategories.join(\", \")}.`,\n },\n };\n }\n\n const requestedIds = listIds(opts.node);\n if (opts.node !== undefined && requestedIds.length === 0) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: \"--node needs a node id.\",\n fix: \"Run `baker canvas catalog` for the ids, then pass one (or several, comma-separated).\",\n },\n };\n }\n const unknownIds = requestedIds.filter((id) => !knownIds.includes(id));\n if (unknownIds.length > 0) {\n return {\n ok: false,\n error: {\n code: \"NOT_FOUND\",\n message: `No node named ${unknownIds.map((id) => `\"${id}\"`).join(\", \")}.`,\n fix: `Pass one of: ${knownIds.join(\", \")}.`,\n },\n };\n }\n\n const inCategory = (e: CatalogEntry) => opts.category === undefined || e.category === opts.category;\n const categories =\n opts.category === undefined ? catalog.categories : { [opts.category]: catalog.categories[opts.category] ?? [] };\n\n if (requestedIds.length > 0) {\n const entries = catalog.entries.filter((e) => requestedIds.includes(e.id));\n return { ok: true, schema: catalog.schema, categories: groupIds(entries), entries };\n }\n\n if (opts.full) {\n return { ok: true, schema: catalog.schema, categories, entries: catalog.entries.filter(inCategory) };\n }\n\n const nodes: Record<string, string> = {};\n for (const e of catalog.entries.filter(inCategory)) nodes[e.id] = oneLine(e.summary);\n return {\n ok: true,\n schema: catalog.schema,\n categories,\n nodes,\n hints: [\n \"One node's params, inputs, outputs, models and when to use it: baker canvas catalog --node <id> (comma-separate several).\",\n \"Every schema in a category: --category <name> --full. Every node: --full (large; prefer --node).\",\n ],\n };\n}\n\nfunction groupIds(entries: CatalogEntry[]): Record<string, string[]> {\n const categories: Record<string, string[]> = {};\n for (const e of entries) {\n let bucket = categories[e.category];\n if (!bucket) {\n bucket = [];\n categories[e.category] = bucket;\n }\n bucket.push(e.id);\n }\n return categories;\n}\n\n/**\n * A node type's price with no canvas to read: its params' defaults when they parse\n * from nothing, else no params at all. Only a finite number is advertised.\n */\nfunction catalogCost(def: AnyNodeDefinition): ReturnType<typeof priceNode> {\n const defaults = def.params.safeParse({});\n return priceNode(def, defaults.success ? defaults.data : {});\n}\n","import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type CacheEntry = {\n cacheKey: string;\n nodeType: string;\n nodeVersion: string;\n outputs: Record<string, unknown>;\n refs: string[]; // sha256s referenced (for GC later)\n createdAt: number;\n};\n\nexport interface CacheStore {\n rootDir: string;\n get(cacheKey: string): Promise<CacheEntry | null>;\n put(entry: CacheEntry): Promise<void>;\n}\n\nexport class LocalCacheStore implements CacheStore {\n rootDir: string;\n constructor(rootDir: string) {\n this.rootDir = rootDir;\n }\n\n private filePath(cacheKey: string): string {\n return path.join(this.rootDir, `${cacheKey}.json`);\n }\n\n async get(cacheKey: string): Promise<CacheEntry | null> {\n try {\n const buf = await readFile(this.filePath(cacheKey), \"utf8\");\n return JSON.parse(buf) as CacheEntry;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw e;\n }\n }\n\n async put(entry: CacheEntry): Promise<void> {\n const dest = this.filePath(entry.cacheKey);\n await mkdir(path.dirname(dest), { recursive: true });\n const tmp = `${dest}.tmp-${process.pid}-${randomUUID()}`;\n await writeFile(tmp, JSON.stringify(entry, null, 0));\n await rename(tmp, dest);\n }\n}\n"],"mappings":"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","names":["configure","str","value","message","tmp","key","res","join","stringify","path","path","message","message","path","message","size","cjsModule","cjsModule","normalized","text","durationSeconds","text","distance","durationSeconds","path","z","z","z","sleep","mkdir","unlink","writeFile","path","path","mkdir","writeFile","unlink","estimateNodeCredits","z","tail","readFile","path","path","readFile","text","path","z","sub","path","field","z","z","text","z","text","pictures","sub","size","at","z","entries","text","strings","tail","Hex","z","mustKeep","text","described","message","text","sentences","pct","at","size","PHONE_CAMERA","designedLines","p","message","ratioValue","size","ratioValue","size","px","showsPeople","compiled","text","at","size","CODES","text","CODES","text","esc","readFile","path","wordCount","text","CODES","path","readFile","estSpeechS","wordCount","clip","extract","z","plain","path","field","text","tail","estimateNodeCredits","readFile","stat","path","z","message","tierOf","message","readFile","path","writeFile","path","text","phrase","message","text","path","readFile","z","normalized","path","sharp","scale","pipeline","offset","size","timescale","duration","message","stat","readFile","path","stat","readFile","message","z","execFileCb","spawn","copyFile","mkdtemp","readFile","rm","stat","tmpdir","path","promisify","execFile","DEFAULT_TIMEOUT_MS","spawnAndWait","text","z","at","z","FFMPEG_BIN","z","z","z","z","FFMPEG_BIN","z","mkdtemp","rm","writeFile","tmpdir","path","z","copyFile","readFile","path","z","text","mkdtemp","path","tmpdir","writeFile","rm","execFile","mkdtemp","readFile","rm","stat","writeFile","tmpdir","path","promisify","z","readdir","readFile","stat","path","entries","readdir","path","stat","readFile","readFile","path","z","copyFile","createRequire","path","require_","mkdtemp","readFile","rm","stat","writeFile","tmpdir","path","execFile","readFile","path","promisify","text","promisify","execFile","message","entries","text","readFile","path","writeFile","path","readFile","path","mkdtemp","tmpdir","rm","stat","writeFile","text","execFileAsync","promisify","execFile","z","mkdtemp","path","tmpdir","stageInputs","substitutionValues","readFile","rm","stat","tail","writeFile","execFile","mkdtemp","readFile","rm","writeFile","createRequire","tmpdir","path","promisify","z","promisify","execFile","require_","createRequire","z","NODE_OWNED_PARAM_KEYS","DEVICE_SCALE_FACTOR","validateAndParseDynamicParams","mkdtemp","path","tmpdir","stageInputs","substitutionValues","buildSubstitutionValues","substituteCompositionFiles","rm","coerceImageParam","readFile","writeFile","execFile","promisify","z","execFileAsync","promisify","execFile","OutputDecl","z","createRequire","z","at","x","createHash","readFile","sharp","z","sharp","offset","sharp","distance","text","size","roleKey","text","execFile","mkdtemp","readdir","readFile","rm","tmpdir","path","promisify","z","z","text","at","plain","execFileAsync","promisify","execFile","readFile","mkdtemp","path","tmpdir","rm","readdir","sharp","z","z","text","sharp","z","text","FIGURE_ROLES","roleKey","HEADLINE_ROLES","tallest","size","BUTTON_ROLES","FINE_PRINT_ROLES","PHONE_WIDTH","createHash","readFile","sharp","ask","require_","createRequire","z","size","z","z","z","z","sharp","sharp","roleKey","at","sharp","px","ratioOf","sharp","px","plain","z","readFile","z","createHash","sharp","isButton","sharp","createHash","dropped","ask","text","z","Usage","readFile","z","Usage","z","Fit","Copy","z","z","z","FFMPEG_BIN","z","clip","normalize","offset","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","mkdtemp","readFile","rm","tmpdir","path","z","execFile","promisify","execFileAsync","num","normalize","similarity","_","wordCount","spans","text","z","readFile","mkdtemp","path","tmpdir","rm","z","z","z","entries","z","randomUUID","mkdir","readFile","rename","writeFile","path","BackendClient","Engine","LocalAssetStore","LocalCacheStore","validateCanvasDeep","generateCatalog","ValidationError","path"]}