@koda-sl/baker-cli 0.422.0-dev.f9f855bf1 → 0.423.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +211 -45
  2. package/dist/{chunk-VF77MVEM.js → chunk-3X2EMLBQ.js} +3 -3
  3. package/dist/{chunk-D76DSHL2.js → chunk-5JVAP5DC.js} +28 -1
  4. package/dist/{chunk-D76DSHL2.js.map → chunk-5JVAP5DC.js.map} +1 -1
  5. package/dist/{chunk-4RN4CBIQ.js → chunk-G5L5FX2B.js} +3 -3
  6. package/dist/{chunk-HFH5HQTP.js → chunk-S36N4SOX.js} +43 -4
  7. package/dist/chunk-S36N4SOX.js.map +1 -0
  8. package/dist/chunk-SWYBHYB5.js +24919 -0
  9. package/dist/chunk-SWYBHYB5.js.map +1 -0
  10. package/dist/{chunk-TTBMFAGF.js → chunk-XNFV6BQU.js} +4 -4
  11. package/dist/cli.js +9568 -14799
  12. package/dist/cli.js.map +1 -1
  13. package/dist/{client-OA2QMHOI.js → client-PIKJZFRA.js} +4 -4
  14. package/dist/engine/index.d.ts +8 -0
  15. package/dist/engine/index.js +3 -3
  16. package/dist/{env-Q5FUIVIJ.js → env-6UAIAY4M.js} +6 -2
  17. package/dist/{output-NMXF2VIL.js → output-CFDBWWYJ.js} +5 -5
  18. package/dist/{shared-PD226SU6.js → shared-3O5RKQYZ.js} +6 -6
  19. package/package.json +4 -1
  20. package/dist/chunk-FQ7WNNOG.js +0 -11637
  21. package/dist/chunk-FQ7WNNOG.js.map +0 -1
  22. package/dist/chunk-HFH5HQTP.js.map +0 -1
  23. /package/dist/{chunk-VF77MVEM.js.map → chunk-3X2EMLBQ.js.map} +0 -0
  24. /package/dist/{chunk-4RN4CBIQ.js.map → chunk-G5L5FX2B.js.map} +0 -0
  25. /package/dist/{chunk-TTBMFAGF.js.map → chunk-XNFV6BQU.js.map} +0 -0
  26. /package/dist/{client-OA2QMHOI.js.map → client-PIKJZFRA.js.map} +0 -0
  27. /package/dist/{env-Q5FUIVIJ.js.map → env-6UAIAY4M.js.map} +0 -0
  28. /package/dist/{output-NMXF2VIL.js.map → output-CFDBWWYJ.js.map} +0 -0
  29. /package/dist/{shared-PD226SU6.js.map → shared-3O5RKQYZ.js.map} +0 -0
@@ -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/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/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/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/text.ts","../src/engine/nodes/local/videoConcat.ts","../src/engine/nodes/remote/audioVoiceConvert.ts","../src/engine/nodes/remote/dialogue.ts","../src/engine/nodes/remote/image.ts","../src/engine/nodes/remote/registry-params.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 { 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];\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 * Whether a failed run died only on outages: every failed node timed out, lost the\n * network, or was refused by a provider that says to retry. Such a run tells nothing about\n * the canvas, so it is not held against the creative's render budget (render-budget.ts).\n * A failed check, a content-policy block or a refused input fails again on retry.\n */\nexport function failedOnlyOnTransientErrors(e: unknown): boolean {\n const reasons = e instanceof LayerExecutionError ? e.failures.map((f) => f.reason) : [e];\n return (\n reasons.length > 0 && reasons.every((r) => r instanceof NodeExecutionError && failureDetail(r).retryable === true)\n );\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 * 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// 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 * 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 — the\n // 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 },\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.\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 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 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 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 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 * 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 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/** 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","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 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 * 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 * 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.\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};\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};\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};\n\nexport const RECRAFT_IMAGE_PROFILE: ImageModelProfile = {\n id: \"recraft\",\n constraintPlacement: \"inline\",\n photorealCue: false,\n paramDefaults: {},\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_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 { imageProfileFor } from \"../../../lib/prompt-profiles.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 * 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. The fix is always the terminal `image_aspect_adapt` node: render ONE\n * hero, re-frame it into every other ratio from that single image.\n */\nfunction checkParallelFormatRenders(ctx: LintContext): void {\n // A render fed another render's (or a judge's) output as a reference IS the re-frame the\n // rule asks for: the static-ad scaffold re-lays its chosen ad this way, from the ad itself.\n const rendered = new Set(\n ctx.canvas.nodes.filter((n) => n.type === \"image_generate\" || n.type === \"image_select\").map((n) => n.id),\n );\n const reframedFromSibling = (inputs: unknown): boolean => {\n const reference = (inputs as { reference?: unknown } | undefined)?.reference;\n const refs = Array.isArray(reference) ? reference : [reference];\n return refs.some((r) => typeof r === \"string\" && rendered.has(r.match(/^\\$ref:([a-z][a-z0-9_]*)\\./)?.[1] ?? \"\"));\n };\n const candidates: FormatRenderNode[] = [];\n for (const n of ctx.canvas.nodes) {\n if (n.type !== \"image_generate\" || reframedFromSibling(n.inputs)) 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 for (let i = 0; i < ctx.canvas.nodes.length; i++) {\n const n = ctx.canvas.nodes[i];\n if (n?.type === \"image_generate\") 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 | { kind: \"plan\"; nodes: Array<{ node_id: string; node_type: string; deps: string[]; params: unknown }> }\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 return { node_id: id, node_type: node?.type ?? \"unknown\", deps: [...deps], params: node?.params };\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 { 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 = \"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 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: yt-dlp for 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 from the local filesystem 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\"` for local files (canvas is not portable across machines without the file). 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 \"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// 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(error.stderrTail ? `${error.message}\\n${error.stderrTail}` : error.message),\n });\n }\n throw error;\n }\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 (p.source === \"url\" && (p.expect === \"video\" || p.expect === \"audio\")) {\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\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 \"-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 ];\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 { 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 { IMAGE_GENERATE_MODELS } 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\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 // `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 cost: () => ({ credits: 5, seconds_estimate: 10 }),\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","import {\n AD_FORMAT_PLATFORMS,\n type AdFormatPlatform,\n OPENROUTER_IMAGE_SIZES,\n resolveAdaptFormats,\n} 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 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;\nconst PRO_ADAPT_MODELS: ReadonlySet<string> = new Set([\n \"google/gemini-3-pro-image\",\n \"google/gemini-3-pro-image-preview\",\n]);\n\n// Ad-format ratios only — shared by both models. The extreme banner ratios\n// (1:8, 8:1…) are deliberately excluded: this node adapts ad creatives, and\n// the registry cannot gate array elements per model.\nconst 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\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 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 * (PRO_ADAPT_MODELS.has(String(p?.model)) ? 20 : 5),\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 { 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;\nconst PRO_SHEET_MODELS: ReadonlySet<string> = new Set([\n \"google/gemini-3-pro-image\",\n \"google/gemini-3-pro-image-preview\",\n]);\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: PRO_SHEET_MODELS.has(String((params as { model?: string } | undefined)?.model)) ? 20 : 5,\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 2+ 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.\",\n when_to_use:\n \"Use after fanning out several image_generate variants (or any pool of 2+ 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(2) }).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 outputs: z.object({ images: z.array(ImageRef).min(1), reasoning: TextRef }).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 < 2) {\n issues.push({ path: \"inputs.images\", message: \"image_select needs at least 2 images 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","value","message","tmp","key","res","join","stringify","path","path","message","message","path","message","cjsModule","cjsModule","durationSeconds","distance","durationSeconds","path","z","z","z","sleep","mkdir","unlink","writeFile","path","path","mkdir","writeFile","unlink","estimateNodeCredits","z","tail","readFile","path","path","readFile","path","z","CODES","CODES","readFile","path","CODES","path","readFile","estSpeechS","extract","z","path","tail","estimateNodeCredits","readFile","stat","path","z","message","message","readFile","path","writeFile","path","message","path","readFile","z","path","scale","pipeline","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","z","at","z","FFMPEG_BIN","z","z","z","z","FFMPEG_BIN","z","mkdtemp","rm","writeFile","tmpdir","path","z","copyFile","readFile","path","z","mkdtemp","path","tmpdir","writeFile","rm","execFile","mkdtemp","readFile","rm","stat","writeFile","tmpdir","path","promisify","z","readdir","readFile","stat","path","readdir","path","stat","readFile","readFile","path","z","copyFile","createRequire","path","require_","mkdtemp","readFile","rm","stat","writeFile","tmpdir","path","execFile","readFile","path","promisify","promisify","execFile","message","readFile","path","writeFile","path","readFile","path","mkdtemp","tmpdir","rm","stat","writeFile","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","z","z","z","FFMPEG_BIN","z","normalize","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","z","z","z","mkdtemp","readFile","rm","tmpdir","path","z","execFile","promisify","execFileAsync","num","_","wordCount","z","readFile","mkdtemp","path","tmpdir","rm","z","z","z","z","randomUUID","mkdir","readFile","rename","writeFile","path","BackendClient","Engine","LocalAssetStore","LocalCacheStore","validateCanvasDeep","generateCatalog","ValidationError","path"]}