@nodetool-ai/agents 0.7.0-rc.23 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/analysis/audio-dsp.d.ts +223 -0
- package/dist/analysis/audio-dsp.d.ts.map +1 -0
- package/dist/analysis/audio-dsp.js +642 -0
- package/dist/analysis/audio-dsp.js.map +1 -0
- package/dist/analysis/media-decode.d.ts +85 -0
- package/dist/analysis/media-decode.d.ts.map +1 -0
- package/dist/analysis/media-decode.js +199 -0
- package/dist/analysis/media-decode.js.map +1 -0
- package/dist/analysis/mediabunny-runtime.d.ts +15 -0
- package/dist/analysis/mediabunny-runtime.d.ts.map +1 -0
- package/dist/analysis/mediabunny-runtime.js +30 -0
- package/dist/analysis/mediabunny-runtime.js.map +1 -0
- package/dist/analysis/video-frames.d.ts +120 -0
- package/dist/analysis/video-frames.d.ts.map +1 -0
- package/dist/analysis/video-frames.js +288 -0
- package/dist/analysis/video-frames.js.map +1 -0
- package/dist/apify/assets.d.ts +80 -0
- package/dist/apify/assets.d.ts.map +1 -0
- package/dist/apify/assets.js +192 -0
- package/dist/apify/assets.js.map +1 -0
- package/dist/apify/catalog.d.ts +44 -0
- package/dist/apify/catalog.d.ts.map +1 -0
- package/dist/apify/catalog.js +108 -0
- package/dist/apify/catalog.js.map +1 -0
- package/dist/apify/client.d.ts +137 -0
- package/dist/apify/client.d.ts.map +1 -0
- package/dist/apify/client.js +346 -0
- package/dist/apify/client.js.map +1 -0
- package/dist/apify/errors.d.ts +85 -0
- package/dist/apify/errors.d.ts.map +1 -0
- package/dist/apify/errors.js +90 -0
- package/dist/apify/errors.js.map +1 -0
- package/dist/apify/normalize.d.ts +131 -0
- package/dist/apify/normalize.d.ts.map +1 -0
- package/dist/apify/normalize.js +202 -0
- package/dist/apify/normalize.js.map +1 -0
- package/dist/apify/policy.d.ts +136 -0
- package/dist/apify/policy.d.ts.map +1 -0
- package/dist/apify/policy.js +223 -0
- package/dist/apify/policy.js.map +1 -0
- package/dist/apify/run.d.ts +86 -0
- package/dist/apify/run.d.ts.map +1 -0
- package/dist/apify/run.js +158 -0
- package/dist/apify/run.js.map +1 -0
- package/dist/app-build/author.d.ts +67 -0
- package/dist/app-build/author.d.ts.map +1 -0
- package/dist/app-build/author.js +231 -0
- package/dist/app-build/author.js.map +1 -0
- package/dist/app-build/bridge.d.ts +130 -0
- package/dist/app-build/bridge.d.ts.map +1 -0
- package/dist/app-build/bridge.js +647 -0
- package/dist/app-build/bridge.js.map +1 -0
- package/dist/app-build/build-service.d.ts +66 -0
- package/dist/app-build/build-service.d.ts.map +1 -0
- package/dist/app-build/build-service.js +249 -0
- package/dist/app-build/build-service.js.map +1 -0
- package/dist/app-build/build.d.ts +115 -0
- package/dist/app-build/build.d.ts.map +1 -0
- package/dist/app-build/build.js +901 -0
- package/dist/app-build/build.js.map +1 -0
- package/dist/app-build/interactions.d.ts +20 -0
- package/dist/app-build/interactions.d.ts.map +1 -0
- package/dist/app-build/interactions.js +121 -0
- package/dist/app-build/interactions.js.map +1 -0
- package/dist/app-build/judge.d.ts +113 -0
- package/dist/app-build/judge.d.ts.map +1 -0
- package/dist/app-build/judge.js +318 -0
- package/dist/app-build/judge.js.map +1 -0
- package/dist/app-build/markdown.d.ts +10 -0
- package/dist/app-build/markdown.d.ts.map +1 -0
- package/dist/app-build/markdown.js +65 -0
- package/dist/app-build/markdown.js.map +1 -0
- package/dist/app-build/spec.d.ts +228 -0
- package/dist/app-build/spec.d.ts.map +1 -0
- package/dist/app-build/spec.js +651 -0
- package/dist/app-build/spec.js.map +1 -0
- package/dist/app-build/tool-loop.d.ts +84 -0
- package/dist/app-build/tool-loop.d.ts.map +1 -0
- package/dist/app-build/tool-loop.js +113 -0
- package/dist/app-build/tool-loop.js.map +1 -0
- package/dist/app-build/types.d.ts +215 -0
- package/dist/app-build/types.d.ts.map +1 -0
- package/dist/app-build/types.js +28 -0
- package/dist/app-build/types.js.map +1 -0
- package/dist/author-graph.d.ts +91 -0
- package/dist/author-graph.d.ts.map +1 -0
- package/dist/author-graph.js +319 -0
- package/dist/author-graph.js.map +1 -0
- package/dist/background-subtasks.d.ts +79 -0
- package/dist/background-subtasks.d.ts.map +1 -0
- package/dist/background-subtasks.js +139 -0
- package/dist/background-subtasks.js.map +1 -0
- package/dist/blender-gate.d.ts +21 -0
- package/dist/blender-gate.d.ts.map +1 -0
- package/dist/blender-gate.js +27 -0
- package/dist/blender-gate.js.map +1 -0
- package/dist/browser-gate.d.ts +28 -0
- package/dist/browser-gate.d.ts.map +1 -0
- package/dist/browser-gate.js +34 -0
- package/dist/browser-gate.js.map +1 -0
- package/dist/capabilities/adapters.d.ts +36 -0
- package/dist/capabilities/adapters.d.ts.map +1 -0
- package/dist/capabilities/adapters.js +44 -0
- package/dist/capabilities/adapters.js.map +1 -0
- package/dist/capabilities/agents.d.ts +78 -0
- package/dist/capabilities/agents.d.ts.map +1 -0
- package/dist/capabilities/agents.js +366 -0
- package/dist/capabilities/agents.js.map +1 -0
- package/dist/capabilities/agents.specs.d.ts +27 -0
- package/dist/capabilities/agents.specs.d.ts.map +1 -0
- package/dist/capabilities/agents.specs.js +243 -0
- package/dist/capabilities/agents.specs.js.map +1 -0
- package/dist/capabilities/analysis.d.ts +93 -0
- package/dist/capabilities/analysis.d.ts.map +1 -0
- package/dist/capabilities/analysis.js +812 -0
- package/dist/capabilities/analysis.js.map +1 -0
- package/dist/capabilities/analysis.specs.d.ts +39 -0
- package/dist/capabilities/analysis.specs.d.ts.map +1 -0
- package/dist/capabilities/analysis.specs.js +217 -0
- package/dist/capabilities/analysis.specs.js.map +1 -0
- package/dist/capabilities/apify.d.ts +34 -0
- package/dist/capabilities/apify.d.ts.map +1 -0
- package/dist/capabilities/apify.js +449 -0
- package/dist/capabilities/apify.js.map +1 -0
- package/dist/capabilities/apify.specs.d.ts +28 -0
- package/dist/capabilities/apify.specs.d.ts.map +1 -0
- package/dist/capabilities/apify.specs.js +232 -0
- package/dist/capabilities/apify.specs.js.map +1 -0
- package/dist/capabilities/apps.d.ts +40 -0
- package/dist/capabilities/apps.d.ts.map +1 -0
- package/dist/capabilities/apps.js +413 -0
- package/dist/capabilities/apps.js.map +1 -0
- package/dist/capabilities/apps.specs.d.ts +23 -0
- package/dist/capabilities/apps.specs.d.ts.map +1 -0
- package/dist/capabilities/apps.specs.js +236 -0
- package/dist/capabilities/apps.specs.js.map +1 -0
- package/dist/capabilities/args.d.ts +65 -0
- package/dist/capabilities/args.d.ts.map +1 -0
- package/dist/capabilities/args.js +218 -0
- package/dist/capabilities/args.js.map +1 -0
- package/dist/capabilities/assets.d.ts +45 -0
- package/dist/capabilities/assets.d.ts.map +1 -0
- package/dist/capabilities/assets.js +952 -0
- package/dist/capabilities/assets.js.map +1 -0
- package/dist/capabilities/assets.specs.d.ts +54 -0
- package/dist/capabilities/assets.specs.d.ts.map +1 -0
- package/dist/capabilities/assets.specs.js +291 -0
- package/dist/capabilities/assets.specs.js.map +1 -0
- package/dist/capabilities/background-generation.d.ts +14 -0
- package/dist/capabilities/background-generation.d.ts.map +1 -0
- package/dist/capabilities/background-generation.js +29 -0
- package/dist/capabilities/background-generation.js.map +1 -0
- package/dist/capabilities/browser.d.ts +30 -0
- package/dist/capabilities/browser.d.ts.map +1 -0
- package/dist/capabilities/browser.js +250 -0
- package/dist/capabilities/browser.js.map +1 -0
- package/dist/capabilities/browser.specs.d.ts +37 -0
- package/dist/capabilities/browser.specs.d.ts.map +1 -0
- package/dist/capabilities/browser.specs.js +173 -0
- package/dist/capabilities/browser.specs.js.map +1 -0
- package/dist/capabilities/code-grading.d.ts +68 -0
- package/dist/capabilities/code-grading.d.ts.map +1 -0
- package/dist/capabilities/code-grading.js +75 -0
- package/dist/capabilities/code-grading.js.map +1 -0
- package/dist/capabilities/code.d.ts +56 -0
- package/dist/capabilities/code.d.ts.map +1 -0
- package/dist/capabilities/code.js +327 -0
- package/dist/capabilities/code.js.map +1 -0
- package/dist/capabilities/code.specs.d.ts +22 -0
- package/dist/capabilities/code.specs.d.ts.map +1 -0
- package/dist/capabilities/code.specs.js +175 -0
- package/dist/capabilities/code.specs.js.map +1 -0
- package/dist/capabilities/collections.d.ts +44 -0
- package/dist/capabilities/collections.d.ts.map +1 -0
- package/dist/capabilities/collections.js +432 -0
- package/dist/capabilities/collections.js.map +1 -0
- package/dist/capabilities/collections.specs.d.ts +25 -0
- package/dist/capabilities/collections.specs.d.ts.map +1 -0
- package/dist/capabilities/collections.specs.js +299 -0
- package/dist/capabilities/collections.specs.js.map +1 -0
- package/dist/capabilities/compositions.d.ts +57 -0
- package/dist/capabilities/compositions.d.ts.map +1 -0
- package/dist/capabilities/compositions.js +384 -0
- package/dist/capabilities/compositions.js.map +1 -0
- package/dist/capabilities/compositions.specs.d.ts +22 -0
- package/dist/capabilities/compositions.specs.d.ts.map +1 -0
- package/dist/capabilities/compositions.specs.js +126 -0
- package/dist/capabilities/compositions.specs.js.map +1 -0
- package/dist/capabilities/concurrency.d.ts +12 -0
- package/dist/capabilities/concurrency.d.ts.map +1 -0
- package/dist/capabilities/concurrency.js +30 -0
- package/dist/capabilities/concurrency.js.map +1 -0
- package/dist/capabilities/costs.d.ts +20 -0
- package/dist/capabilities/costs.d.ts.map +1 -0
- package/dist/capabilities/costs.js +40 -0
- package/dist/capabilities/costs.js.map +1 -0
- package/dist/capabilities/costs.specs.d.ts +12 -0
- package/dist/capabilities/costs.specs.d.ts.map +1 -0
- package/dist/capabilities/costs.specs.js +35 -0
- package/dist/capabilities/costs.specs.js.map +1 -0
- package/dist/capabilities/dispatcher.d.ts +57 -0
- package/dist/capabilities/dispatcher.d.ts.map +1 -0
- package/dist/capabilities/dispatcher.js +117 -0
- package/dist/capabilities/dispatcher.js.map +1 -0
- package/dist/capabilities/documents.d.ts +25 -0
- package/dist/capabilities/documents.d.ts.map +1 -0
- package/dist/capabilities/documents.js +278 -0
- package/dist/capabilities/documents.js.map +1 -0
- package/dist/capabilities/documents.specs.d.ts +18 -0
- package/dist/capabilities/documents.specs.d.ts.map +1 -0
- package/dist/capabilities/documents.specs.js +201 -0
- package/dist/capabilities/documents.specs.js.map +1 -0
- package/dist/capabilities/email.d.ts +23 -0
- package/dist/capabilities/email.d.ts.map +1 -0
- package/dist/capabilities/email.js +226 -0
- package/dist/capabilities/email.js.map +1 -0
- package/dist/capabilities/email.specs.d.ts +16 -0
- package/dist/capabilities/email.specs.d.ts.map +1 -0
- package/dist/capabilities/email.specs.js +102 -0
- package/dist/capabilities/email.specs.js.map +1 -0
- package/dist/capabilities/entities.d.ts +52 -0
- package/dist/capabilities/entities.d.ts.map +1 -0
- package/dist/capabilities/entities.js +478 -0
- package/dist/capabilities/entities.js.map +1 -0
- package/dist/capabilities/entities.specs.d.ts +25 -0
- package/dist/capabilities/entities.specs.d.ts.map +1 -0
- package/dist/capabilities/entities.specs.js +247 -0
- package/dist/capabilities/entities.specs.js.map +1 -0
- package/dist/capabilities/example-timelines.d.ts +3 -0
- package/dist/capabilities/example-timelines.d.ts.map +1 -0
- package/dist/capabilities/example-timelines.js +191 -0
- package/dist/capabilities/example-timelines.js.map +1 -0
- package/dist/capabilities/example-timelines.specs.d.ts +5 -0
- package/dist/capabilities/example-timelines.specs.d.ts.map +1 -0
- package/dist/capabilities/example-timelines.specs.js +53 -0
- package/dist/capabilities/example-timelines.specs.js.map +1 -0
- package/dist/capabilities/files.d.ts +53 -0
- package/dist/capabilities/files.d.ts.map +1 -0
- package/dist/capabilities/files.js +1172 -0
- package/dist/capabilities/files.js.map +1 -0
- package/dist/capabilities/files.specs.d.ts +28 -0
- package/dist/capabilities/files.specs.d.ts.map +1 -0
- package/dist/capabilities/files.specs.js +259 -0
- package/dist/capabilities/files.specs.js.map +1 -0
- package/dist/capabilities/flow.d.ts +93 -0
- package/dist/capabilities/flow.d.ts.map +1 -0
- package/dist/capabilities/flow.js +285 -0
- package/dist/capabilities/flow.js.map +1 -0
- package/dist/capabilities/flow.specs.d.ts +25 -0
- package/dist/capabilities/flow.specs.d.ts.map +1 -0
- package/dist/capabilities/flow.specs.js +114 -0
- package/dist/capabilities/flow.specs.js.map +1 -0
- package/dist/capabilities/game-asset-source.d.ts +31 -0
- package/dist/capabilities/game-asset-source.d.ts.map +1 -0
- package/dist/capabilities/game-asset-source.js +81 -0
- package/dist/capabilities/game-asset-source.js.map +1 -0
- package/dist/capabilities/game.d.ts +12 -0
- package/dist/capabilities/game.d.ts.map +1 -0
- package/dist/capabilities/game.js +1262 -0
- package/dist/capabilities/game.js.map +1 -0
- package/dist/capabilities/game.specs.d.ts +3 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -0
- package/dist/capabilities/game.specs.js +96 -0
- package/dist/capabilities/game.specs.js.map +1 -0
- package/dist/capabilities/gate-from-context.d.ts +39 -0
- package/dist/capabilities/gate-from-context.d.ts.map +1 -0
- package/dist/capabilities/gate-from-context.js +97 -0
- package/dist/capabilities/gate-from-context.js.map +1 -0
- package/dist/capabilities/gate-tools.d.ts +18 -0
- package/dist/capabilities/gate-tools.d.ts.map +1 -0
- package/dist/capabilities/gate-tools.js +66 -0
- package/dist/capabilities/gate-tools.js.map +1 -0
- package/dist/capabilities/generations.d.ts +79 -0
- package/dist/capabilities/generations.d.ts.map +1 -0
- package/dist/capabilities/generations.js +382 -0
- package/dist/capabilities/generations.js.map +1 -0
- package/dist/capabilities/generations.specs.d.ts +62 -0
- package/dist/capabilities/generations.specs.d.ts.map +1 -0
- package/dist/capabilities/generations.specs.js +230 -0
- package/dist/capabilities/generations.specs.js.map +1 -0
- package/dist/capabilities/google.d.ts +50 -0
- package/dist/capabilities/google.d.ts.map +1 -0
- package/dist/capabilities/google.js +246 -0
- package/dist/capabilities/google.js.map +1 -0
- package/dist/capabilities/google.specs.d.ts +52 -0
- package/dist/capabilities/google.specs.d.ts.map +1 -0
- package/dist/capabilities/google.specs.js +391 -0
- package/dist/capabilities/google.specs.js.map +1 -0
- package/dist/capabilities/html-text.d.ts +28 -0
- package/dist/capabilities/html-text.d.ts.map +1 -0
- package/dist/capabilities/html-text.js +76 -0
- package/dist/capabilities/html-text.js.map +1 -0
- package/dist/capabilities/index.d.ts +19 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +14 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/invoke.d.ts +89 -0
- package/dist/capabilities/invoke.d.ts.map +1 -0
- package/dist/capabilities/invoke.js +214 -0
- package/dist/capabilities/invoke.js.map +1 -0
- package/dist/capabilities/jobs.d.ts +32 -0
- package/dist/capabilities/jobs.d.ts.map +1 -0
- package/dist/capabilities/jobs.js +123 -0
- package/dist/capabilities/jobs.js.map +1 -0
- package/dist/capabilities/jobs.specs.d.ts +20 -0
- package/dist/capabilities/jobs.specs.d.ts.map +1 -0
- package/dist/capabilities/jobs.specs.js +103 -0
- package/dist/capabilities/jobs.specs.js.map +1 -0
- package/dist/capabilities/js-scripts.d.ts +90 -0
- package/dist/capabilities/js-scripts.d.ts.map +1 -0
- package/dist/capabilities/js-scripts.js +700 -0
- package/dist/capabilities/js-scripts.js.map +1 -0
- package/dist/capabilities/js-scripts.specs.d.ts +50 -0
- package/dist/capabilities/js-scripts.specs.d.ts.map +1 -0
- package/dist/capabilities/js-scripts.specs.js +344 -0
- package/dist/capabilities/js-scripts.specs.js.map +1 -0
- package/dist/capabilities/lazy-tool.d.ts +42 -0
- package/dist/capabilities/lazy-tool.d.ts.map +1 -0
- package/dist/capabilities/lazy-tool.js +88 -0
- package/dist/capabilities/lazy-tool.js.map +1 -0
- package/dist/capabilities/local-browser.d.ts +24 -0
- package/dist/capabilities/local-browser.d.ts.map +1 -0
- package/dist/capabilities/local-browser.js +105 -0
- package/dist/capabilities/local-browser.js.map +1 -0
- package/dist/capabilities/media.d.ts +121 -0
- package/dist/capabilities/media.d.ts.map +1 -0
- package/dist/capabilities/media.js +2217 -0
- package/dist/capabilities/media.js.map +1 -0
- package/dist/capabilities/media.specs.d.ts +65 -0
- package/dist/capabilities/media.specs.d.ts.map +1 -0
- package/dist/capabilities/media.specs.js +779 -0
- package/dist/capabilities/media.specs.js.map +1 -0
- package/dist/capabilities/memory.d.ts +31 -0
- package/dist/capabilities/memory.d.ts.map +1 -0
- package/dist/capabilities/memory.js +403 -0
- package/dist/capabilities/memory.js.map +1 -0
- package/dist/capabilities/memory.specs.d.ts +55 -0
- package/dist/capabilities/memory.specs.d.ts.map +1 -0
- package/dist/capabilities/memory.specs.js +208 -0
- package/dist/capabilities/memory.specs.js.map +1 -0
- package/dist/capabilities/model3d.d.ts +29 -0
- package/dist/capabilities/model3d.d.ts.map +1 -0
- package/dist/capabilities/model3d.js +482 -0
- package/dist/capabilities/model3d.js.map +1 -0
- package/dist/capabilities/model3d.specs.d.ts +28 -0
- package/dist/capabilities/model3d.specs.d.ts.map +1 -0
- package/dist/capabilities/model3d.specs.js +305 -0
- package/dist/capabilities/model3d.specs.js.map +1 -0
- package/dist/capabilities/models.d.ts +138 -0
- package/dist/capabilities/models.d.ts.map +1 -0
- package/dist/capabilities/models.js +803 -0
- package/dist/capabilities/models.js.map +1 -0
- package/dist/capabilities/models.specs.d.ts +21 -0
- package/dist/capabilities/models.specs.d.ts.map +1 -0
- package/dist/capabilities/models.specs.js +141 -0
- package/dist/capabilities/models.specs.js.map +1 -0
- package/dist/capabilities/nodes.d.ts +26 -0
- package/dist/capabilities/nodes.d.ts.map +1 -0
- package/dist/capabilities/nodes.js +266 -0
- package/dist/capabilities/nodes.js.map +1 -0
- package/dist/capabilities/nodes.specs.d.ts +20 -0
- package/dist/capabilities/nodes.specs.d.ts.map +1 -0
- package/dist/capabilities/nodes.specs.js +106 -0
- package/dist/capabilities/nodes.specs.js.map +1 -0
- package/dist/capabilities/packs.d.ts +69 -0
- package/dist/capabilities/packs.d.ts.map +1 -0
- package/dist/capabilities/packs.js +246 -0
- package/dist/capabilities/packs.js.map +1 -0
- package/dist/capabilities/packs.specs.d.ts +27 -0
- package/dist/capabilities/packs.specs.d.ts.map +1 -0
- package/dist/capabilities/packs.specs.js +64 -0
- package/dist/capabilities/packs.specs.js.map +1 -0
- package/dist/capabilities/project-scope.d.ts +12 -0
- package/dist/capabilities/project-scope.d.ts.map +1 -0
- package/dist/capabilities/project-scope.js +19 -0
- package/dist/capabilities/project-scope.js.map +1 -0
- package/dist/capabilities/projects.d.ts +23 -0
- package/dist/capabilities/projects.d.ts.map +1 -0
- package/dist/capabilities/projects.js +206 -0
- package/dist/capabilities/projects.js.map +1 -0
- package/dist/capabilities/projects.specs.d.ts +23 -0
- package/dist/capabilities/projects.specs.d.ts.map +1 -0
- package/dist/capabilities/projects.specs.js +118 -0
- package/dist/capabilities/projects.specs.js.map +1 -0
- package/dist/capabilities/registry.d.ts +109 -0
- package/dist/capabilities/registry.d.ts.map +1 -0
- package/dist/capabilities/registry.js +445 -0
- package/dist/capabilities/registry.js.map +1 -0
- package/dist/capabilities/script-link.d.ts +22 -0
- package/dist/capabilities/script-link.d.ts.map +1 -0
- package/dist/capabilities/script-link.js +35 -0
- package/dist/capabilities/script-link.js.map +1 -0
- package/dist/capabilities/scripts.d.ts +43 -0
- package/dist/capabilities/scripts.d.ts.map +1 -0
- package/dist/capabilities/scripts.js +1789 -0
- package/dist/capabilities/scripts.js.map +1 -0
- package/dist/capabilities/scripts.specs.d.ts +40 -0
- package/dist/capabilities/scripts.specs.d.ts.map +1 -0
- package/dist/capabilities/scripts.specs.js +319 -0
- package/dist/capabilities/scripts.specs.js.map +1 -0
- package/dist/capabilities/serpapi.d.ts +42 -0
- package/dist/capabilities/serpapi.d.ts.map +1 -0
- package/dist/capabilities/serpapi.js +262 -0
- package/dist/capabilities/serpapi.js.map +1 -0
- package/dist/capabilities/serpapi.specs.d.ts +24 -0
- package/dist/capabilities/serpapi.specs.d.ts.map +1 -0
- package/dist/capabilities/serpapi.specs.js +174 -0
- package/dist/capabilities/serpapi.specs.js.map +1 -0
- package/dist/capabilities/settings.d.ts +48 -0
- package/dist/capabilities/settings.d.ts.map +1 -0
- package/dist/capabilities/settings.js +226 -0
- package/dist/capabilities/settings.js.map +1 -0
- package/dist/capabilities/settings.specs.d.ts +42 -0
- package/dist/capabilities/settings.specs.d.ts.map +1 -0
- package/dist/capabilities/settings.specs.js +126 -0
- package/dist/capabilities/settings.specs.js.map +1 -0
- package/dist/capabilities/shared.d.ts +35 -0
- package/dist/capabilities/shared.d.ts.map +1 -0
- package/dist/capabilities/shared.js +165 -0
- package/dist/capabilities/shared.js.map +1 -0
- package/dist/capabilities/shared.specs.d.ts +19 -0
- package/dist/capabilities/shared.specs.d.ts.map +1 -0
- package/dist/capabilities/shared.specs.js +134 -0
- package/dist/capabilities/shared.specs.js.map +1 -0
- package/dist/capabilities/sketches.d.ts +52 -0
- package/dist/capabilities/sketches.d.ts.map +1 -0
- package/dist/capabilities/sketches.js +1163 -0
- package/dist/capabilities/sketches.js.map +1 -0
- package/dist/capabilities/sketches.specs.d.ts +44 -0
- package/dist/capabilities/sketches.specs.d.ts.map +1 -0
- package/dist/capabilities/sketches.specs.js +392 -0
- package/dist/capabilities/sketches.specs.js.map +1 -0
- package/dist/capabilities/skills.d.ts +28 -0
- package/dist/capabilities/skills.d.ts.map +1 -0
- package/dist/capabilities/skills.js +269 -0
- package/dist/capabilities/skills.js.map +1 -0
- package/dist/capabilities/skills.specs.d.ts +17 -0
- package/dist/capabilities/skills.specs.d.ts.map +1 -0
- package/dist/capabilities/skills.specs.js +108 -0
- package/dist/capabilities/skills.specs.js.map +1 -0
- package/dist/capabilities/storyboards.d.ts +68 -0
- package/dist/capabilities/storyboards.d.ts.map +1 -0
- package/dist/capabilities/storyboards.js +2246 -0
- package/dist/capabilities/storyboards.js.map +1 -0
- package/dist/capabilities/storyboards.specs.d.ts +42 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -0
- package/dist/capabilities/storyboards.specs.js +418 -0
- package/dist/capabilities/storyboards.specs.js.map +1 -0
- package/dist/capabilities/threads.d.ts +25 -0
- package/dist/capabilities/threads.d.ts.map +1 -0
- package/dist/capabilities/threads.js +240 -0
- package/dist/capabilities/threads.js.map +1 -0
- package/dist/capabilities/threads.specs.d.ts +26 -0
- package/dist/capabilities/threads.specs.d.ts.map +1 -0
- package/dist/capabilities/threads.specs.js +108 -0
- package/dist/capabilities/threads.specs.js.map +1 -0
- package/dist/capabilities/timeline-audio-bake.d.ts +114 -0
- package/dist/capabilities/timeline-audio-bake.d.ts.map +1 -0
- package/dist/capabilities/timeline-audio-bake.js +188 -0
- package/dist/capabilities/timeline-audio-bake.js.map +1 -0
- package/dist/capabilities/timeline-bake.d.ts +21 -0
- package/dist/capabilities/timeline-bake.d.ts.map +1 -0
- package/dist/capabilities/timeline-bake.js +118 -0
- package/dist/capabilities/timeline-bake.js.map +1 -0
- package/dist/capabilities/timeline-isolate-subject.d.ts +157 -0
- package/dist/capabilities/timeline-isolate-subject.d.ts.map +1 -0
- package/dist/capabilities/timeline-isolate-subject.js +293 -0
- package/dist/capabilities/timeline-isolate-subject.js.map +1 -0
- package/dist/capabilities/timeline-native-media.d.ts +4 -0
- package/dist/capabilities/timeline-native-media.d.ts.map +1 -0
- package/dist/capabilities/timeline-native-media.js +920 -0
- package/dist/capabilities/timeline-native-media.js.map +1 -0
- package/dist/capabilities/timeline-track-object.d.ts +83 -0
- package/dist/capabilities/timeline-track-object.d.ts.map +1 -0
- package/dist/capabilities/timeline-track-object.js +195 -0
- package/dist/capabilities/timeline-track-object.js.map +1 -0
- package/dist/capabilities/timelines.d.ts +190 -0
- package/dist/capabilities/timelines.d.ts.map +1 -0
- package/dist/capabilities/timelines.js +2326 -0
- package/dist/capabilities/timelines.js.map +1 -0
- package/dist/capabilities/timelines.specs.d.ts +134 -0
- package/dist/capabilities/timelines.specs.d.ts.map +1 -0
- package/dist/capabilities/timelines.specs.js +1362 -0
- package/dist/capabilities/timelines.specs.js.map +1 -0
- package/dist/capabilities/types.d.ts +197 -0
- package/dist/capabilities/types.d.ts.map +1 -0
- package/dist/capabilities/types.js +19 -0
- package/dist/capabilities/types.js.map +1 -0
- package/dist/capabilities/ui.d.ts +38 -0
- package/dist/capabilities/ui.d.ts.map +1 -0
- package/dist/capabilities/ui.js +128 -0
- package/dist/capabilities/ui.js.map +1 -0
- package/dist/capabilities/ui.specs.d.ts +22 -0
- package/dist/capabilities/ui.specs.d.ts.map +1 -0
- package/dist/capabilities/ui.specs.js +59 -0
- package/dist/capabilities/ui.specs.js.map +1 -0
- package/dist/capabilities/video-production-acceptance.d.ts +27 -0
- package/dist/capabilities/video-production-acceptance.d.ts.map +1 -0
- package/dist/capabilities/video-production-acceptance.js +41 -0
- package/dist/capabilities/video-production-acceptance.js.map +1 -0
- package/dist/capabilities/video-production.d.ts +179 -0
- package/dist/capabilities/video-production.d.ts.map +1 -0
- package/dist/capabilities/video-production.js +1492 -0
- package/dist/capabilities/video-production.js.map +1 -0
- package/dist/capabilities/video-production.specs.d.ts +13 -0
- package/dist/capabilities/video-production.specs.d.ts.map +1 -0
- package/dist/capabilities/video-production.specs.js +256 -0
- package/dist/capabilities/video-production.specs.js.map +1 -0
- package/dist/capabilities/web.d.ts +86 -0
- package/dist/capabilities/web.d.ts.map +1 -0
- package/dist/capabilities/web.js +850 -0
- package/dist/capabilities/web.js.map +1 -0
- package/dist/capabilities/web.specs.d.ts +33 -0
- package/dist/capabilities/web.specs.d.ts.map +1 -0
- package/dist/capabilities/web.specs.js +286 -0
- package/dist/capabilities/web.specs.js.map +1 -0
- package/dist/capabilities/workflows.d.ts +58 -0
- package/dist/capabilities/workflows.d.ts.map +1 -0
- package/dist/capabilities/workflows.js +1530 -0
- package/dist/capabilities/workflows.js.map +1 -0
- package/dist/capabilities/workflows.specs.d.ts +63 -0
- package/dist/capabilities/workflows.specs.d.ts.map +1 -0
- package/dist/capabilities/workflows.specs.js +852 -0
- package/dist/capabilities/workflows.specs.js.map +1 -0
- package/dist/code-gen/analyze.d.ts +26 -0
- package/dist/code-gen/analyze.d.ts.map +1 -0
- package/dist/code-gen/analyze.js +79 -0
- package/dist/code-gen/analyze.js.map +1 -0
- package/dist/code-gen/port-types.d.ts +58 -0
- package/dist/code-gen/port-types.d.ts.map +1 -0
- package/dist/code-gen/port-types.js +115 -0
- package/dist/code-gen/port-types.js.map +1 -0
- package/dist/code-gen/prompt.d.ts +37 -0
- package/dist/code-gen/prompt.d.ts.map +1 -0
- package/dist/code-gen/prompt.js +129 -0
- package/dist/code-gen/prompt.js.map +1 -0
- package/dist/code-gen/sandbox-manifest.d.ts +120 -0
- package/dist/code-gen/sandbox-manifest.d.ts.map +1 -0
- package/dist/code-gen/sandbox-manifest.js +1023 -0
- package/dist/code-gen/sandbox-manifest.js.map +1 -0
- package/dist/code-gen/sandbox-prompt.d.ts +26 -0
- package/dist/code-gen/sandbox-prompt.d.ts.map +1 -0
- package/dist/code-gen/sandbox-prompt.js +126 -0
- package/dist/code-gen/sandbox-prompt.js.map +1 -0
- package/dist/code-planner.d.ts +72 -0
- package/dist/code-planner.d.ts.map +1 -0
- package/dist/code-planner.js +271 -0
- package/dist/code-planner.js.map +1 -0
- package/dist/codeact/action-diagnostics.d.ts +35 -0
- package/dist/codeact/action-diagnostics.d.ts.map +1 -0
- package/dist/codeact/action-diagnostics.js +147 -0
- package/dist/codeact/action-diagnostics.js.map +1 -0
- package/dist/codeact/capability-modules.d.ts +66 -0
- package/dist/codeact/capability-modules.d.ts.map +1 -0
- package/dist/codeact/capability-modules.js +278 -0
- package/dist/codeact/capability-modules.js.map +1 -0
- package/dist/codeact/chat-codeact.d.ts +104 -0
- package/dist/codeact/chat-codeact.d.ts.map +1 -0
- package/dist/codeact/chat-codeact.js +424 -0
- package/dist/codeact/chat-codeact.js.map +1 -0
- package/dist/codeact/codeact-executor.d.ts +247 -0
- package/dist/codeact/codeact-executor.d.ts.map +1 -0
- package/dist/codeact/codeact-executor.js +1176 -0
- package/dist/codeact/codeact-executor.js.map +1 -0
- package/dist/codeact/compact-ids.d.ts +28 -0
- package/dist/codeact/compact-ids.d.ts.map +1 -0
- package/dist/codeact/compact-ids.js +77 -0
- package/dist/codeact/compact-ids.js.map +1 -0
- package/dist/codeact/execute-code-contract.d.ts +97 -0
- package/dist/codeact/execute-code-contract.d.ts.map +1 -0
- package/dist/codeact/execute-code-contract.js +225 -0
- package/dist/codeact/execute-code-contract.js.map +1 -0
- package/dist/codeact/fabric-package.d.ts +29 -0
- package/dist/codeact/fabric-package.d.ts.map +1 -0
- package/dist/codeact/fabric-package.js +62 -0
- package/dist/codeact/fabric-package.js.map +1 -0
- package/dist/codeact/flow-package.d.ts +31 -0
- package/dist/codeact/flow-package.d.ts.map +1 -0
- package/dist/codeact/flow-package.js +80 -0
- package/dist/codeact/flow-package.js.map +1 -0
- package/dist/codeact/graph-dsl-package.d.ts +42 -0
- package/dist/codeact/graph-dsl-package.d.ts.map +1 -0
- package/dist/codeact/graph-dsl-package.js +100 -0
- package/dist/codeact/graph-dsl-package.js.map +1 -0
- package/dist/codeact/graph-model.d.ts +30 -0
- package/dist/codeact/graph-model.d.ts.map +1 -0
- package/dist/codeact/graph-model.js +408 -0
- package/dist/codeact/graph-model.js.map +1 -0
- package/dist/codeact/mcp-guest-contract.d.ts +66 -0
- package/dist/codeact/mcp-guest-contract.d.ts.map +1 -0
- package/dist/codeact/mcp-guest-contract.js +108 -0
- package/dist/codeact/mcp-guest-contract.js.map +1 -0
- package/dist/codeact/nodetool-api.d.ts +60 -0
- package/dist/codeact/nodetool-api.d.ts.map +1 -0
- package/dist/codeact/nodetool-api.js +2056 -0
- package/dist/codeact/nodetool-api.js.map +1 -0
- package/dist/codeact/prompt.d.ts +88 -0
- package/dist/codeact/prompt.d.ts.map +1 -0
- package/dist/codeact/prompt.js +326 -0
- package/dist/codeact/prompt.js.map +1 -0
- package/dist/codeact/sandbox-package-docs.d.ts +48 -0
- package/dist/codeact/sandbox-package-docs.d.ts.map +1 -0
- package/dist/codeact/sandbox-package-docs.js +55 -0
- package/dist/codeact/sandbox-package-docs.js.map +1 -0
- package/dist/codeact/sandbox-package-listing.d.ts +81 -0
- package/dist/codeact/sandbox-package-listing.d.ts.map +1 -0
- package/dist/codeact/sandbox-package-listing.js +101 -0
- package/dist/codeact/sandbox-package-listing.js.map +1 -0
- package/dist/codeact/sandbox-packages.d.ts +72 -0
- package/dist/codeact/sandbox-packages.d.ts.map +1 -0
- package/dist/codeact/sandbox-packages.js +230 -0
- package/dist/codeact/sandbox-packages.js.map +1 -0
- package/dist/codeact/timeline-package.d.ts +5 -0
- package/dist/codeact/timeline-package.d.ts.map +1 -0
- package/dist/codeact/timeline-package.js +10 -0
- package/dist/codeact/timeline-package.js.map +1 -0
- package/dist/codeact/tool-api.d.ts +197 -0
- package/dist/codeact/tool-api.d.ts.map +1 -0
- package/dist/codeact/tool-api.js +445 -0
- package/dist/codeact/tool-api.js.map +1 -0
- package/dist/codeact/tools-prelude.d.ts +32 -0
- package/dist/codeact/tools-prelude.d.ts.map +1 -0
- package/dist/codeact/tools-prelude.js +65 -0
- package/dist/codeact/tools-prelude.js.map +1 -0
- package/dist/constants.d.ts +47 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +64 -2
- package/dist/constants.js.map +1 -1
- package/dist/custom-animation-bake.d.ts +57 -0
- package/dist/custom-animation-bake.d.ts.map +1 -0
- package/dist/custom-animation-bake.js +88 -0
- package/dist/custom-animation-bake.js.map +1 -0
- package/dist/dynamic-slots.d.ts +72 -0
- package/dist/dynamic-slots.d.ts.map +1 -0
- package/dist/dynamic-slots.js +0 -0
- package/dist/dynamic-slots.js.map +1 -0
- package/dist/evals/app-build-cases.d.ts +35 -0
- package/dist/evals/app-build-cases.d.ts.map +1 -0
- package/dist/evals/app-build-cases.js +722 -0
- package/dist/evals/app-build-cases.js.map +1 -0
- package/dist/evals/app-build-eval.d.ts +208 -0
- package/dist/evals/app-build-eval.d.ts.map +1 -0
- package/dist/evals/app-build-eval.js +518 -0
- package/dist/evals/app-build-eval.js.map +1 -0
- package/dist/evals/app-build-plan.d.ts +76 -0
- package/dist/evals/app-build-plan.d.ts.map +1 -0
- package/dist/evals/app-build-plan.js +140 -0
- package/dist/evals/app-build-plan.js.map +1 -0
- package/dist/evals/code-gen-cases.d.ts +13 -0
- package/dist/evals/code-gen-cases.d.ts.map +1 -0
- package/dist/evals/code-gen-cases.js +124 -0
- package/dist/evals/code-gen-cases.js.map +1 -0
- package/dist/evals/code-gen-eval.d.ts +109 -0
- package/dist/evals/code-gen-eval.d.ts.map +1 -0
- package/dist/evals/code-gen-eval.js +299 -0
- package/dist/evals/code-gen-eval.js.map +1 -0
- package/dist/evals/codeact-api-cases.d.ts +14 -0
- package/dist/evals/codeact-api-cases.d.ts.map +1 -0
- package/dist/evals/codeact-api-cases.js +29 -0
- package/dist/evals/codeact-api-cases.js.map +1 -0
- package/dist/evals/codeact-api-core.d.ts +23 -0
- package/dist/evals/codeact-api-core.d.ts.map +1 -0
- package/dist/evals/codeact-api-core.js +1411 -0
- package/dist/evals/codeact-api-core.js.map +1 -0
- package/dist/evals/codeact-api-surfaces.d.ts +25 -0
- package/dist/evals/codeact-api-surfaces.d.ts.map +1 -0
- package/dist/evals/codeact-api-surfaces.js +2191 -0
- package/dist/evals/codeact-api-surfaces.js.map +1 -0
- package/dist/evals/codeact-cases.d.ts +76 -0
- package/dist/evals/codeact-cases.d.ts.map +1 -0
- package/dist/evals/codeact-cases.js +151 -0
- package/dist/evals/codeact-cases.js.map +1 -0
- package/dist/evals/codeact-eval.d.ts +66 -0
- package/dist/evals/codeact-eval.d.ts.map +1 -0
- package/dist/evals/codeact-eval.js +250 -0
- package/dist/evals/codeact-eval.js.map +1 -0
- package/dist/evals/codeact-sandbox-pack-cases.d.ts +31 -0
- package/dist/evals/codeact-sandbox-pack-cases.d.ts.map +1 -0
- package/dist/evals/codeact-sandbox-pack-cases.js +219 -0
- package/dist/evals/codeact-sandbox-pack-cases.js.map +1 -0
- package/dist/evals/escalation-cases.d.ts +22 -0
- package/dist/evals/escalation-cases.d.ts.map +1 -0
- package/dist/evals/escalation-cases.js +343 -0
- package/dist/evals/escalation-cases.js.map +1 -0
- package/dist/evals/escalation.d.ts +92 -0
- package/dist/evals/escalation.d.ts.map +1 -0
- package/dist/evals/escalation.js +152 -0
- package/dist/evals/escalation.js.map +1 -0
- package/dist/evals/goal-judge.d.ts +51 -0
- package/dist/evals/goal-judge.d.ts.map +1 -0
- package/dist/evals/goal-judge.js +152 -0
- package/dist/evals/goal-judge.js.map +1 -0
- package/dist/evals/graph-e2e-cases.d.ts +21 -0
- package/dist/evals/graph-e2e-cases.d.ts.map +1 -0
- package/dist/evals/graph-e2e-cases.js +262 -0
- package/dist/evals/graph-e2e-cases.js.map +1 -0
- package/dist/evals/graph-e2e-eval.d.ts +202 -0
- package/dist/evals/graph-e2e-eval.d.ts.map +1 -0
- package/dist/evals/graph-e2e-eval.js +438 -0
- package/dist/evals/graph-e2e-eval.js.map +1 -0
- package/dist/evals/graph-planner-cases.d.ts +12 -0
- package/dist/evals/graph-planner-cases.d.ts.map +1 -0
- package/dist/evals/graph-planner-cases.js +256 -0
- package/dist/evals/graph-planner-cases.js.map +1 -0
- package/dist/evals/graph-planner-eval.d.ts +178 -0
- package/dist/evals/graph-planner-eval.d.ts.map +1 -0
- package/dist/evals/graph-planner-eval.js +495 -0
- package/dist/evals/graph-planner-eval.js.map +1 -0
- package/dist/evals/planner-tools.d.ts +21 -0
- package/dist/evals/planner-tools.d.ts.map +1 -0
- package/dist/evals/planner-tools.js +66 -0
- package/dist/evals/planner-tools.js.map +1 -0
- package/dist/evals/subtask-cases.d.ts +88 -0
- package/dist/evals/subtask-cases.d.ts.map +1 -0
- package/dist/evals/subtask-cases.js +322 -0
- package/dist/evals/subtask-cases.js.map +1 -0
- package/dist/evals/subtask-eval.d.ts +129 -0
- package/dist/evals/subtask-eval.d.ts.map +1 -0
- package/dist/evals/subtask-eval.js +397 -0
- package/dist/evals/subtask-eval.js.map +1 -0
- package/dist/evals/surfaces/app.d.ts +19 -0
- package/dist/evals/surfaces/app.d.ts.map +1 -0
- package/dist/evals/surfaces/app.js +341 -0
- package/dist/evals/surfaces/app.js.map +1 -0
- package/dist/evals/surfaces/creative-pipeline.d.ts +203 -0
- package/dist/evals/surfaces/creative-pipeline.d.ts.map +1 -0
- package/dist/evals/surfaces/creative-pipeline.js +909 -0
- package/dist/evals/surfaces/creative-pipeline.js.map +1 -0
- package/dist/evals/surfaces/game.d.ts +5 -0
- package/dist/evals/surfaces/game.d.ts.map +1 -0
- package/dist/evals/surfaces/game.js +42 -0
- package/dist/evals/surfaces/game.js.map +1 -0
- package/dist/evals/surfaces/js-script.d.ts +66 -0
- package/dist/evals/surfaces/js-script.d.ts.map +1 -0
- package/dist/evals/surfaces/js-script.js +407 -0
- package/dist/evals/surfaces/js-script.js.map +1 -0
- package/dist/evals/surfaces/memory.d.ts +34 -0
- package/dist/evals/surfaces/memory.d.ts.map +1 -0
- package/dist/evals/surfaces/memory.js +264 -0
- package/dist/evals/surfaces/memory.js.map +1 -0
- package/dist/evals/surfaces/model3d.d.ts +45 -0
- package/dist/evals/surfaces/model3d.d.ts.map +1 -0
- package/dist/evals/surfaces/model3d.js +346 -0
- package/dist/evals/surfaces/model3d.js.map +1 -0
- package/dist/evals/surfaces/script.d.ts +96 -0
- package/dist/evals/surfaces/script.d.ts.map +1 -0
- package/dist/evals/surfaces/script.js +709 -0
- package/dist/evals/surfaces/script.js.map +1 -0
- package/dist/evals/surfaces/sketch.d.ts +124 -0
- package/dist/evals/surfaces/sketch.d.ts.map +1 -0
- package/dist/evals/surfaces/sketch.js +1298 -0
- package/dist/evals/surfaces/sketch.js.map +1 -0
- package/dist/evals/surfaces/storyboard.d.ts +97 -0
- package/dist/evals/surfaces/storyboard.d.ts.map +1 -0
- package/dist/evals/surfaces/storyboard.js +693 -0
- package/dist/evals/surfaces/storyboard.js.map +1 -0
- package/dist/evals/surfaces/timeline.d.ts +416 -0
- package/dist/evals/surfaces/timeline.d.ts.map +1 -0
- package/dist/evals/surfaces/timeline.js +3267 -0
- package/dist/evals/surfaces/timeline.js.map +1 -0
- package/dist/evals/task-planner-cases.d.ts +57 -0
- package/dist/evals/task-planner-cases.d.ts.map +1 -0
- package/dist/evals/task-planner-cases.js +117 -0
- package/dist/evals/task-planner-cases.js.map +1 -0
- package/dist/evals/task-planner-eval.d.ts +101 -0
- package/dist/evals/task-planner-eval.d.ts.map +1 -0
- package/dist/evals/task-planner-eval.js +380 -0
- package/dist/evals/task-planner-eval.js.map +1 -0
- package/dist/evals/tool-loop-bridge.d.ts +57 -0
- package/dist/evals/tool-loop-bridge.d.ts.map +1 -0
- package/dist/evals/tool-loop-bridge.js +129 -0
- package/dist/evals/tool-loop-bridge.js.map +1 -0
- package/dist/evals/tool-loop-cases.d.ts +21 -0
- package/dist/evals/tool-loop-cases.d.ts.map +1 -0
- package/dist/evals/tool-loop-cases.js +401 -0
- package/dist/evals/tool-loop-cases.js.map +1 -0
- package/dist/evals/tool-loop-eval.d.ts +234 -0
- package/dist/evals/tool-loop-eval.d.ts.map +1 -0
- package/dist/evals/tool-loop-eval.js +373 -0
- package/dist/evals/tool-loop-eval.js.map +1 -0
- package/dist/evals/tool-loop-permission.d.ts +43 -0
- package/dist/evals/tool-loop-permission.d.ts.map +1 -0
- package/dist/evals/tool-loop-permission.js +76 -0
- package/dist/evals/tool-loop-permission.js.map +1 -0
- package/dist/game-examples.d.ts +447 -0
- package/dist/game-examples.d.ts.map +1 -0
- package/dist/game-examples.js +187 -0
- package/dist/game-examples.js.map +1 -0
- package/dist/graph-builder.d.ts +55 -3
- package/dist/graph-builder.d.ts.map +1 -1
- package/dist/graph-builder.js +159 -4
- package/dist/graph-builder.js.map +1 -1
- package/dist/graph-dsl-core.d.ts +25 -0
- package/dist/graph-dsl-core.d.ts.map +1 -0
- package/dist/graph-dsl-core.js +312 -0
- package/dist/graph-dsl-core.js.map +1 -0
- package/dist/graph-dsl.d.ts +30 -0
- package/dist/graph-dsl.d.ts.map +1 -0
- package/dist/graph-dsl.js +116 -0
- package/dist/graph-dsl.js.map +1 -0
- package/dist/host-binaries.d.ts +6 -0
- package/dist/host-binaries.d.ts.map +1 -0
- package/dist/host-binaries.js +6 -0
- package/dist/host-binaries.js.map +1 -0
- package/dist/host-binary-guard.d.ts +100 -0
- package/dist/host-binary-guard.d.ts.map +1 -0
- package/dist/host-binary-guard.js +205 -0
- package/dist/host-binary-guard.js.map +1 -0
- package/dist/host-modules/aws.d.ts +57 -0
- package/dist/host-modules/aws.d.ts.map +1 -0
- package/dist/host-modules/aws.js +309 -0
- package/dist/host-modules/aws.js.map +1 -0
- package/dist/host-modules/chrono.d.ts +15 -0
- package/dist/host-modules/chrono.d.ts.map +1 -0
- package/dist/host-modules/chrono.js +35 -0
- package/dist/host-modules/chrono.js.map +1 -0
- package/dist/host-modules/csv.d.ts +17 -0
- package/dist/host-modules/csv.d.ts.map +1 -0
- package/dist/host-modules/csv.js +64 -0
- package/dist/host-modules/csv.js.map +1 -0
- package/dist/host-modules/diff.d.ts +9 -0
- package/dist/host-modules/diff.d.ts.map +1 -0
- package/dist/host-modules/diff.js +26 -0
- package/dist/host-modules/diff.js.map +1 -0
- package/dist/host-modules/dispatcher.d.ts +41 -0
- package/dist/host-modules/dispatcher.d.ts.map +1 -0
- package/dist/host-modules/dispatcher.js +99 -0
- package/dist/host-modules/dispatcher.js.map +1 -0
- package/dist/host-modules/docx.d.ts +19 -0
- package/dist/host-modules/docx.d.ts.map +1 -0
- package/dist/host-modules/docx.js +120 -0
- package/dist/host-modules/docx.js.map +1 -0
- package/dist/host-modules/epub.d.ts +48 -0
- package/dist/host-modules/epub.d.ts.map +1 -0
- package/dist/host-modules/epub.js +100 -0
- package/dist/host-modules/epub.js.map +1 -0
- package/dist/host-modules/exif.d.ts +8 -0
- package/dist/host-modules/exif.d.ts.map +1 -0
- package/dist/host-modules/exif.js +19 -0
- package/dist/host-modules/exif.js.map +1 -0
- package/dist/host-modules/expr.d.ts +16 -0
- package/dist/host-modules/expr.d.ts.map +1 -0
- package/dist/host-modules/expr.js +18 -0
- package/dist/host-modules/expr.js.map +1 -0
- package/dist/host-modules/fabric.d.ts +46 -0
- package/dist/host-modules/fabric.d.ts.map +1 -0
- package/dist/host-modules/fabric.js +178 -0
- package/dist/host-modules/fabric.js.map +1 -0
- package/dist/host-modules/html.d.ts +51 -0
- package/dist/host-modules/html.d.ts.map +1 -0
- package/dist/host-modules/html.js +198 -0
- package/dist/host-modules/html.js.map +1 -0
- package/dist/host-modules/ics.d.ts +9 -0
- package/dist/host-modules/ics.d.ts.map +1 -0
- package/dist/host-modules/ics.js +35 -0
- package/dist/host-modules/ics.js.map +1 -0
- package/dist/host-modules/limits.d.ts +41 -0
- package/dist/host-modules/limits.d.ts.map +1 -0
- package/dist/host-modules/limits.js +76 -0
- package/dist/host-modules/limits.js.map +1 -0
- package/dist/host-modules/mammoth.d.ts +11 -0
- package/dist/host-modules/mammoth.d.ts.map +1 -0
- package/dist/host-modules/mammoth.js +29 -0
- package/dist/host-modules/mammoth.js.map +1 -0
- package/dist/host-modules/notion.d.ts +45 -0
- package/dist/host-modules/notion.d.ts.map +1 -0
- package/dist/host-modules/notion.js +128 -0
- package/dist/host-modules/notion.js.map +1 -0
- package/dist/host-modules/ocr.d.ts +25 -0
- package/dist/host-modules/ocr.d.ts.map +1 -0
- package/dist/host-modules/ocr.js +74 -0
- package/dist/host-modules/ocr.js.map +1 -0
- package/dist/host-modules/pdf.d.ts +21 -0
- package/dist/host-modules/pdf.d.ts.map +1 -0
- package/dist/host-modules/pdf.js +53 -0
- package/dist/host-modules/pdf.js.map +1 -0
- package/dist/host-modules/pdflib.d.ts +17 -0
- package/dist/host-modules/pdflib.d.ts.map +1 -0
- package/dist/host-modules/pdflib.js +138 -0
- package/dist/host-modules/pdflib.js.map +1 -0
- package/dist/host-modules/pptx.d.ts +19 -0
- package/dist/host-modules/pptx.d.ts.map +1 -0
- package/dist/host-modules/pptx.js +67 -0
- package/dist/host-modules/pptx.js.map +1 -0
- package/dist/host-modules/pptxgen.d.ts +13 -0
- package/dist/host-modules/pptxgen.d.ts.map +1 -0
- package/dist/host-modules/pptxgen.js +98 -0
- package/dist/host-modules/pptxgen.js.map +1 -0
- package/dist/host-modules/prepared-request.d.ts +41 -0
- package/dist/host-modules/prepared-request.d.ts.map +1 -0
- package/dist/host-modules/prepared-request.js +104 -0
- package/dist/host-modules/prepared-request.js.map +1 -0
- package/dist/host-modules/registry.d.ts +31 -0
- package/dist/host-modules/registry.d.ts.map +1 -0
- package/dist/host-modules/registry.js +193 -0
- package/dist/host-modules/registry.js.map +1 -0
- package/dist/host-modules/subtitle.d.ts +15 -0
- package/dist/host-modules/subtitle.d.ts.map +1 -0
- package/dist/host-modules/subtitle.js +45 -0
- package/dist/host-modules/subtitle.js.map +1 -0
- package/dist/host-modules/supabase.d.ts +42 -0
- package/dist/host-modules/supabase.d.ts.map +1 -0
- package/dist/host-modules/supabase.js +141 -0
- package/dist/host-modules/supabase.js.map +1 -0
- package/dist/host-modules/tfjs.d.ts +42 -0
- package/dist/host-modules/tfjs.d.ts.map +1 -0
- package/dist/host-modules/tfjs.js +186 -0
- package/dist/host-modules/tfjs.js.map +1 -0
- package/dist/host-modules/tokens.d.ts +17 -0
- package/dist/host-modules/tokens.d.ts.map +1 -0
- package/dist/host-modules/tokens.js +74 -0
- package/dist/host-modules/tokens.js.map +1 -0
- package/dist/host-modules/twilio.d.ts +34 -0
- package/dist/host-modules/twilio.d.ts.map +1 -0
- package/dist/host-modules/twilio.js +67 -0
- package/dist/host-modules/twilio.js.map +1 -0
- package/dist/host-modules/xlsx.d.ts +26 -0
- package/dist/host-modules/xlsx.d.ts.map +1 -0
- package/dist/host-modules/xlsx.js +323 -0
- package/dist/host-modules/xlsx.js.map +1 -0
- package/dist/host-modules/xml.d.ts +23 -0
- package/dist/host-modules/xml.d.ts.map +1 -0
- package/dist/host-modules/xml.js +38 -0
- package/dist/host-modules/xml.js.map +1 -0
- package/dist/host-modules/zip.d.ts +24 -0
- package/dist/host-modules/zip.d.ts.map +1 -0
- package/dist/host-modules/zip.js +70 -0
- package/dist/host-modules/zip.js.map +1 -0
- package/dist/index.d.ts +197 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +153 -60
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox-worker/host.d.ts +136 -0
- package/dist/js-sandbox-worker/host.d.ts.map +1 -0
- package/dist/js-sandbox-worker/host.js +482 -0
- package/dist/js-sandbox-worker/host.js.map +1 -0
- package/dist/js-sandbox-worker/interpreter.d.ts +276 -0
- package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -0
- package/dist/js-sandbox-worker/interpreter.js +1201 -0
- package/dist/js-sandbox-worker/interpreter.js.map +1 -0
- package/dist/js-sandbox-worker/protocol.d.ts +168 -0
- package/dist/js-sandbox-worker/protocol.d.ts.map +1 -0
- package/dist/js-sandbox-worker/protocol.js +217 -0
- package/dist/js-sandbox-worker/protocol.js.map +1 -0
- package/dist/js-sandbox-worker/worker-entry.d.ts +26 -0
- package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -0
- package/dist/js-sandbox-worker/worker-entry.js +307 -0
- package/dist/js-sandbox-worker/worker-entry.js.map +1 -0
- package/dist/js-sandbox.d.ts +455 -15
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +1961 -309
- package/dist/js-sandbox.js.map +1 -1
- package/dist/js-script-app-runner.d.ts +9 -0
- package/dist/js-script-app-runner.d.ts.map +1 -0
- package/dist/js-script-app-runner.js +49 -0
- package/dist/js-script-app-runner.js.map +1 -0
- package/dist/js-script-sandbox.d.ts +33 -0
- package/dist/js-script-sandbox.d.ts.map +1 -0
- package/dist/js-script-sandbox.js +82 -0
- package/dist/js-script-sandbox.js.map +1 -0
- package/dist/jtbd/friction.d.ts +26 -0
- package/dist/jtbd/friction.d.ts.map +1 -0
- package/dist/jtbd/friction.js +169 -0
- package/dist/jtbd/friction.js.map +1 -0
- package/dist/jtbd/index.d.ts +6 -0
- package/dist/jtbd/index.d.ts.map +1 -0
- package/dist/jtbd/index.js +6 -0
- package/dist/jtbd/index.js.map +1 -0
- package/dist/jtbd/optimize.d.ts +71 -0
- package/dist/jtbd/optimize.d.ts.map +1 -0
- package/dist/jtbd/optimize.js +176 -0
- package/dist/jtbd/optimize.js.map +1 -0
- package/dist/jtbd/registry.d.ts +29 -0
- package/dist/jtbd/registry.d.ts.map +1 -0
- package/dist/jtbd/registry.js +451 -0
- package/dist/jtbd/registry.js.map +1 -0
- package/dist/jtbd/run.d.ts +70 -0
- package/dist/jtbd/run.d.ts.map +1 -0
- package/dist/jtbd/run.js +177 -0
- package/dist/jtbd/run.js.map +1 -0
- package/dist/jtbd/types.d.ts +141 -0
- package/dist/jtbd/types.d.ts.map +1 -0
- package/dist/jtbd/types.js +25 -0
- package/dist/jtbd/types.js.map +1 -0
- package/dist/model-prompting-skills.d.ts +45 -0
- package/dist/model-prompting-skills.d.ts.map +1 -0
- package/dist/model-prompting-skills.js +116 -0
- package/dist/model-prompting-skills.js.map +1 -0
- package/dist/network-guard.d.ts +50 -0
- package/dist/network-guard.d.ts.map +1 -0
- package/dist/network-guard.js +106 -0
- package/dist/network-guard.js.map +1 -0
- package/dist/normalize-model-properties.d.ts +26 -0
- package/dist/normalize-model-properties.d.ts.map +1 -0
- package/dist/normalize-model-properties.js +58 -0
- package/dist/normalize-model-properties.js.map +1 -0
- package/dist/output-format.d.ts +5 -1
- package/dist/output-format.d.ts.map +1 -1
- package/dist/output-format.js.map +1 -1
- package/dist/parallel-task-executor.d.ts +68 -37
- package/dist/parallel-task-executor.d.ts.map +1 -1
- package/dist/parallel-task-executor.js +234 -203
- package/dist/parallel-task-executor.js.map +1 -1
- package/dist/prompts/background-subtask-prompt.d.ts +14 -0
- package/dist/prompts/background-subtask-prompt.d.ts.map +1 -0
- package/dist/prompts/background-subtask-prompt.js +64 -0
- package/dist/prompts/background-subtask-prompt.js.map +1 -0
- package/dist/prompts/product-knowledge.d.ts +3 -0
- package/dist/prompts/product-knowledge.d.ts.map +1 -0
- package/dist/prompts/product-knowledge.js +79 -0
- package/dist/prompts/product-knowledge.js.map +1 -0
- package/dist/prompts/read-only-search-prompt.d.ts +33 -0
- package/dist/prompts/read-only-search-prompt.d.ts.map +1 -0
- package/dist/prompts/read-only-search-prompt.js +109 -0
- package/dist/prompts/read-only-search-prompt.js.map +1 -0
- package/dist/prompts/sandbox-pack-catalog.d.ts +23 -0
- package/dist/prompts/sandbox-pack-catalog.d.ts.map +1 -0
- package/dist/prompts/sandbox-pack-catalog.js +40 -0
- package/dist/prompts/sandbox-pack-catalog.js.map +1 -0
- package/dist/prompts/security-monitor-prompt.d.ts +32 -0
- package/dist/prompts/security-monitor-prompt.d.ts.map +1 -0
- package/dist/prompts/security-monitor-prompt.js +99 -0
- package/dist/prompts/security-monitor-prompt.js.map +1 -0
- package/dist/prompts/workflow-authoring-knowledge.d.ts +77 -0
- package/dist/prompts/workflow-authoring-knowledge.d.ts.map +1 -0
- package/dist/prompts/workflow-authoring-knowledge.js +319 -0
- package/dist/prompts/workflow-authoring-knowledge.js.map +1 -0
- package/dist/run-policy.d.ts +25 -0
- package/dist/run-policy.d.ts.map +1 -0
- package/dist/run-policy.js +54 -0
- package/dist/run-policy.js.map +1 -0
- package/dist/sandbox-av-media.d.ts +33 -0
- package/dist/sandbox-av-media.d.ts.map +1 -0
- package/dist/sandbox-av-media.js +666 -0
- package/dist/sandbox-av-media.js.map +1 -0
- package/dist/sandbox-bootstrap-modules.d.ts +54 -0
- package/dist/sandbox-bootstrap-modules.d.ts.map +1 -0
- package/dist/sandbox-bootstrap-modules.js +104 -0
- package/dist/sandbox-bootstrap-modules.js.map +1 -0
- package/dist/sandbox-bytes.d.ts +43 -0
- package/dist/sandbox-bytes.d.ts.map +1 -0
- package/dist/sandbox-bytes.js +61 -0
- package/dist/sandbox-bytes.js.map +1 -0
- package/dist/sandbox-canvas-api.d.ts +30 -0
- package/dist/sandbox-canvas-api.d.ts.map +1 -0
- package/dist/sandbox-canvas-api.js +81 -0
- package/dist/sandbox-canvas-api.js.map +1 -0
- package/dist/sandbox-constants.d.ts +5 -0
- package/dist/sandbox-constants.d.ts.map +1 -0
- package/dist/sandbox-constants.js +13 -0
- package/dist/sandbox-constants.js.map +1 -0
- package/dist/sandbox-json-transport.d.ts +107 -0
- package/dist/sandbox-json-transport.d.ts.map +1 -0
- package/dist/sandbox-json-transport.js +324 -0
- package/dist/sandbox-json-transport.js.map +1 -0
- package/dist/sandbox-media-handle.d.ts +78 -0
- package/dist/sandbox-media-handle.d.ts.map +1 -0
- package/dist/sandbox-media-handle.js +114 -0
- package/dist/sandbox-media-handle.js.map +1 -0
- package/dist/sandbox-media-ref.d.ts +134 -0
- package/dist/sandbox-media-ref.d.ts.map +1 -0
- package/dist/sandbox-media-ref.js +442 -0
- package/dist/sandbox-media-ref.js.map +1 -0
- package/dist/sandbox-media.d.ts +177 -0
- package/dist/sandbox-media.d.ts.map +1 -0
- package/dist/sandbox-media.js +963 -0
- package/dist/sandbox-media.js.map +1 -0
- package/dist/sandbox-module-fixtures.d.ts +445 -0
- package/dist/sandbox-module-fixtures.d.ts.map +1 -0
- package/dist/sandbox-module-fixtures.js +214 -0
- package/dist/sandbox-module-fixtures.js.map +1 -0
- package/dist/sandbox-toolbelt.d.ts +13 -0
- package/dist/sandbox-toolbelt.d.ts.map +1 -0
- package/dist/sandbox-toolbelt.js +64 -0
- package/dist/sandbox-toolbelt.js.map +1 -0
- package/dist/security-monitor.d.ts +89 -0
- package/dist/security-monitor.d.ts.map +1 -0
- package/dist/security-monitor.js +307 -0
- package/dist/security-monitor.js.map +1 -0
- package/dist/serpapi/catalog.d.ts +101 -0
- package/dist/serpapi/catalog.d.ts.map +1 -0
- package/dist/serpapi/catalog.js +246 -0
- package/dist/serpapi/catalog.js.map +1 -0
- package/dist/serpapi/client.d.ts +81 -0
- package/dist/serpapi/client.d.ts.map +1 -0
- package/dist/serpapi/client.js +228 -0
- package/dist/serpapi/client.js.map +1 -0
- package/dist/serpapi/errors.d.ts +49 -0
- package/dist/serpapi/errors.d.ts.map +1 -0
- package/dist/serpapi/errors.js +64 -0
- package/dist/serpapi/errors.js.map +1 -0
- package/dist/serpapi/normalize.d.ts +58 -0
- package/dist/serpapi/normalize.d.ts.map +1 -0
- package/dist/serpapi/normalize.js +140 -0
- package/dist/serpapi/normalize.js.map +1 -0
- package/dist/skill-prompt.d.ts +27 -0
- package/dist/skill-prompt.d.ts.map +1 -0
- package/dist/skill-prompt.js +61 -0
- package/dist/skill-prompt.js.map +1 -0
- package/dist/step-executor.d.ts +90 -77
- package/dist/step-executor.d.ts.map +1 -1
- package/dist/step-executor.js +384 -690
- package/dist/step-executor.js.map +1 -1
- package/dist/subagent.d.ts +275 -0
- package/dist/subagent.d.ts.map +1 -0
- package/dist/subagent.js +444 -0
- package/dist/subagent.js.map +1 -0
- package/dist/supervisor/prompt.d.ts +12 -0
- package/dist/supervisor/prompt.d.ts.map +1 -0
- package/dist/supervisor/prompt.js +51 -0
- package/dist/supervisor/prompt.js.map +1 -0
- package/dist/supervisor/supervisor-agent.d.ts +72 -0
- package/dist/supervisor/supervisor-agent.d.ts.map +1 -0
- package/dist/supervisor/supervisor-agent.js +185 -0
- package/dist/supervisor/supervisor-agent.js.map +1 -0
- package/dist/supervisor/tools.d.ts +56 -0
- package/dist/supervisor/tools.d.ts.map +1 -0
- package/dist/supervisor/tools.js +83 -0
- package/dist/supervisor/tools.js.map +1 -0
- package/dist/supervisor/verdict-schema.d.ts +18 -0
- package/dist/supervisor/verdict-schema.d.ts.map +1 -0
- package/dist/supervisor/verdict-schema.js +58 -0
- package/dist/supervisor/verdict-schema.js.map +1 -0
- package/dist/system-skills.d.ts +68 -0
- package/dist/system-skills.d.ts.map +1 -0
- package/dist/system-skills.js +168 -0
- package/dist/system-skills.js.map +1 -0
- package/dist/task-executor.d.ts +90 -40
- package/dist/task-executor.d.ts.map +1 -1
- package/dist/task-executor.js +259 -282
- package/dist/task-executor.js.map +1 -1
- package/dist/task-planner.d.ts +18 -12
- package/dist/task-planner.d.ts.map +1 -1
- package/dist/task-planner.js +342 -328
- package/dist/task-planner.js.map +1 -1
- package/dist/timeline-preview/compare.d.ts +27 -0
- package/dist/timeline-preview/compare.d.ts.map +1 -0
- package/dist/timeline-preview/compare.js +71 -0
- package/dist/timeline-preview/compare.js.map +1 -0
- package/dist/timeline-preview/frames.d.ts +167 -0
- package/dist/timeline-preview/frames.d.ts.map +1 -0
- package/dist/timeline-preview/frames.js +646 -0
- package/dist/timeline-preview/frames.js.map +1 -0
- package/dist/timeline-preview/model3d.d.ts +67 -0
- package/dist/timeline-preview/model3d.d.ts.map +1 -0
- package/dist/timeline-preview/model3d.js +165 -0
- package/dist/timeline-preview/model3d.js.map +1 -0
- package/dist/timeline-preview/rasterize.d.ts +33 -0
- package/dist/timeline-preview/rasterize.d.ts.map +1 -0
- package/dist/timeline-preview/rasterize.js +83 -0
- package/dist/timeline-preview/rasterize.js.map +1 -0
- package/dist/timeline-preview/sheet.d.ts +38 -0
- package/dist/timeline-preview/sheet.d.ts.map +1 -0
- package/dist/timeline-preview/sheet.js +72 -0
- package/dist/timeline-preview/sheet.js.map +1 -0
- package/dist/tools/asset-persist.d.ts +29 -0
- package/dist/tools/asset-persist.d.ts.map +1 -0
- package/dist/tools/asset-persist.js +78 -0
- package/dist/tools/asset-persist.js.map +1 -0
- package/dist/tools/base-tool.d.ts +52 -5
- package/dist/tools/base-tool.d.ts.map +1 -1
- package/dist/tools/base-tool.js +180 -3
- package/dist/tools/base-tool.js.map +1 -1
- package/dist/tools/binary-output.d.ts +54 -0
- package/dist/tools/binary-output.d.ts.map +1 -0
- package/dist/tools/binary-output.js +127 -0
- package/dist/tools/binary-output.js.map +1 -0
- package/dist/tools/browser-tools.d.ts +5 -29
- package/dist/tools/browser-tools.d.ts.map +1 -1
- package/dist/tools/browser-tools.js +5 -205
- package/dist/tools/browser-tools.js.map +1 -1
- package/dist/tools/builtin-tools.d.ts +52 -0
- package/dist/tools/builtin-tools.d.ts.map +1 -0
- package/dist/tools/builtin-tools.js +276 -0
- package/dist/tools/builtin-tools.js.map +1 -0
- package/dist/tools/control-tool.d.ts +2 -5
- package/dist/tools/control-tool.d.ts.map +1 -1
- package/dist/tools/control-tool.js +18 -15
- package/dist/tools/control-tool.js.map +1 -1
- package/dist/tools/dataseo-tools.d.ts +34 -32
- package/dist/tools/dataseo-tools.d.ts.map +1 -1
- package/dist/tools/dataseo-tools.js +148 -233
- package/dist/tools/dataseo-tools.js.map +1 -1
- package/dist/tools/external-capability-tools.d.ts +36 -0
- package/dist/tools/external-capability-tools.d.ts.map +1 -0
- package/dist/tools/external-capability-tools.js +41 -0
- package/dist/tools/external-capability-tools.js.map +1 -0
- package/dist/tools/external-mcp-tools.d.ts +139 -0
- package/dist/tools/external-mcp-tools.d.ts.map +1 -0
- package/dist/tools/external-mcp-tools.js +552 -0
- package/dist/tools/external-mcp-tools.js.map +1 -0
- package/dist/tools/finish-step-tool.d.ts +1 -1
- package/dist/tools/finish-step-tool.d.ts.map +1 -1
- package/dist/tools/finish-step-tool.js +3 -3
- package/dist/tools/finish-step-tool.js.map +1 -1
- package/dist/tools/google-tools.d.ts +28 -39
- package/dist/tools/google-tools.d.ts.map +1 -1
- package/dist/tools/google-tools.js +51 -164
- package/dist/tools/google-tools.js.map +1 -1
- package/dist/tools/google-workspace-tools.d.ts +23 -0
- package/dist/tools/google-workspace-tools.d.ts.map +1 -0
- package/dist/tools/google-workspace-tools.js +26 -0
- package/dist/tools/google-workspace-tools.js.map +1 -0
- package/dist/tools/graph-validation-registry.d.ts +33 -0
- package/dist/tools/graph-validation-registry.d.ts.map +1 -0
- package/dist/tools/graph-validation-registry.js +54 -0
- package/dist/tools/graph-validation-registry.js.map +1 -0
- package/dist/tools/http-tools.d.ts +5 -53
- package/dist/tools/http-tools.d.ts.map +1 -1
- package/dist/tools/http-tools.js +5 -177
- package/dist/tools/http-tools.js.map +1 -1
- package/dist/tools/image-injection.d.ts +51 -0
- package/dist/tools/image-injection.d.ts.map +1 -0
- package/dist/tools/image-injection.js +85 -0
- package/dist/tools/image-injection.js.map +1 -0
- package/dist/tools/mcp-tool-support.d.ts +203 -0
- package/dist/tools/mcp-tool-support.d.ts.map +1 -0
- package/dist/tools/mcp-tool-support.js +536 -0
- package/dist/tools/mcp-tool-support.js.map +1 -0
- package/dist/tools/mcp-tools.d.ts +104 -390
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +128 -600
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/tools/media-tools.d.ts +9 -272
- package/dist/tools/media-tools.d.ts.map +1 -1
- package/dist/tools/media-tools.js +11 -676
- package/dist/tools/media-tools.js.map +1 -1
- package/dist/tools/memory-tools.d.ts +36 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +52 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/openai-tools.d.ts +28 -68
- package/dist/tools/openai-tools.d.ts.map +1 -1
- package/dist/tools/openai-tools.js +51 -181
- package/dist/tools/openai-tools.js.map +1 -1
- package/dist/tools/plan-builder-tools.d.ts +141 -7
- package/dist/tools/plan-builder-tools.d.ts.map +1 -1
- package/dist/tools/plan-builder-tools.js +159 -86
- package/dist/tools/plan-builder-tools.js.map +1 -1
- package/dist/tools/run-search-tool.d.ts +82 -0
- package/dist/tools/run-search-tool.d.ts.map +1 -0
- package/dist/tools/run-search-tool.js +121 -0
- package/dist/tools/run-search-tool.js.map +1 -0
- package/dist/tools/run-subtask-tool.d.ts +49 -0
- package/dist/tools/run-subtask-tool.d.ts.map +1 -0
- package/dist/tools/run-subtask-tool.js +88 -0
- package/dist/tools/run-subtask-tool.js.map +1 -0
- package/dist/tools/search-tools.d.ts +5 -36
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +5 -215
- package/dist/tools/search-tools.js.map +1 -1
- package/dist/tools/serp-providers/apify-provider.d.ts +18 -0
- package/dist/tools/serp-providers/apify-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/apify-provider.js +93 -0
- package/dist/tools/serp-providers/apify-provider.js.map +1 -0
- package/dist/tools/serp-providers/brave-provider.d.ts +34 -0
- package/dist/tools/serp-providers/brave-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/brave-provider.js +86 -0
- package/dist/tools/serp-providers/brave-provider.js.map +1 -0
- package/dist/tools/serp-providers/dataforseo-provider.d.ts +14 -1
- package/dist/tools/serp-providers/dataforseo-provider.d.ts.map +1 -1
- package/dist/tools/serp-providers/dataforseo-provider.js +0 -9
- package/dist/tools/serp-providers/dataforseo-provider.js.map +1 -1
- package/dist/tools/serp-providers/gemini-provider.d.ts +45 -0
- package/dist/tools/serp-providers/gemini-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/gemini-provider.js +148 -0
- package/dist/tools/serp-providers/gemini-provider.js.map +1 -0
- package/dist/tools/serp-providers/index.d.ts +61 -0
- package/dist/tools/serp-providers/index.d.ts.map +1 -1
- package/dist/tools/serp-providers/index.js +119 -3
- package/dist/tools/serp-providers/index.js.map +1 -1
- package/dist/tools/serp-providers/openai-provider.d.ts +58 -0
- package/dist/tools/serp-providers/openai-provider.d.ts.map +1 -0
- package/dist/tools/serp-providers/openai-provider.js +113 -0
- package/dist/tools/serp-providers/openai-provider.js.map +1 -0
- package/dist/tools/serp-providers/serpapi-provider.d.ts +9 -5
- package/dist/tools/serp-providers/serpapi-provider.d.ts.map +1 -1
- package/dist/tools/serp-providers/serpapi-provider.js +26 -44
- package/dist/tools/serp-providers/serpapi-provider.js.map +1 -1
- package/dist/tools/serp-tool-factory.d.ts +27 -0
- package/dist/tools/serp-tool-factory.d.ts.map +1 -0
- package/dist/tools/serp-tool-factory.js +56 -0
- package/dist/tools/serp-tool-factory.js.map +1 -0
- package/dist/tools/shared-tools.d.ts +33 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +39 -0
- package/dist/tools/shared-tools.js.map +1 -0
- package/dist/tools/start-subtask-tool.d.ts +51 -0
- package/dist/tools/start-subtask-tool.d.ts.map +1 -0
- package/dist/tools/start-subtask-tool.js +189 -0
- package/dist/tools/start-subtask-tool.js.map +1 -0
- package/dist/tools/submit-code-tool.d.ts +159 -0
- package/dist/tools/submit-code-tool.d.ts.map +1 -0
- package/dist/tools/submit-code-tool.js +209 -0
- package/dist/tools/submit-code-tool.js.map +1 -0
- package/dist/tools/subtask-fields.d.ts +11 -0
- package/dist/tools/subtask-fields.d.ts.map +1 -0
- package/dist/tools/subtask-fields.js +11 -0
- package/dist/tools/subtask-fields.js.map +1 -0
- package/dist/tools/todo-tools.d.ts +15 -0
- package/dist/tools/todo-tools.d.ts.map +1 -0
- package/dist/tools/todo-tools.js +15 -0
- package/dist/tools/todo-tools.js.map +1 -0
- package/dist/tools/tool-permissions.d.ts +40 -0
- package/dist/tools/tool-permissions.d.ts.map +1 -0
- package/dist/tools/tool-permissions.js +52 -0
- package/dist/tools/tool-permissions.js.map +1 -0
- package/dist/tools/tool-search.d.ts +39 -0
- package/dist/tools/tool-search.d.ts.map +1 -0
- package/dist/tools/tool-search.js +108 -0
- package/dist/tools/tool-search.js.map +1 -0
- package/dist/tools/vector-tool-support.d.ts +15 -0
- package/dist/tools/vector-tool-support.d.ts.map +1 -0
- package/dist/tools/vector-tool-support.js +133 -0
- package/dist/tools/vector-tool-support.js.map +1 -0
- package/dist/tools/vector-tools.d.ts +13 -220
- package/dist/tools/vector-tools.d.ts.map +1 -1
- package/dist/tools/vector-tools.js +7 -578
- package/dist/tools/vector-tools.js.map +1 -1
- package/dist/tools/wait-subtasks-tool.d.ts +27 -0
- package/dist/tools/wait-subtasks-tool.d.ts.map +1 -0
- package/dist/tools/wait-subtasks-tool.js +66 -0
- package/dist/tools/wait-subtasks-tool.js.map +1 -0
- package/dist/tsconfig.tests.tmp.tsbuildinfo +1 -0
- package/dist/types.d.ts +16 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/dag-scheduler.d.ts +85 -0
- package/dist/utils/dag-scheduler.d.ts.map +1 -0
- package/dist/utils/dag-scheduler.js +180 -0
- package/dist/utils/dag-scheduler.js.map +1 -0
- package/dist/utils/json-parser.d.ts +32 -1
- package/dist/utils/json-parser.d.ts.map +1 -1
- package/dist/utils/json-parser.js +145 -10
- package/dist/utils/json-parser.js.map +1 -1
- package/dist/utils/json-schema-validate.d.ts +29 -0
- package/dist/utils/json-schema-validate.d.ts.map +1 -0
- package/dist/utils/json-schema-validate.js +228 -0
- package/dist/utils/json-schema-validate.js.map +1 -0
- package/dist/utils/link-abort.d.ts +13 -0
- package/dist/utils/link-abort.d.ts.map +1 -0
- package/dist/utils/link-abort.js +23 -0
- package/dist/utils/link-abort.js.map +1 -0
- package/dist/utils/merge-generators.d.ts +58 -0
- package/dist/utils/merge-generators.d.ts.map +1 -0
- package/dist/utils/merge-generators.js +148 -0
- package/dist/utils/merge-generators.js.map +1 -0
- package/dist/utils/remove-base64-images.d.ts +0 -5
- package/dist/utils/remove-base64-images.d.ts.map +1 -1
- package/dist/utils/remove-base64-images.js.map +1 -1
- package/dist/utils/step-failure.d.ts +20 -0
- package/dist/utils/step-failure.d.ts.map +1 -0
- package/dist/utils/step-failure.js +43 -0
- package/dist/utils/step-failure.js.map +1 -0
- package/dist/utils/think-tags.d.ts +16 -0
- package/dist/utils/think-tags.d.ts.map +1 -0
- package/dist/utils/think-tags.js +23 -0
- package/dist/utils/think-tags.js.map +1 -0
- package/dist/utils/type-guards.d.ts +26 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +44 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/utils/ui-event-buffer.d.ts +18 -0
- package/dist/utils/ui-event-buffer.d.ts.map +1 -0
- package/dist/utils/ui-event-buffer.js +18 -0
- package/dist/utils/ui-event-buffer.js.map +1 -0
- package/dist/utils/wrap-generators-parallel.d.ts +0 -5
- package/dist/utils/wrap-generators-parallel.d.ts.map +1 -1
- package/dist/utils/wrap-generators-parallel.js +3 -6
- package/dist/utils/wrap-generators-parallel.js.map +1 -1
- package/dist/wasm-sandbox/host.d.ts +106 -0
- package/dist/wasm-sandbox/host.d.ts.map +1 -0
- package/dist/wasm-sandbox/host.js +409 -0
- package/dist/wasm-sandbox/host.js.map +1 -0
- package/dist/wasm-sandbox/workers.d.ts +43 -0
- package/dist/wasm-sandbox/workers.d.ts.map +1 -0
- package/dist/wasm-sandbox/workers.js +150 -0
- package/dist/wasm-sandbox/workers.js.map +1 -0
- package/dist/workspace-paths.d.ts +40 -0
- package/dist/workspace-paths.d.ts.map +1 -0
- package/dist/workspace-paths.js +102 -0
- package/dist/workspace-paths.js.map +1 -0
- package/dist/yt-dlp-gate.d.ts +19 -0
- package/dist/yt-dlp-gate.d.ts.map +1 -0
- package/dist/yt-dlp-gate.js +22 -0
- package/dist/yt-dlp-gate.js.map +1 -0
- package/package.json +112 -14
- package/dist/agent-executor.d.ts +0 -62
- package/dist/agent-executor.d.ts.map +0 -1
- package/dist/agent-executor.js +0 -324
- package/dist/agent-executor.js.map +0 -1
- package/dist/agent-step-executor.d.ts +0 -28
- package/dist/agent-step-executor.d.ts.map +0 -1
- package/dist/agent-step-executor.js +0 -99
- package/dist/agent-step-executor.js.map +0 -1
- package/dist/agent-workflow-runner.d.ts +0 -31
- package/dist/agent-workflow-runner.d.ts.map +0 -1
- package/dist/agent-workflow-runner.js +0 -124
- package/dist/agent-workflow-runner.js.map +0 -1
- package/dist/agent.d.ts +0 -115
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js +0 -587
- package/dist/agent.js.map +0 -1
- package/dist/base-agent.d.ts +0 -42
- package/dist/base-agent.d.ts.map +0 -1
- package/dist/base-agent.js +0 -29
- package/dist/base-agent.js.map +0 -1
- package/dist/graph-planner.d.ts +0 -56
- package/dist/graph-planner.d.ts.map +0 -1
- package/dist/graph-planner.js +0 -311
- package/dist/graph-planner.js.map +0 -1
- package/dist/multi-mode-agent.d.ts +0 -86
- package/dist/multi-mode-agent.d.ts.map +0 -1
- package/dist/multi-mode-agent.js +0 -476
- package/dist/multi-mode-agent.js.map +0 -1
- package/dist/prompts/graph-planner-prompt.d.ts +0 -48
- package/dist/prompts/graph-planner-prompt.d.ts.map +0 -1
- package/dist/prompts/graph-planner-prompt.js +0 -180
- package/dist/prompts/graph-planner-prompt.js.map +0 -1
- package/dist/reject-agentic-provider.d.ts +0 -21
- package/dist/reject-agentic-provider.d.ts.map +0 -1
- package/dist/reject-agentic-provider.js +0 -32
- package/dist/reject-agentic-provider.js.map +0 -1
- package/dist/simple-agent.d.ts +0 -29
- package/dist/simple-agent.d.ts.map +0 -1
- package/dist/simple-agent.js +0 -54
- package/dist/simple-agent.js.map +0 -1
- package/dist/sub-agent-planner.d.ts +0 -33
- package/dist/sub-agent-planner.d.ts.map +0 -1
- package/dist/sub-agent-planner.js +0 -203
- package/dist/sub-agent-planner.js.map +0 -1
- package/dist/team/db-task-board.d.ts +0 -54
- package/dist/team/db-task-board.d.ts.map +0 -1
- package/dist/team/db-task-board.js +0 -361
- package/dist/team/db-task-board.js.map +0 -1
- package/dist/team/edge-message-bus.d.ts +0 -51
- package/dist/team/edge-message-bus.d.ts.map +0 -1
- package/dist/team/edge-message-bus.js +0 -169
- package/dist/team/edge-message-bus.js.map +0 -1
- package/dist/team/edge-task-board.d.ts +0 -77
- package/dist/team/edge-task-board.d.ts.map +0 -1
- package/dist/team/edge-task-board.js +0 -140
- package/dist/team/edge-task-board.js.map +0 -1
- package/dist/team/index.d.ts +0 -16
- package/dist/team/index.d.ts.map +0 -1
- package/dist/team/index.js +0 -17
- package/dist/team/index.js.map +0 -1
- package/dist/team/message-bus.d.ts +0 -58
- package/dist/team/message-bus.d.ts.map +0 -1
- package/dist/team/message-bus.js +0 -151
- package/dist/team/message-bus.js.map +0 -1
- package/dist/team/task-board.d.ts +0 -106
- package/dist/team/task-board.d.ts.map +0 -1
- package/dist/team/task-board.js +0 -370
- package/dist/team/task-board.js.map +0 -1
- package/dist/team/team-executor.d.ts +0 -72
- package/dist/team/team-executor.d.ts.map +0 -1
- package/dist/team/team-executor.js +0 -466
- package/dist/team/team-executor.js.map +0 -1
- package/dist/team/team-tools.d.ts +0 -252
- package/dist/team/team-tools.d.ts.map +0 -1
- package/dist/team/team-tools.js +0 -412
- package/dist/team/team-tools.js.map +0 -1
- package/dist/team/types.d.ts +0 -198
- package/dist/team/types.d.ts.map +0 -1
- package/dist/team/types.js +0 -8
- package/dist/team/types.js.map +0 -1
- package/dist/tools/add-edge-tool.d.ts +0 -18
- package/dist/tools/add-edge-tool.d.ts.map +0 -1
- package/dist/tools/add-edge-tool.js +0 -92
- package/dist/tools/add-edge-tool.js.map +0 -1
- package/dist/tools/add-node-tool.d.ts +0 -22
- package/dist/tools/add-node-tool.d.ts.map +0 -1
- package/dist/tools/add-node-tool.js +0 -128
- package/dist/tools/add-node-tool.js.map +0 -1
- package/dist/tools/asset-tools.d.ts +0 -58
- package/dist/tools/asset-tools.d.ts.map +0 -1
- package/dist/tools/asset-tools.js +0 -191
- package/dist/tools/asset-tools.js.map +0 -1
- package/dist/tools/calculator-tool.d.ts +0 -24
- package/dist/tools/calculator-tool.d.ts.map +0 -1
- package/dist/tools/calculator-tool.js +0 -64
- package/dist/tools/calculator-tool.js.map +0 -1
- package/dist/tools/claude-code-tools.d.ts +0 -96
- package/dist/tools/claude-code-tools.d.ts.map +0 -1
- package/dist/tools/claude-code-tools.js +0 -463
- package/dist/tools/claude-code-tools.js.map +0 -1
- package/dist/tools/code-tools.d.ts +0 -26
- package/dist/tools/code-tools.d.ts.map +0 -1
- package/dist/tools/code-tools.js +0 -82
- package/dist/tools/code-tools.js.map +0 -1
- package/dist/tools/create-plan-tool.d.ts +0 -29
- package/dist/tools/create-plan-tool.d.ts.map +0 -1
- package/dist/tools/create-plan-tool.js +0 -355
- package/dist/tools/create-plan-tool.js.map +0 -1
- package/dist/tools/create-task-tool.d.ts +0 -28
- package/dist/tools/create-task-tool.d.ts.map +0 -1
- package/dist/tools/create-task-tool.js +0 -254
- package/dist/tools/create-task-tool.js.map +0 -1
- package/dist/tools/email-tools.d.ts +0 -76
- package/dist/tools/email-tools.d.ts.map +0 -1
- package/dist/tools/email-tools.js +0 -259
- package/dist/tools/email-tools.js.map +0 -1
- package/dist/tools/filesystem-tools.d.ts +0 -77
- package/dist/tools/filesystem-tools.d.ts.map +0 -1
- package/dist/tools/filesystem-tools.js +0 -334
- package/dist/tools/filesystem-tools.js.map +0 -1
- package/dist/tools/find-model-tool.d.ts +0 -25
- package/dist/tools/find-model-tool.d.ts.map +0 -1
- package/dist/tools/find-model-tool.js +0 -236
- package/dist/tools/find-model-tool.js.map +0 -1
- package/dist/tools/finish-graph-tool.d.ts +0 -23
- package/dist/tools/finish-graph-tool.d.ts.map +0 -1
- package/dist/tools/finish-graph-tool.js +0 -46
- package/dist/tools/finish-graph-tool.js.map +0 -1
- package/dist/tools/js-code-tool.d.ts +0 -30
- package/dist/tools/js-code-tool.d.ts.map +0 -1
- package/dist/tools/js-code-tool.js +0 -98
- package/dist/tools/js-code-tool.js.map +0 -1
- package/dist/tools/local-get-node-info-tool.d.ts +0 -19
- package/dist/tools/local-get-node-info-tool.d.ts.map +0 -1
- package/dist/tools/local-get-node-info-tool.js +0 -75
- package/dist/tools/local-get-node-info-tool.js.map +0 -1
- package/dist/tools/local-list-nodes-tool.d.ts +0 -19
- package/dist/tools/local-list-nodes-tool.d.ts.map +0 -1
- package/dist/tools/local-list-nodes-tool.js +0 -69
- package/dist/tools/local-list-nodes-tool.js.map +0 -1
- package/dist/tools/local-search-nodes-tool.d.ts +0 -21
- package/dist/tools/local-search-nodes-tool.d.ts.map +0 -1
- package/dist/tools/local-search-nodes-tool.js +0 -111
- package/dist/tools/local-search-nodes-tool.js.map +0 -1
- package/dist/tools/math-tools.d.ts +0 -156
- package/dist/tools/math-tools.d.ts.map +0 -1
- package/dist/tools/math-tools.js +0 -422
- package/dist/tools/math-tools.js.map +0 -1
- package/dist/tools/model-tools.d.ts +0 -25
- package/dist/tools/model-tools.d.ts.map +0 -1
- package/dist/tools/model-tools.js +0 -48
- package/dist/tools/model-tools.js.map +0 -1
- package/dist/tools/pdf-tools.d.ts +0 -152
- package/dist/tools/pdf-tools.d.ts.map +0 -1
- package/dist/tools/pdf-tools.js +0 -339
- package/dist/tools/pdf-tools.js.map +0 -1
- package/dist/tools/tool-registry.d.ts +0 -9
- package/dist/tools/tool-registry.d.ts.map +0 -1
- package/dist/tools/tool-registry.js +0 -17
- package/dist/tools/tool-registry.js.map +0 -1
- package/dist/tools/workspace-tools.d.ts +0 -58
- package/dist/tools/workspace-tools.d.ts.map +0 -1
- package/dist/tools/workspace-tools.js +0 -152
- package/dist/tools/workspace-tools.js.map +0 -1
|
@@ -0,0 +1,2246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `storyboards` capability module.
|
|
3
|
+
*
|
|
4
|
+
* Storyboard capabilities: list and create, render stills and clips, revise
|
|
5
|
+
* one take, assemble a timeline, edit the shot list, extract a script, and
|
|
6
|
+
* delete. The render path used to be seven `Tool` subclasses in
|
|
7
|
+
* `../tools/storyboard-render-tools.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Wire names, descriptions and schemas are unchanged. Prompt composition,
|
|
10
|
+
* entity seasoning, and the shot → timeline mapping stay the editor's own
|
|
11
|
+
* (`entitiesForShot` from `@nodetool-ai/protocol`, `buildStoryboardTimeline`
|
|
12
|
+
* from `@nodetool-ai/timeline`), so a board rendered headlessly matches one
|
|
13
|
+
* rendered in the UI. Every heavy dependency is imported inside the
|
|
14
|
+
* implementation that needs it.
|
|
15
|
+
*
|
|
16
|
+
* Design: docs/tool-class-retirement-design.md § "Migration".
|
|
17
|
+
*/
|
|
18
|
+
import { mp4DurationSeconds } from "@nodetool-ai/runtime";
|
|
19
|
+
import { stampScriptStoryboardId } from "./script-link.js";
|
|
20
|
+
import { listStoryboardsSpec, createStoryboardSpec, getStoryboardSpec, renderStoryboardStillsSpec, renderStoryboardClipsSpec, reviseStoryboardClipSpec, assembleStoryboardTimelineSpec, editStoryboardSpec, directStoryboardSpec, extractScriptFromStoryboardSpec, deleteStoryboardSpec } from "./storyboards.specs.js";
|
|
21
|
+
import { clampConcurrency, mapWithConcurrency } from "./concurrency.js";
|
|
22
|
+
import { isNonBlankString, isNumber, isRecord, isString } from "../utils/type-guards.js";
|
|
23
|
+
import { resolveProjectId } from "./project-scope.js";
|
|
24
|
+
/** Shots one call may render, so a stray `targets: "all"` cannot bankrupt a run. */
|
|
25
|
+
const MAX_SHOTS_PER_CALL = 24;
|
|
26
|
+
/** Attempts to land a document write: the first try plus one re-read-and-reapply (ADR 0001). */
|
|
27
|
+
const CAS_ATTEMPTS = 2;
|
|
28
|
+
/** The guided-setup stages, in order. Mirrors `storyboardSetupStage`. */
|
|
29
|
+
const SETUP_STAGES = [
|
|
30
|
+
"idea",
|
|
31
|
+
"genre",
|
|
32
|
+
"review",
|
|
33
|
+
"entities",
|
|
34
|
+
"look",
|
|
35
|
+
"done"
|
|
36
|
+
];
|
|
37
|
+
const isError = (value) => !!value &&
|
|
38
|
+
typeof value === "object" &&
|
|
39
|
+
typeof value.error === "string";
|
|
40
|
+
async function loadBoard(run, storyboardId) {
|
|
41
|
+
if (!isString(storyboardId) || !storyboardId) {
|
|
42
|
+
return {
|
|
43
|
+
error: "storyboard_id is required (use list_storyboards to find one)."
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
47
|
+
const row = await Storyboard.findById(storyboardId);
|
|
48
|
+
// A board owned by someone else reads as missing — the same rule the tRPC
|
|
49
|
+
// router's ownership check applies.
|
|
50
|
+
if (!row || row.user_id !== run.context.userId) {
|
|
51
|
+
return { error: `Storyboard ${storyboardId} was not found.` };
|
|
52
|
+
}
|
|
53
|
+
return { row, doc: row.toDocument() };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The board as a {@link Screenplay}, which is the shape every script-link
|
|
57
|
+
* function reads. `doc.shots` is the authoritative shot list — `doc.screenplay`
|
|
58
|
+
* carries the piece's framing (title, narration, music, and the `script_id`
|
|
59
|
+
* link) and a board directed shot-by-shot may not have one at all.
|
|
60
|
+
*/
|
|
61
|
+
function boardScreenplay(row, doc) {
|
|
62
|
+
return {
|
|
63
|
+
type: "screenplay",
|
|
64
|
+
id: `sp_${row.id}`,
|
|
65
|
+
title: row.name,
|
|
66
|
+
...(doc.screenplay ?? {}),
|
|
67
|
+
shots: doc.shots
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** The linked script's document, or null when nothing links or it is gone. */
|
|
71
|
+
async function loadLinkedScript(screenplay, userId) {
|
|
72
|
+
const scriptId = screenplay.script_id;
|
|
73
|
+
if (!scriptId)
|
|
74
|
+
return null;
|
|
75
|
+
const { Script } = await import("@nodetool-ai/models");
|
|
76
|
+
const row = await Script.findById(scriptId);
|
|
77
|
+
if (!row || row.user_id !== userId)
|
|
78
|
+
return null;
|
|
79
|
+
return row.toDocument();
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The linked script as the joint assembler wants it, or `null` when nothing
|
|
83
|
+
* links, the script is gone, or reading it failed. Never throws: a broken link
|
|
84
|
+
* downgrades the assemble to the unlinked path with a warning (design §1.3).
|
|
85
|
+
*/
|
|
86
|
+
async function loadLinkedAssemblyScript(scriptId, userId) {
|
|
87
|
+
try {
|
|
88
|
+
const { Script } = await import("@nodetool-ai/models");
|
|
89
|
+
const row = await Script.findById(scriptId);
|
|
90
|
+
if (!row || row.user_id !== userId)
|
|
91
|
+
return null;
|
|
92
|
+
const doc = row.toDocument();
|
|
93
|
+
return { scriptId, cast: doc.cast, sections: doc.sections };
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// A link that cannot be read is a link that is not there.
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Link state for a board: what it links, which shots' text has drifted from
|
|
102
|
+
* the lines they project, which lines no shot covers, and what
|
|
103
|
+
* `validateScriptLink` says. Reported by `get_storyboard` so an agent can see
|
|
104
|
+
* what needs fixing without a second round trip.
|
|
105
|
+
*/
|
|
106
|
+
async function scriptLinkSummary(screenplay, scriptDoc) {
|
|
107
|
+
const { orphanedLineIds, shotDialogueDrifted, validateScriptLink } = await import("@nodetool-ai/protocol");
|
|
108
|
+
const linked = !!screenplay.script_id;
|
|
109
|
+
const validation = validateScriptLink(screenplay, scriptDoc);
|
|
110
|
+
const linesById = new Map((scriptDoc?.sections ?? [])
|
|
111
|
+
.flatMap((section) => section.lines)
|
|
112
|
+
.map((line) => [line.id, line]));
|
|
113
|
+
return {
|
|
114
|
+
linked,
|
|
115
|
+
script_id: screenplay.script_id ?? null,
|
|
116
|
+
script_found: !!scriptDoc,
|
|
117
|
+
drifted_shot_ids: scriptDoc
|
|
118
|
+
? screenplay.shots
|
|
119
|
+
.filter((shot) => shotDialogueDrifted(shot, linesById))
|
|
120
|
+
.map((shot) => shot.id)
|
|
121
|
+
: [],
|
|
122
|
+
orphan_line_ids: scriptDoc ? orphanedLineIds(screenplay, scriptDoc) : [],
|
|
123
|
+
issues: [...validation.errors, ...validation.warnings].map((i) => i.message)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/** Resolve a shot reference: shot id, 0-based index, or a slug. */
|
|
127
|
+
function findShot(shots, target) {
|
|
128
|
+
const byId = shots.find((s) => s.id === target);
|
|
129
|
+
if (byId)
|
|
130
|
+
return byId;
|
|
131
|
+
const index = Number(target);
|
|
132
|
+
if (Number.isInteger(index)) {
|
|
133
|
+
const byIndex = shots.find((s) => s.index === index);
|
|
134
|
+
if (byIndex)
|
|
135
|
+
return byIndex;
|
|
136
|
+
}
|
|
137
|
+
const slug = target.trim().toLowerCase();
|
|
138
|
+
return shots.find((s) => (s.slug ?? "").trim().toLowerCase() === slug);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether a shot's picture already exists — its own clip, or a window into the
|
|
142
|
+
* clip of the shot that covers it.
|
|
143
|
+
*
|
|
144
|
+
* The render selections read this rather than `shot.clip`, so a shot fused
|
|
145
|
+
* into a sibling's generation is not offered up to be generated a second time.
|
|
146
|
+
*/
|
|
147
|
+
const shotHasPicture = (shot, shots) => {
|
|
148
|
+
if (shot.clip)
|
|
149
|
+
return true;
|
|
150
|
+
const coverage = shot.covered_by;
|
|
151
|
+
if (!coverage)
|
|
152
|
+
return false;
|
|
153
|
+
return shots.some((s) => s.id === coverage.shot_id && !!s.clip);
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* The shots a render call acts on: the explicit `targets` when given, else
|
|
157
|
+
* every shot `needsWork` accepts — the "render what is missing" default that
|
|
158
|
+
* makes a whole board one call.
|
|
159
|
+
*/
|
|
160
|
+
function selectShots(shots, targets, needsWork) {
|
|
161
|
+
const ordered = [...shots].sort((a, b) => a.index - b.index);
|
|
162
|
+
if (targets === undefined || targets === null) {
|
|
163
|
+
return ordered.filter(needsWork);
|
|
164
|
+
}
|
|
165
|
+
const list = Array.isArray(targets) ? targets : [targets];
|
|
166
|
+
const selected = [];
|
|
167
|
+
for (const raw of list) {
|
|
168
|
+
const target = String(raw);
|
|
169
|
+
const shot = findShot(ordered, target);
|
|
170
|
+
if (!shot) {
|
|
171
|
+
return {
|
|
172
|
+
error: `No shot matches "${target}". Call get_storyboard to list shot ids and indexes.`
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!selected.includes(shot))
|
|
176
|
+
selected.push(shot);
|
|
177
|
+
}
|
|
178
|
+
return selected;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Apply `patch` to one shot and persist the board.
|
|
182
|
+
*
|
|
183
|
+
* Renders run concurrently against a single row, so the write is a CAS on
|
|
184
|
+
* `updated_at`: on conflict the row is re-read and the patch re-applied to the
|
|
185
|
+
* fresher document, never to the stale copy the render started from.
|
|
186
|
+
*/
|
|
187
|
+
async function patchShot(storyboardId, shotId, patch) {
|
|
188
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
189
|
+
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
190
|
+
const row = await Storyboard.findById(storyboardId);
|
|
191
|
+
if (!row)
|
|
192
|
+
return { error: `Storyboard ${storyboardId} was not found.` };
|
|
193
|
+
const doc = row.toDocument();
|
|
194
|
+
const index = doc.shots.findIndex((s) => s.id === shotId);
|
|
195
|
+
if (index === -1)
|
|
196
|
+
return { error: `Shot ${shotId} is no longer on the board.` };
|
|
197
|
+
const updated = patch(doc.shots[index]);
|
|
198
|
+
const shots = [...doc.shots];
|
|
199
|
+
shots[index] = updated;
|
|
200
|
+
const saved = await Storyboard.updateFieldsIfUnchanged(storyboardId, row.updated_at, { document: JSON.stringify({ ...doc, shots }) },
|
|
201
|
+
// One update_shot per write, so an open editor merges this into its
|
|
202
|
+
// draft per shot instead of treating the board as replaced.
|
|
203
|
+
{ ops: [{ tool: "update_shot", input: { id: shotId } }] });
|
|
204
|
+
if (saved)
|
|
205
|
+
return updated;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
error: `Storyboard ${storyboardId} is being modified concurrently; the render finished but could not be saved. Retry the call.`
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/** The board's library entities, dropping ids that no longer resolve. */
|
|
212
|
+
async function loadBoardEntities(context, doc) {
|
|
213
|
+
const ids = doc.entityIds ?? [];
|
|
214
|
+
if (ids.length === 0 || !context.userId)
|
|
215
|
+
return [];
|
|
216
|
+
const { Asset } = await import("@nodetool-ai/models");
|
|
217
|
+
const { entityFromAsset } = await import("./entities.js");
|
|
218
|
+
const loaded = await Promise.all(ids.map(async (id) => {
|
|
219
|
+
try {
|
|
220
|
+
const asset = await Asset.find(context.userId, id);
|
|
221
|
+
return asset ? entityFromAsset(asset) : null;
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}));
|
|
227
|
+
return loaded.filter((e) => !!e && e.name.length > 0);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The provider+model a render runs on: the call's override, else the board's
|
|
231
|
+
* own selection. There is no fallback default — an unset model is an error
|
|
232
|
+
* that names `find_model`, rather than silent spend on a model nobody chose.
|
|
233
|
+
*/
|
|
234
|
+
function resolveModel(params, boardModel, kind, capability) {
|
|
235
|
+
const provider = isString(params["provider"]) && params["provider"]
|
|
236
|
+
? params["provider"]
|
|
237
|
+
: isString(boardModel?.provider)
|
|
238
|
+
? boardModel.provider
|
|
239
|
+
: "";
|
|
240
|
+
const model = isString(params["model"]) && params["model"]
|
|
241
|
+
? params["model"]
|
|
242
|
+
: isString(boardModel?.id)
|
|
243
|
+
? boardModel.id
|
|
244
|
+
: "";
|
|
245
|
+
if (!provider || !model) {
|
|
246
|
+
return {
|
|
247
|
+
error: `No ${kind} model is set on this storyboard. Pass provider + model (use find_model with capability=${capability}), or set one on the board.`
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return { provider, model };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Render through the generation seam and answer with the asset it saved; the
|
|
254
|
+
* board can only reference persisted media. The seam records the row and
|
|
255
|
+
* links the asset (docs/media-generation-tracking-design.md § 8, S2).
|
|
256
|
+
*/
|
|
257
|
+
async function renderMedia(context, req, namePrefix, mime) {
|
|
258
|
+
const { randomUUID } = await import("node:crypto");
|
|
259
|
+
const id = randomUUID();
|
|
260
|
+
const persist = { name: namePrefix };
|
|
261
|
+
if (mime)
|
|
262
|
+
persist.mime = mime;
|
|
263
|
+
const result = await context.runGeneration({
|
|
264
|
+
...req,
|
|
265
|
+
id,
|
|
266
|
+
origin: { surface: "capability" },
|
|
267
|
+
persist
|
|
268
|
+
});
|
|
269
|
+
const asset = result.assets[0];
|
|
270
|
+
if (!asset?.asset_id) {
|
|
271
|
+
return {
|
|
272
|
+
error: "The render succeeded but could not be saved as an asset, so it cannot be attached to the shot. This host has no asset storage wired."
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
assetId: asset.asset_id,
|
|
277
|
+
uri: asset.uri,
|
|
278
|
+
generationId: id,
|
|
279
|
+
output: result.output
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* The clip a render actually produced, with its real length when readable.
|
|
284
|
+
*
|
|
285
|
+
* A video model quantizes the duration it is asked for — a shot directed at
|
|
286
|
+
* 1.5s came back as 5.184s — and the ref carried only the asset id, so
|
|
287
|
+
* assembly laid down the *intended* 1.5s over five seconds of footage and
|
|
288
|
+
* threw the rest away without saying so. The bytes are already in hand here,
|
|
289
|
+
* so the length is free to read; a container we cannot parse leaves `duration`
|
|
290
|
+
* unset, which every reader treats as unknown rather than zero.
|
|
291
|
+
*/
|
|
292
|
+
export function renderedVideoRef(saved) {
|
|
293
|
+
const clip = {
|
|
294
|
+
type: "video",
|
|
295
|
+
asset_id: saved.assetId,
|
|
296
|
+
uri: saved.uri
|
|
297
|
+
};
|
|
298
|
+
const bytes = saved.output;
|
|
299
|
+
if (bytes instanceof Uint8Array) {
|
|
300
|
+
const seconds = mp4DurationSeconds(bytes);
|
|
301
|
+
if (seconds !== null)
|
|
302
|
+
clip.duration = seconds;
|
|
303
|
+
}
|
|
304
|
+
return clip;
|
|
305
|
+
}
|
|
306
|
+
const errorMessage = (e) => e instanceof Error ? e.message : String(e);
|
|
307
|
+
const listStoryboards = {
|
|
308
|
+
spec: listStoryboardsSpec,
|
|
309
|
+
impl: async (run, params) => {
|
|
310
|
+
const userId = run.context.userId;
|
|
311
|
+
if (!userId)
|
|
312
|
+
return { error: "No user is bound to this session." };
|
|
313
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
314
|
+
const limit = Math.max(1, Math.min(Number(params["limit"]) || 20, 100));
|
|
315
|
+
const rows = await Storyboard.listByUser(userId, limit);
|
|
316
|
+
return {
|
|
317
|
+
storyboards: rows.map((row) => {
|
|
318
|
+
const doc = row.toDocument();
|
|
319
|
+
return {
|
|
320
|
+
id: row.id,
|
|
321
|
+
name: row.name,
|
|
322
|
+
shots: doc.shots.length,
|
|
323
|
+
with_keyframe: doc.shots.filter((s) => !!s.keyframe).length,
|
|
324
|
+
with_clip: doc.shots.filter((s) => !!s.clip).length,
|
|
325
|
+
timeline_id: row.timeline_id ?? undefined,
|
|
326
|
+
updated_at: row.updated_at
|
|
327
|
+
};
|
|
328
|
+
})
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
/** A blank board matching `storyboards.create` on the tRPC router. */
|
|
333
|
+
function createdBoardSummary(row) {
|
|
334
|
+
const doc = row.toDocument();
|
|
335
|
+
return {
|
|
336
|
+
ok: true,
|
|
337
|
+
storyboard_id: row.id,
|
|
338
|
+
// The same value under the key a caller reaches for first. Reading `.id`
|
|
339
|
+
// off this result and passing the `undefined` onward is what a create/edit
|
|
340
|
+
// pair actually did, and the edit blamed a missing argument two calls later.
|
|
341
|
+
id: row.id,
|
|
342
|
+
name: row.name,
|
|
343
|
+
project_id: row.project_id,
|
|
344
|
+
shots: doc.shots.length,
|
|
345
|
+
updated_at: row.updated_at
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
const createStoryboard = {
|
|
349
|
+
spec: createStoryboardSpec,
|
|
350
|
+
impl: async (run, params) => {
|
|
351
|
+
const userId = run.context.userId;
|
|
352
|
+
if (!userId)
|
|
353
|
+
return { error: "No user is bound to this session." };
|
|
354
|
+
const name = params["name"];
|
|
355
|
+
if (!isNonBlankString(name)) {
|
|
356
|
+
return { error: "name is required and must be a non-empty string." };
|
|
357
|
+
}
|
|
358
|
+
const projectId = resolveProjectId(run, params);
|
|
359
|
+
const requestedId = isNonBlankString(params["id"])
|
|
360
|
+
? params["id"].trim()
|
|
361
|
+
: undefined;
|
|
362
|
+
const { Storyboard, emptyStoryboardDocument } = await import("@nodetool-ai/models");
|
|
363
|
+
if (requestedId) {
|
|
364
|
+
const existing = await Storyboard.findById(requestedId);
|
|
365
|
+
if (existing) {
|
|
366
|
+
if (existing.user_id !== userId) {
|
|
367
|
+
return {
|
|
368
|
+
error: `A storyboard with id ${requestedId} already exists.`
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return createdBoardSummary(existing);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
const document = emptyStoryboardDocument();
|
|
375
|
+
if (isNonBlankString(params["brief"])) {
|
|
376
|
+
document.brief = params["brief"].trim();
|
|
377
|
+
}
|
|
378
|
+
if (isNonBlankString(params["style"])) {
|
|
379
|
+
document.style = params["style"].trim();
|
|
380
|
+
}
|
|
381
|
+
if (isNonBlankString(params["aspect_ratio"])) {
|
|
382
|
+
document.aspectRatio = params["aspect_ratio"].trim();
|
|
383
|
+
}
|
|
384
|
+
const fields = {
|
|
385
|
+
user_id: userId,
|
|
386
|
+
project_id: projectId,
|
|
387
|
+
name: name.trim(),
|
|
388
|
+
document: JSON.stringify(document)
|
|
389
|
+
};
|
|
390
|
+
if (requestedId) {
|
|
391
|
+
fields.id = requestedId;
|
|
392
|
+
}
|
|
393
|
+
const board = new Storyboard(fields);
|
|
394
|
+
await board.save();
|
|
395
|
+
return createdBoardSummary(board);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const getStoryboard = {
|
|
399
|
+
spec: getStoryboardSpec,
|
|
400
|
+
impl: async (run, params) => {
|
|
401
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
402
|
+
if (isError(board))
|
|
403
|
+
return board;
|
|
404
|
+
const { row, doc } = board;
|
|
405
|
+
const screenplay = boardScreenplay(row, doc);
|
|
406
|
+
const scriptDoc = await loadLinkedScript(screenplay, run.context.userId);
|
|
407
|
+
const link = await scriptLinkSummary(screenplay, scriptDoc);
|
|
408
|
+
const drifted = new Set(link.drifted_shot_ids);
|
|
409
|
+
return {
|
|
410
|
+
id: row.id,
|
|
411
|
+
name: row.name,
|
|
412
|
+
brief: doc.brief,
|
|
413
|
+
style: doc.style,
|
|
414
|
+
aspect_ratio: doc.aspectRatio,
|
|
415
|
+
image_model: doc.imageModel ?? null,
|
|
416
|
+
video_model: doc.videoModel ?? null,
|
|
417
|
+
entity_ids: doc.entityIds ?? [],
|
|
418
|
+
timeline_id: row.timeline_id ?? undefined,
|
|
419
|
+
narration: doc.screenplay?.narration ?? undefined,
|
|
420
|
+
music_prompt: doc.screenplay?.music_prompt ?? undefined,
|
|
421
|
+
script_id: link.script_id,
|
|
422
|
+
script_link: link,
|
|
423
|
+
shots: [...doc.shots]
|
|
424
|
+
.sort((a, b) => a.index - b.index)
|
|
425
|
+
.map((shot) => ({
|
|
426
|
+
id: shot.id,
|
|
427
|
+
index: shot.index,
|
|
428
|
+
slug: shot.slug,
|
|
429
|
+
action: shot.action,
|
|
430
|
+
camera: shot.camera,
|
|
431
|
+
motion: shot.motion,
|
|
432
|
+
duration_seconds: shot.duration_seconds,
|
|
433
|
+
status: shot.status,
|
|
434
|
+
has_keyframe: !!shot.keyframe,
|
|
435
|
+
has_clip: shotHasPicture(shot, doc.shots),
|
|
436
|
+
covered_by: shot.covered_by ?? null,
|
|
437
|
+
render_mode: shot.render_mode ?? "keyframe",
|
|
438
|
+
script_line_ids: shot.script_line_ids ?? [],
|
|
439
|
+
duration_source: shot.duration_source,
|
|
440
|
+
script_drifted: drifted.has(shot.id)
|
|
441
|
+
}))
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
/**
|
|
446
|
+
* The render path's host seam (`renderShots` in `@nodetool-ai/storyboard`).
|
|
447
|
+
*
|
|
448
|
+
* The package sits below runtime and models, so it takes these four operations
|
|
449
|
+
* rather than a `ProcessingContext`: the generation seam that records and
|
|
450
|
+
* persists a render, and the board read/write pair the per-shot CAS uses.
|
|
451
|
+
*/
|
|
452
|
+
function renderHost(context) {
|
|
453
|
+
return {
|
|
454
|
+
runGeneration: async (request) => {
|
|
455
|
+
const result = await context.runGeneration({
|
|
456
|
+
id: request.id,
|
|
457
|
+
provider: request.provider,
|
|
458
|
+
capability: request.capability,
|
|
459
|
+
model: request.model,
|
|
460
|
+
params: request.params,
|
|
461
|
+
origin: { surface: "capability" },
|
|
462
|
+
persist: request.persist,
|
|
463
|
+
destination: request.destination
|
|
464
|
+
});
|
|
465
|
+
return { output: result.output, assets: result.assets };
|
|
466
|
+
},
|
|
467
|
+
getStoryboard: async (id) => {
|
|
468
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
469
|
+
const row = await Storyboard.findById(id);
|
|
470
|
+
return row
|
|
471
|
+
? { document: row.toDocument(), updatedAt: row.updated_at }
|
|
472
|
+
: null;
|
|
473
|
+
},
|
|
474
|
+
updateStoryboard: async ({ id, document, baseUpdatedAt, shotId }) => {
|
|
475
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
476
|
+
const saved = await Storyboard.updateFieldsIfUnchanged(id, baseUpdatedAt, { document: JSON.stringify(document) },
|
|
477
|
+
// One update_shot per write, so an open editor merges this into its
|
|
478
|
+
// draft per shot instead of treating the board as replaced.
|
|
479
|
+
{ ops: [{ tool: "update_shot", input: { id: shotId } }] });
|
|
480
|
+
return saved ? { document, updatedAt: saved.updated_at } : null;
|
|
481
|
+
},
|
|
482
|
+
loadMedia: async (ref) => {
|
|
483
|
+
const { loadMediaRefBytes } = await import("@nodetool-ai/runtime");
|
|
484
|
+
return loadMediaRefBytes(ref, context);
|
|
485
|
+
},
|
|
486
|
+
videoDurationSeconds: (bytes) => mp4DurationSeconds(bytes)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
/** The per-shot result row a render capability answers with. */
|
|
490
|
+
function outcomeRow(outcome, withMode) {
|
|
491
|
+
const row = {
|
|
492
|
+
shot_id: outcome.shotId,
|
|
493
|
+
index: outcome.index,
|
|
494
|
+
slug: outcome.slug,
|
|
495
|
+
ok: outcome.ok
|
|
496
|
+
};
|
|
497
|
+
if (withMode)
|
|
498
|
+
row.render_mode = outcome.mode;
|
|
499
|
+
if (outcome.assetId !== undefined)
|
|
500
|
+
row.asset_id = outcome.assetId;
|
|
501
|
+
if (outcome.assetUri !== undefined)
|
|
502
|
+
row.asset_uri = outcome.assetUri;
|
|
503
|
+
if (outcome.generationId !== undefined) {
|
|
504
|
+
row.generation_id = outcome.generationId;
|
|
505
|
+
}
|
|
506
|
+
if (outcome.status !== undefined)
|
|
507
|
+
row.status = outcome.status;
|
|
508
|
+
if (outcome.error !== undefined)
|
|
509
|
+
row.error = outcome.error;
|
|
510
|
+
return row;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Drop the shots whose selected version is still current. Additive: without
|
|
514
|
+
* `stale_only` the selection is returned untouched.
|
|
515
|
+
*
|
|
516
|
+
* The freshness question is the render plan's, so a shot this skips is exactly
|
|
517
|
+
* a shot the node's `only_stale` skips.
|
|
518
|
+
*/
|
|
519
|
+
async function filterStale(selected, params, doc, entities, kind) {
|
|
520
|
+
if (params["stale_only"] !== true) {
|
|
521
|
+
return { shots: selected, skipped: [] };
|
|
522
|
+
}
|
|
523
|
+
const { planShotRenders } = await import("@nodetool-ai/storyboard");
|
|
524
|
+
const fresh = new Set(planShotRenders(doc, entities, kind, selected.map((shot) => shot.id))
|
|
525
|
+
.filter((plan) => plan.fresh)
|
|
526
|
+
.map((plan) => plan.shotId));
|
|
527
|
+
return {
|
|
528
|
+
shots: selected.filter((shot) => !fresh.has(shot.id)),
|
|
529
|
+
skipped: selected
|
|
530
|
+
.filter((shot) => fresh.has(shot.id))
|
|
531
|
+
.map((shot) => shot.id)
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
const renderStoryboardStills = {
|
|
535
|
+
spec: renderStoryboardStillsSpec,
|
|
536
|
+
impl: async (run, params) => {
|
|
537
|
+
const context = run.context;
|
|
538
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
539
|
+
if (isError(board))
|
|
540
|
+
return board;
|
|
541
|
+
const { row, doc } = board;
|
|
542
|
+
const model = resolveModel(params, doc.imageModel, "still", "text_to_image");
|
|
543
|
+
if (isError(model))
|
|
544
|
+
return model;
|
|
545
|
+
const { shotRenderMode } = await import("@nodetool-ai/protocol");
|
|
546
|
+
// A direct shot renders its clip from the prompt, so it needs no still.
|
|
547
|
+
// Naming it in `targets` still renders one — a board frame to look at is
|
|
548
|
+
// worth having even when the clip does not come from it.
|
|
549
|
+
const selected = selectShots(doc.shots, params["targets"], (s) => !s.keyframe &&
|
|
550
|
+
shotRenderMode(s) !== "direct" &&
|
|
551
|
+
!shotHasPicture(s, doc.shots));
|
|
552
|
+
if (isError(selected))
|
|
553
|
+
return selected;
|
|
554
|
+
const entities = await loadBoardEntities(context, doc);
|
|
555
|
+
const fresh = await filterStale(selected, params, doc, entities, "keyframe");
|
|
556
|
+
const skipped = fresh.skipped;
|
|
557
|
+
const chosen = fresh.shots;
|
|
558
|
+
if (chosen.length === 0) {
|
|
559
|
+
return {
|
|
560
|
+
rendered: 0,
|
|
561
|
+
results: [],
|
|
562
|
+
skipped,
|
|
563
|
+
note: skipped.length > 0
|
|
564
|
+
? "No selected shot's still is stale."
|
|
565
|
+
: "No shot needs a still: each already has one, or renders its clip directly."
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
if (chosen.length > MAX_SHOTS_PER_CALL) {
|
|
569
|
+
return {
|
|
570
|
+
error: `${chosen.length} shots selected, over the ${MAX_SHOTS_PER_CALL}-shot per-call limit. Pass targets in batches.`
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
const { planShotRenders, renderShots } = await import("@nodetool-ai/storyboard");
|
|
574
|
+
// What this call actually renders with, which is not always what the board
|
|
575
|
+
// says: `style` and `model` can be overridden per call. Recording the
|
|
576
|
+
// override is the point — a version rendered against something other than
|
|
577
|
+
// the board's settings reads stale against the board, correctly.
|
|
578
|
+
const planOptions = {
|
|
579
|
+
provider: model.provider,
|
|
580
|
+
model: model.model
|
|
581
|
+
};
|
|
582
|
+
if (isString(params["style"])) {
|
|
583
|
+
planOptions.style = params["style"];
|
|
584
|
+
}
|
|
585
|
+
const plans = planShotRenders(doc, entities, "keyframe", chosen.map((shot) => shot.id), planOptions);
|
|
586
|
+
const outcomes = await renderShots(renderHost(context), { id: row.id }, plans, {
|
|
587
|
+
concurrency: clampConcurrency(params["concurrency"])
|
|
588
|
+
});
|
|
589
|
+
const results = outcomes.map((outcome) => outcomeRow(outcome, false));
|
|
590
|
+
return {
|
|
591
|
+
storyboard_id: row.id,
|
|
592
|
+
provider: model.provider,
|
|
593
|
+
model: model.model,
|
|
594
|
+
rendered: results.filter((r) => r.ok).length,
|
|
595
|
+
failed: results.filter((r) => !r.ok).length,
|
|
596
|
+
skipped,
|
|
597
|
+
results
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
const renderStoryboardClips = {
|
|
602
|
+
spec: renderStoryboardClipsSpec,
|
|
603
|
+
impl: async (run, params) => {
|
|
604
|
+
const context = run.context;
|
|
605
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
606
|
+
if (isError(board))
|
|
607
|
+
return board;
|
|
608
|
+
const { row, doc } = board;
|
|
609
|
+
const override = params["mode"];
|
|
610
|
+
if (override !== undefined &&
|
|
611
|
+
override !== "keyframe" &&
|
|
612
|
+
override !== "direct" &&
|
|
613
|
+
override !== "reference") {
|
|
614
|
+
return { error: 'mode must be "keyframe", "direct", or "reference".' };
|
|
615
|
+
}
|
|
616
|
+
// The call's override wins over the shot's own setting, for this call only.
|
|
617
|
+
const { shotRenderMode } = await import("@nodetool-ai/protocol");
|
|
618
|
+
const modeOf = (shot) => override === "keyframe" ||
|
|
619
|
+
override === "direct" ||
|
|
620
|
+
override === "reference"
|
|
621
|
+
? override
|
|
622
|
+
: shotRenderMode(shot);
|
|
623
|
+
const selected = selectShots(doc.shots, params["targets"], (s) => !shotHasPicture(s, doc.shots) &&
|
|
624
|
+
(modeOf(s) === "direct" || modeOf(s) === "reference" || !!s.keyframe));
|
|
625
|
+
if (isError(selected))
|
|
626
|
+
return selected;
|
|
627
|
+
const requiredCapabilities = new Set(selected.map((shot) => modeOf(shot) === "direct"
|
|
628
|
+
? "text_to_video"
|
|
629
|
+
: modeOf(shot) === "reference"
|
|
630
|
+
? "reference_to_video"
|
|
631
|
+
: "image_to_video"));
|
|
632
|
+
const capability = requiredCapabilities.has("reference_to_video")
|
|
633
|
+
? "reference_to_video"
|
|
634
|
+
: requiredCapabilities.has("image_to_video")
|
|
635
|
+
? "image_to_video"
|
|
636
|
+
: "text_to_video";
|
|
637
|
+
const model = resolveModel(params, doc.videoModel, "clip", capability);
|
|
638
|
+
if (isError(model))
|
|
639
|
+
return model;
|
|
640
|
+
const declaredTasks = doc.videoModel?.supported_tasks;
|
|
641
|
+
if (Array.isArray(declaredTasks) &&
|
|
642
|
+
declaredTasks.length > 0 &&
|
|
643
|
+
model.model === doc.videoModel?.id &&
|
|
644
|
+
model.provider === doc.videoModel?.provider) {
|
|
645
|
+
const unsupported = [...requiredCapabilities].find((task) => !declaredTasks.includes(task));
|
|
646
|
+
if (unsupported) {
|
|
647
|
+
return {
|
|
648
|
+
error: `The selected clip model does not support ${unsupported}; choose a model that supports every selected shot mode.`
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const entities = await loadBoardEntities(context, doc);
|
|
653
|
+
const fresh = await filterStale(selected, params, doc, entities, "clip");
|
|
654
|
+
const skipped = fresh.skipped;
|
|
655
|
+
const chosen = fresh.shots;
|
|
656
|
+
if (chosen.length === 0) {
|
|
657
|
+
return {
|
|
658
|
+
rendered: 0,
|
|
659
|
+
results: [],
|
|
660
|
+
skipped,
|
|
661
|
+
note: skipped.length > 0
|
|
662
|
+
? "No selected shot's clip is stale."
|
|
663
|
+
: 'No shot is ready for a clip. A keyframe-mode shot needs a still first (render_storyboard_stills), or set its render_mode to "direct" — or pass mode: "direct" here — to render straight from the prompt. Name shots explicitly with `targets` to override the selection.'
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
if (chosen.length > MAX_SHOTS_PER_CALL) {
|
|
667
|
+
return {
|
|
668
|
+
error: `${chosen.length} shots selected, over the ${MAX_SHOTS_PER_CALL}-shot per-call limit. Pass targets in batches.`
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
const { scriptLinesById } = await import("@nodetool-ai/timeline");
|
|
672
|
+
const { planShotRenders, renderShots } = await import("@nodetool-ai/storyboard");
|
|
673
|
+
// A linked board times its shots from the words they cover, so a clip is
|
|
674
|
+
// rendered long enough to hold its voiceover (design §2.3). A shot pinned
|
|
675
|
+
// to `manual`, an unvoiced line, or an unlinked board keeps
|
|
676
|
+
// `duration_seconds`.
|
|
677
|
+
const scriptDoc = await loadLinkedScript(boardScreenplay(row, doc), context.userId);
|
|
678
|
+
// As in the stills path: the record says what this call rendered with, so
|
|
679
|
+
// a per-call model or style override reads stale against the board.
|
|
680
|
+
const planOptions = {
|
|
681
|
+
provider: model.provider,
|
|
682
|
+
model: model.model,
|
|
683
|
+
style: isString(params["style"]) ? params["style"] : doc.style || "",
|
|
684
|
+
scriptLines: scriptLinesById(scriptDoc?.sections ?? [])
|
|
685
|
+
};
|
|
686
|
+
if (override !== undefined) {
|
|
687
|
+
planOptions.mode = override;
|
|
688
|
+
}
|
|
689
|
+
const plans = planShotRenders(doc, entities, "clip", chosen.map((shot) => shot.id), planOptions);
|
|
690
|
+
const renderOptions = {
|
|
691
|
+
concurrency: clampConcurrency(params["concurrency"])
|
|
692
|
+
};
|
|
693
|
+
if (isString(params["resolution"])) {
|
|
694
|
+
renderOptions.resolution = params["resolution"];
|
|
695
|
+
}
|
|
696
|
+
const outcomes = await renderShots(renderHost(context), { id: row.id }, plans, renderOptions);
|
|
697
|
+
const results = outcomes.map((outcome) => outcomeRow(outcome, true));
|
|
698
|
+
return {
|
|
699
|
+
storyboard_id: row.id,
|
|
700
|
+
provider: model.provider,
|
|
701
|
+
model: model.model,
|
|
702
|
+
rendered: results.filter((r) => r.ok).length,
|
|
703
|
+
failed: results.filter((r) => !r.ok).length,
|
|
704
|
+
skipped,
|
|
705
|
+
results
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
const reviseStoryboardClip = {
|
|
710
|
+
spec: reviseStoryboardClipSpec,
|
|
711
|
+
impl: async (run, params) => {
|
|
712
|
+
const context = run.context;
|
|
713
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
714
|
+
if (isError(board))
|
|
715
|
+
return board;
|
|
716
|
+
const { row, doc } = board;
|
|
717
|
+
const instruction = String(params["instruction"] ?? "").trim();
|
|
718
|
+
if (!instruction)
|
|
719
|
+
return { error: "instruction is required." };
|
|
720
|
+
const shot = findShot(doc.shots, String(params["target"] ?? ""));
|
|
721
|
+
if (!shot) {
|
|
722
|
+
return {
|
|
723
|
+
error: `No shot matches "${String(params["target"])}". Call get_storyboard to list shot ids.`
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
if (!shot.clip) {
|
|
727
|
+
return {
|
|
728
|
+
error: "Shot has no clip to revise. Run render_storyboard_clips first."
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
const model = resolveModel(params, doc.videoModel, "clip", "video_to_video");
|
|
732
|
+
if (isError(model))
|
|
733
|
+
return model;
|
|
734
|
+
const { loadMediaRefBytes } = await import("@nodetool-ai/runtime");
|
|
735
|
+
try {
|
|
736
|
+
const source = await loadMediaRefBytes(shot.clip, context);
|
|
737
|
+
if (!source || source.length === 0) {
|
|
738
|
+
return {
|
|
739
|
+
error: "The shot's clip could not be read back from storage."
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
const saved = await renderMedia(context, {
|
|
743
|
+
provider: model.provider,
|
|
744
|
+
capability: "video_to_video",
|
|
745
|
+
model: model.model,
|
|
746
|
+
params: { video: source, prompt: instruction }
|
|
747
|
+
}, `shot-${shot.index + 1}-revision`, "video/mp4");
|
|
748
|
+
if (isError(saved))
|
|
749
|
+
return saved;
|
|
750
|
+
const clip = renderedVideoRef(saved);
|
|
751
|
+
const updated = await patchShot(row.id, shot.id, (current) => {
|
|
752
|
+
const versions = current.clip_versions ?? (current.clip ? [current.clip] : []);
|
|
753
|
+
return {
|
|
754
|
+
...current,
|
|
755
|
+
clip,
|
|
756
|
+
clip_versions: [...versions, clip],
|
|
757
|
+
status: "rendered"
|
|
758
|
+
};
|
|
759
|
+
});
|
|
760
|
+
if (isError(updated))
|
|
761
|
+
return updated;
|
|
762
|
+
return {
|
|
763
|
+
ok: true,
|
|
764
|
+
shot_id: shot.id,
|
|
765
|
+
index: shot.index,
|
|
766
|
+
asset_id: saved.assetId,
|
|
767
|
+
asset_uri: saved.uri,
|
|
768
|
+
generation_id: saved.generationId,
|
|
769
|
+
status: updated.status
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
catch (e) {
|
|
773
|
+
return { error: `video_to_video failed: ${errorMessage(e)}` };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
// ---------------------------------------------------------------------------
|
|
778
|
+
// assemble_storyboard_timeline
|
|
779
|
+
// ---------------------------------------------------------------------------
|
|
780
|
+
const assembleStoryboardTimeline = {
|
|
781
|
+
spec: assembleStoryboardTimelineSpec,
|
|
782
|
+
impl: async (run, params) => {
|
|
783
|
+
const context = run.context;
|
|
784
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
785
|
+
if (isError(board))
|
|
786
|
+
return board;
|
|
787
|
+
const { row, doc } = board;
|
|
788
|
+
const { Storyboard, TimelineSequence } = await import("@nodetool-ai/models");
|
|
789
|
+
const { buildLinkedTimeline, buildStoryboardTimeline, foreignTimelineParts, frameSizeForAspect } = await import("@nodetool-ai/timeline");
|
|
790
|
+
// A board that links a script is cut against the words: shot lengths come
|
|
791
|
+
// from the takes and every voiced line gets its own clip. A link pointing
|
|
792
|
+
// at a script that is gone is a warning, not a failure — the board still
|
|
793
|
+
// assembles on its own (design §1.3).
|
|
794
|
+
const scriptId = doc.screenplay?.script_id ?? null;
|
|
795
|
+
const script = scriptId
|
|
796
|
+
? await loadLinkedAssemblyScript(scriptId, context.userId)
|
|
797
|
+
: null;
|
|
798
|
+
const warnings = [];
|
|
799
|
+
if (scriptId && !script) {
|
|
800
|
+
warnings.push(`Script ${scriptId} is linked but could not be loaded, so the board was assembled on its own.`);
|
|
801
|
+
}
|
|
802
|
+
const { loadMediaRefBytes, probeVideoDurationSeconds } = await import("@nodetool-ai/runtime");
|
|
803
|
+
const { applyMeasuredShotClipDurations } = await import("@nodetool-ai/timeline");
|
|
804
|
+
const signal = context.signal ?? new AbortController().signal;
|
|
805
|
+
const measurements = await mapWithConcurrency(doc.shots, clampConcurrency(undefined), async (shot) => {
|
|
806
|
+
const clip = shot.clip;
|
|
807
|
+
const assetId = clip?.asset_id;
|
|
808
|
+
if (shot.status !== "rendered" || !assetId)
|
|
809
|
+
return null;
|
|
810
|
+
try {
|
|
811
|
+
const bytes = await loadMediaRefBytes(clip, context);
|
|
812
|
+
const seconds = bytes?.length
|
|
813
|
+
? await probeVideoDurationSeconds(bytes, signal)
|
|
814
|
+
: null;
|
|
815
|
+
return seconds === null ? null : [assetId, seconds];
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
if (signal.aborted)
|
|
819
|
+
throw error;
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
const durations = new Map();
|
|
824
|
+
for (const measurement of measurements) {
|
|
825
|
+
if (measurement)
|
|
826
|
+
durations.set(...measurement);
|
|
827
|
+
}
|
|
828
|
+
const measuredShots = applyMeasuredShotClipDurations(doc.shots, durations);
|
|
829
|
+
const assembled = script
|
|
830
|
+
? buildLinkedTimeline({
|
|
831
|
+
boardId: row.id,
|
|
832
|
+
shots: measuredShots,
|
|
833
|
+
musicPrompt: doc.screenplay?.music_prompt,
|
|
834
|
+
script
|
|
835
|
+
})
|
|
836
|
+
: buildStoryboardTimeline({
|
|
837
|
+
boardId: row.id,
|
|
838
|
+
shots: measuredShots,
|
|
839
|
+
narration: doc.screenplay?.narration,
|
|
840
|
+
musicPrompt: doc.screenplay?.music_prompt
|
|
841
|
+
});
|
|
842
|
+
const skippedLineIds = "skippedLineIds" in assembled ? assembled.skippedLineIds : [];
|
|
843
|
+
// A model returns the length it returns: shots directed at 1.5s come back
|
|
844
|
+
// at 5.2s, and the cut plays all of it. Saying which shots came back off
|
|
845
|
+
// the length they were directed at turns a film longer than the caller
|
|
846
|
+
// planned into a decision they can make.
|
|
847
|
+
if (assembled.retimedShots.length > 0) {
|
|
848
|
+
const worst = assembled.retimedShots.reduce((a, b) => Math.abs(b.directedMs - b.usedMs) > Math.abs(a.directedMs - a.usedMs)
|
|
849
|
+
? b
|
|
850
|
+
: a);
|
|
851
|
+
warnings.push(`${assembled.retimedShots.length} shot(s) run at their rendered length, not the length they were directed at — ` +
|
|
852
|
+
`the largest difference is ${Math.round(Math.abs(worst.directedMs - worst.usedMs))}ms on shot ${worst.shotId} ` +
|
|
853
|
+
`(${worst.usedMs}ms in the cut, ${worst.directedMs}ms directed). ` +
|
|
854
|
+
`Re-render with a duration the model honours, or trim the clips in the timeline.`);
|
|
855
|
+
}
|
|
856
|
+
// A jointly assembled cut takes its lengths from the words, so a shot can
|
|
857
|
+
// sit in a slot its footage does not fill: black under the voiceover, or a
|
|
858
|
+
// render the cut never reaches the end of.
|
|
859
|
+
if (assembled.trimmedShots.length > 0) {
|
|
860
|
+
const worst = assembled.trimmedShots.reduce((a, b) => Math.abs(b.sourceMs - b.usedMs) > Math.abs(a.sourceMs - a.usedMs)
|
|
861
|
+
? b
|
|
862
|
+
: a);
|
|
863
|
+
warnings.push(`${assembled.trimmedShots.length} shot(s) do not match their rendered footage — ` +
|
|
864
|
+
`the longest gap is ${Math.round(Math.abs(worst.sourceMs - worst.usedMs))}ms on shot ${worst.shotId} ` +
|
|
865
|
+
`(${worst.usedMs}ms in the cut, ${worst.sourceMs}ms rendered). ` +
|
|
866
|
+
`Re-render those shots at the length their lines need.`);
|
|
867
|
+
}
|
|
868
|
+
if (assembled.clips.length === 0) {
|
|
869
|
+
return {
|
|
870
|
+
error: "No shot has a rendered clip, so there is nothing to assemble. Run render_storyboard_stills, then render_storyboard_clips.",
|
|
871
|
+
skipped_shot_ids: assembled.skippedShotIds,
|
|
872
|
+
skipped_line_ids: skippedLineIds
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
const { width, height } = frameSizeForAspect(doc.aspectRatio);
|
|
876
|
+
const fps = Math.max(1, Math.min(Number(params["fps"]) || 30, 120));
|
|
877
|
+
const name = isString(params["name"]) && params["name"] ? params["name"] : row.name;
|
|
878
|
+
// Re-assembling replaces the board's existing cut rather than leaving a
|
|
879
|
+
// trail of orphan sequences behind it — and rewrites only what this board
|
|
880
|
+
// (and, when linked, this script) owns.
|
|
881
|
+
const existing = row.timeline_id
|
|
882
|
+
? await TimelineSequence.findById(row.timeline_id)
|
|
883
|
+
: null;
|
|
884
|
+
const reuse = existing && existing.user_id === context.userId ? existing : null;
|
|
885
|
+
if (!reuse) {
|
|
886
|
+
const sequence = new TimelineSequence({
|
|
887
|
+
user_id: context.userId,
|
|
888
|
+
project_id: row.project_id,
|
|
889
|
+
name
|
|
890
|
+
});
|
|
891
|
+
sequence.name = name;
|
|
892
|
+
sequence.fps = fps;
|
|
893
|
+
sequence.width = width;
|
|
894
|
+
sequence.height = height;
|
|
895
|
+
sequence.duration_ms = assembled.durationMs;
|
|
896
|
+
sequence.fromDocument({
|
|
897
|
+
tracks: assembled.tracks,
|
|
898
|
+
clips: assembled.clips,
|
|
899
|
+
markers: []
|
|
900
|
+
});
|
|
901
|
+
await sequence.save();
|
|
902
|
+
if (row.timeline_id !== sequence.id) {
|
|
903
|
+
await Storyboard.updateFieldsIfUnchanged(row.id, row.updated_at, { timeline_id: sequence.id }, { ops: [{ tool: "set_link", input: { timeline_id: sequence.id } }] });
|
|
904
|
+
}
|
|
905
|
+
const created = {
|
|
906
|
+
ok: true,
|
|
907
|
+
timeline_id: sequence.id,
|
|
908
|
+
name: sequence.name,
|
|
909
|
+
fps,
|
|
910
|
+
width,
|
|
911
|
+
height,
|
|
912
|
+
duration_ms: assembled.durationMs,
|
|
913
|
+
clip_count: assembled.clips.length,
|
|
914
|
+
track_count: assembled.tracks.length,
|
|
915
|
+
script_id: script ? scriptId : null,
|
|
916
|
+
skipped_shot_ids: assembled.skippedShotIds,
|
|
917
|
+
skipped_line_ids: skippedLineIds,
|
|
918
|
+
trimmed_shots: assembled.trimmedShots,
|
|
919
|
+
retimed_shots: assembled.retimedShots
|
|
920
|
+
};
|
|
921
|
+
if (warnings.length) {
|
|
922
|
+
created.warnings = warnings;
|
|
923
|
+
}
|
|
924
|
+
return created;
|
|
925
|
+
}
|
|
926
|
+
// Re-assembling over an existing sequence is a CAS write carrying its
|
|
927
|
+
// ops (S2.1): the foreign parts are re-read per attempt, and an open
|
|
928
|
+
// editor merges the change instead of seeing the sequence as replaced.
|
|
929
|
+
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
930
|
+
const current = attempt === 0 ? reuse : await TimelineSequence.findById(reuse.id);
|
|
931
|
+
if (!current || current.user_id !== context.userId) {
|
|
932
|
+
return {
|
|
933
|
+
error: `Timeline ${reuse.id} was deleted while assembling; create a new timeline by calling this again.`
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
const previous = current.toDocument();
|
|
937
|
+
const foreign = foreignTimelineParts(previous, (clip) => clip.storyboardBoardId === row.id ||
|
|
938
|
+
(!!scriptId && clip.scriptId === scriptId));
|
|
939
|
+
const tracks = [...assembled.tracks, ...foreign.tracks];
|
|
940
|
+
const clips = [...assembled.clips, ...foreign.clips];
|
|
941
|
+
const durationMs = clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0);
|
|
942
|
+
const nextDocument = {
|
|
943
|
+
...previous,
|
|
944
|
+
tracks,
|
|
945
|
+
clips,
|
|
946
|
+
markers: previous.markers ?? []
|
|
947
|
+
};
|
|
948
|
+
const saved = await TimelineSequence.updateFieldsIfUnchanged(current.id, current.updated_at, {
|
|
949
|
+
name,
|
|
950
|
+
fps,
|
|
951
|
+
width,
|
|
952
|
+
height,
|
|
953
|
+
duration_ms: durationMs,
|
|
954
|
+
document: JSON.stringify(nextDocument)
|
|
955
|
+
}, {
|
|
956
|
+
ops: [
|
|
957
|
+
{
|
|
958
|
+
tool: "assemble_storyboard_timeline",
|
|
959
|
+
input: { storyboard_id: row.id }
|
|
960
|
+
}
|
|
961
|
+
]
|
|
962
|
+
});
|
|
963
|
+
if (!saved)
|
|
964
|
+
continue;
|
|
965
|
+
if (row.timeline_id !== current.id) {
|
|
966
|
+
await Storyboard.updateFieldsIfUnchanged(row.id, row.updated_at, { timeline_id: current.id }, { ops: [{ tool: "set_link", input: { timeline_id: current.id } }] });
|
|
967
|
+
}
|
|
968
|
+
const result = {
|
|
969
|
+
ok: true,
|
|
970
|
+
timeline_id: current.id,
|
|
971
|
+
name,
|
|
972
|
+
fps,
|
|
973
|
+
width,
|
|
974
|
+
height,
|
|
975
|
+
duration_ms: durationMs,
|
|
976
|
+
clip_count: clips.length,
|
|
977
|
+
track_count: tracks.length,
|
|
978
|
+
script_id: script ? scriptId : null,
|
|
979
|
+
skipped_shot_ids: assembled.skippedShotIds,
|
|
980
|
+
skipped_line_ids: skippedLineIds,
|
|
981
|
+
trimmed_shots: assembled.trimmedShots,
|
|
982
|
+
retimed_shots: assembled.retimedShots
|
|
983
|
+
};
|
|
984
|
+
if (warnings.length) {
|
|
985
|
+
result.warnings = warnings;
|
|
986
|
+
}
|
|
987
|
+
return result;
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
error: `Timeline ${reuse.id} is being modified concurrently; the assembly finished but could not be saved. Retry the call.`
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
// ---------------------------------------------------------------------------
|
|
995
|
+
// edit_storyboard
|
|
996
|
+
// ---------------------------------------------------------------------------
|
|
997
|
+
//
|
|
998
|
+
// The render capabilities above fill a board in; this one shapes it. Adding,
|
|
999
|
+
// rewriting and reordering shots was browser-only — the `ui_storyboard_*` tools
|
|
1000
|
+
// round-trip into the open editor's Zustand store — so an agent working
|
|
1001
|
+
// headlessly could render a board it could not author.
|
|
1002
|
+
/** Operations one call may apply, so a runaway script cannot rewrite a board. */
|
|
1003
|
+
const MAX_BOARD_OPS = 60;
|
|
1004
|
+
const BOARD_OPS = [
|
|
1005
|
+
"add_shot",
|
|
1006
|
+
"update_shot",
|
|
1007
|
+
"remove_shot",
|
|
1008
|
+
"reorder_shot",
|
|
1009
|
+
"move_shot",
|
|
1010
|
+
"duplicate_shot",
|
|
1011
|
+
"set_board",
|
|
1012
|
+
"set_setup",
|
|
1013
|
+
"update_scene",
|
|
1014
|
+
"create_scene",
|
|
1015
|
+
"merge_scene",
|
|
1016
|
+
"set_style",
|
|
1017
|
+
"select_version",
|
|
1018
|
+
"delete_version",
|
|
1019
|
+
"add_keyframe_version"
|
|
1020
|
+
];
|
|
1021
|
+
/**
|
|
1022
|
+
* Op names an agent reaches for that mean one of {@link BOARD_OPS}.
|
|
1023
|
+
*
|
|
1024
|
+
* The browser tool that casts entities is `ui_storyboard_set_entities`, so a
|
|
1025
|
+
* script written against it calls `{op: "set_entities", entity_ids: [...]}`
|
|
1026
|
+
* here and was refused — the rejection listed the five real ops without
|
|
1027
|
+
* saying which one takes `entity_ids`, and the caller had to go read the tool
|
|
1028
|
+
* catalogue mid-task. The alias resolves to the op that already does the work.
|
|
1029
|
+
*/
|
|
1030
|
+
const BOARD_OP_ALIASES = {
|
|
1031
|
+
set_entities: "set_board",
|
|
1032
|
+
set_storyboard: "set_board",
|
|
1033
|
+
add_shots: "add_shot",
|
|
1034
|
+
edit_shot: "update_shot",
|
|
1035
|
+
delete_shot: "remove_shot",
|
|
1036
|
+
// `move_shot` used to alias `reorder_shot`. It is its own op now, and the
|
|
1037
|
+
// two coincide on a board with no scenes: one implicit group means a
|
|
1038
|
+
// position inside it is a board position.
|
|
1039
|
+
copy_shot: "duplicate_shot",
|
|
1040
|
+
set_scene: "update_scene",
|
|
1041
|
+
add_scene: "create_scene",
|
|
1042
|
+
set_setup_stage: "set_setup"
|
|
1043
|
+
};
|
|
1044
|
+
const isBoardOpName = (value) => BOARD_OPS.includes(value);
|
|
1045
|
+
/** The op a name selects, following {@link BOARD_OP_ALIASES}. */
|
|
1046
|
+
export const resolveBoardOpName = (value) => {
|
|
1047
|
+
const name = value.trim();
|
|
1048
|
+
if (isBoardOpName(name))
|
|
1049
|
+
return name;
|
|
1050
|
+
return BOARD_OP_ALIASES[name] ?? null;
|
|
1051
|
+
};
|
|
1052
|
+
function parseBoardOps(raw) {
|
|
1053
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
1054
|
+
return {
|
|
1055
|
+
error: 'ops must be a non-empty array, e.g. [{"op": "add_shot", "action": "Wide of the lighthouse at dusk"}].'
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
if (raw.length > MAX_BOARD_OPS) {
|
|
1059
|
+
return {
|
|
1060
|
+
error: `ops holds ${raw.length} entries; at most ${MAX_BOARD_OPS} per call.`
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
const parsed = [];
|
|
1064
|
+
for (const [index, entry] of raw.entries()) {
|
|
1065
|
+
if (!isRecord(entry)) {
|
|
1066
|
+
return { error: `ops[${index}] must be an object.` };
|
|
1067
|
+
}
|
|
1068
|
+
const { op, ...args } = entry;
|
|
1069
|
+
const resolved = isString(op) ? resolveBoardOpName(op) : null;
|
|
1070
|
+
if (!resolved) {
|
|
1071
|
+
return {
|
|
1072
|
+
error: `ops[${index}] names "${String(op)}"; expected one of ${BOARD_OPS.join(", ")}.`
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
parsed.push({ op: resolved, args });
|
|
1076
|
+
}
|
|
1077
|
+
return parsed;
|
|
1078
|
+
}
|
|
1079
|
+
/** Renumber `index` to the array order — the field the editor sorts on. */
|
|
1080
|
+
function renumberShots(shots) {
|
|
1081
|
+
return shots.map((shot, index) => shot.index === index ? shot : { ...shot, index });
|
|
1082
|
+
}
|
|
1083
|
+
// ── Scenes ──────────────────────────────────────────────────────────────────
|
|
1084
|
+
// A scene has no order of its own: its position is the position of its first
|
|
1085
|
+
// shot, and its shots are contiguous in `index` (PRD § 7.7.3). These mirror
|
|
1086
|
+
// `web/src/lib/storyboard/sceneOrder.ts` and the store's `structural`, so a
|
|
1087
|
+
// board edited headlessly and one edited in the browser end up the same shape.
|
|
1088
|
+
/** The board's scenes. They live on the screenplay; the document reads them. */
|
|
1089
|
+
function docScenes(doc) {
|
|
1090
|
+
return doc.screenplay?.scenes ?? [];
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* A fresh id for a screenplay, scene or shot. A clock and a count are not
|
|
1094
|
+
* enough: ops in one batch run within the same millisecond, and a count
|
|
1095
|
+
* repeats after a remove or when two counts coincide.
|
|
1096
|
+
*/
|
|
1097
|
+
function newDocId(prefix) {
|
|
1098
|
+
return `${prefix}_${crypto.randomUUID().replaceAll("-", "").slice(0, 16)}`;
|
|
1099
|
+
}
|
|
1100
|
+
/** Write scenes back, materializing a minimal screenplay when there is none. */
|
|
1101
|
+
function setDocScenes(doc, scenes) {
|
|
1102
|
+
if (doc.screenplay) {
|
|
1103
|
+
doc.screenplay = { ...doc.screenplay, scenes };
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
if (scenes.length === 0)
|
|
1107
|
+
return;
|
|
1108
|
+
doc.screenplay = {
|
|
1109
|
+
type: "screenplay",
|
|
1110
|
+
id: newDocId("sp"),
|
|
1111
|
+
title: "",
|
|
1112
|
+
shots: [],
|
|
1113
|
+
scenes
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
/** Scenes in derived order, each with its shots. `null` is the implicit header. */
|
|
1117
|
+
function sceneGroups(shots) {
|
|
1118
|
+
const groups = [];
|
|
1119
|
+
const byKey = new Map();
|
|
1120
|
+
for (const shot of [...shots].sort((a, b) => a.index - b.index)) {
|
|
1121
|
+
const key = shot.scene_id ?? null;
|
|
1122
|
+
let group = byKey.get(key);
|
|
1123
|
+
if (!group) {
|
|
1124
|
+
group = { sceneId: key, shots: [] };
|
|
1125
|
+
byKey.set(key, group);
|
|
1126
|
+
groups.push(group);
|
|
1127
|
+
}
|
|
1128
|
+
group.shots.push(shot);
|
|
1129
|
+
}
|
|
1130
|
+
return groups;
|
|
1131
|
+
}
|
|
1132
|
+
/** Put a shot in a scene, or out of every scene when `sceneId` is null. */
|
|
1133
|
+
function withScene(shot, sceneId) {
|
|
1134
|
+
if ((shot.scene_id ?? null) === sceneId)
|
|
1135
|
+
return shot;
|
|
1136
|
+
const next = { ...shot };
|
|
1137
|
+
if (sceneId === null) {
|
|
1138
|
+
delete next.scene_id;
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
next.scene_id = sceneId;
|
|
1142
|
+
}
|
|
1143
|
+
return next;
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Finish a structural edit: collect each scene's shots into one run in the
|
|
1147
|
+
* order the board already renders them, renumber `0..n-1`, and drop every
|
|
1148
|
+
* scene no shot is in. The caller's array order is the intent, so it is
|
|
1149
|
+
* stamped before grouping.
|
|
1150
|
+
*/
|
|
1151
|
+
function applyStructural(doc, shots) {
|
|
1152
|
+
const proposed = renumberShots(shots);
|
|
1153
|
+
doc.shots = renumberShots(sceneGroups(proposed).flatMap((group) => group.shots));
|
|
1154
|
+
const used = new Set(doc.shots.map((shot) => shot.scene_id).filter((id) => !!id));
|
|
1155
|
+
const scenes = docScenes(doc);
|
|
1156
|
+
const pruned = scenes.filter((scene) => used.has(scene.id));
|
|
1157
|
+
if (pruned.length !== scenes.length) {
|
|
1158
|
+
setDocScenes(doc, pruned);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* The first scene-creating operation on a board with unscened shots puts them
|
|
1163
|
+
* all in one new scene, in index order (PRD § 7.7.3). Returns that scene's id,
|
|
1164
|
+
* or null when there was nothing unscened.
|
|
1165
|
+
*/
|
|
1166
|
+
function materializeLegacyScene(doc) {
|
|
1167
|
+
if (!doc.shots.some((shot) => !shot.scene_id))
|
|
1168
|
+
return null;
|
|
1169
|
+
const scene = {
|
|
1170
|
+
type: "scene",
|
|
1171
|
+
id: newDocId("scene"),
|
|
1172
|
+
slugline: ""
|
|
1173
|
+
};
|
|
1174
|
+
doc.shots = doc.shots.map((shot) => shot.scene_id ? shot : withScene(shot, scene.id));
|
|
1175
|
+
setDocScenes(doc, [...docScenes(doc), scene]);
|
|
1176
|
+
return scene.id;
|
|
1177
|
+
}
|
|
1178
|
+
/** A blank planned shot. `index` is stamped by the reindex that follows. */
|
|
1179
|
+
function blankShot(id, sceneId) {
|
|
1180
|
+
const shot = {
|
|
1181
|
+
type: "shot",
|
|
1182
|
+
id,
|
|
1183
|
+
index: 0,
|
|
1184
|
+
action: "",
|
|
1185
|
+
status: "planned"
|
|
1186
|
+
};
|
|
1187
|
+
return sceneId === null ? shot : withScene(shot, sceneId);
|
|
1188
|
+
}
|
|
1189
|
+
/** A shot's stills or takes, oldest first, seeded from a legacy single ref. */
|
|
1190
|
+
function versionList(shot, kind) {
|
|
1191
|
+
if (kind === "keyframe") {
|
|
1192
|
+
return shot.keyframe_versions ?? (shot.keyframe ? [shot.keyframe] : []);
|
|
1193
|
+
}
|
|
1194
|
+
return shot.clip_versions ?? (shot.clip ? [shot.clip] : []);
|
|
1195
|
+
}
|
|
1196
|
+
/** Read `kind` and a 0-based `version` off an op, refusing anything else. */
|
|
1197
|
+
function readVersionTarget(shot, args) {
|
|
1198
|
+
const kind = String(args["kind"] ?? "keyframe");
|
|
1199
|
+
if (kind !== "keyframe" && kind !== "clip") {
|
|
1200
|
+
throw new Error('kind must be "keyframe" or "clip".');
|
|
1201
|
+
}
|
|
1202
|
+
const index = Number(args["version"] ?? args["index"]);
|
|
1203
|
+
const versions = versionList(shot, kind);
|
|
1204
|
+
if (!Number.isInteger(index) || index < 0 || index >= versions.length) {
|
|
1205
|
+
throw new Error(`version must be an integer in [0, ${Math.max(versions.length - 1, 0)}]; shot ${shot.id} holds ${versions.length} ${kind} version(s).`);
|
|
1206
|
+
}
|
|
1207
|
+
return { kind, index, versions };
|
|
1208
|
+
}
|
|
1209
|
+
const optionalString = (value) => isString(value) ? value : undefined;
|
|
1210
|
+
/** The shot fields an edit may set, plus the two op-level keys. */
|
|
1211
|
+
const SHOT_EDIT_FIELDS = new Set([
|
|
1212
|
+
"target",
|
|
1213
|
+
"index",
|
|
1214
|
+
"action",
|
|
1215
|
+
"slug",
|
|
1216
|
+
"camera",
|
|
1217
|
+
"motion",
|
|
1218
|
+
"dialogue",
|
|
1219
|
+
"narration",
|
|
1220
|
+
"notes",
|
|
1221
|
+
"duration_seconds",
|
|
1222
|
+
"duration_source",
|
|
1223
|
+
"render_mode",
|
|
1224
|
+
"entity_ids",
|
|
1225
|
+
"location_id",
|
|
1226
|
+
"covered_by",
|
|
1227
|
+
"scene_id",
|
|
1228
|
+
"after_shot_id"
|
|
1229
|
+
]);
|
|
1230
|
+
/** Fields a caller reaches for to attach media, which an edit cannot set. */
|
|
1231
|
+
const SHOT_MEDIA_FIELDS = new Set([
|
|
1232
|
+
"clip",
|
|
1233
|
+
"clip_uri",
|
|
1234
|
+
"clip_asset",
|
|
1235
|
+
"video",
|
|
1236
|
+
"keyframe",
|
|
1237
|
+
"keyframe_uri",
|
|
1238
|
+
"still",
|
|
1239
|
+
"image",
|
|
1240
|
+
"status"
|
|
1241
|
+
]);
|
|
1242
|
+
/**
|
|
1243
|
+
* Refuse a field an edit does not set, instead of dropping it.
|
|
1244
|
+
*
|
|
1245
|
+
* `{op: "update_shot", target, clip: "asset://…"}` came back `applied: 1` and
|
|
1246
|
+
* changed nothing: the write reported success, `has_clip` stayed false, and
|
|
1247
|
+
* the session read that as the board rejecting its asset rather than as the
|
|
1248
|
+
* op ignoring a field it never had. An op that silently drops half its
|
|
1249
|
+
* arguments is indistinguishable from one that worked.
|
|
1250
|
+
*/
|
|
1251
|
+
function assertKnownShotFields(op, args) {
|
|
1252
|
+
const unknown = Object.keys(args).filter((key) => !SHOT_EDIT_FIELDS.has(key));
|
|
1253
|
+
if (unknown.length === 0)
|
|
1254
|
+
return;
|
|
1255
|
+
const media = unknown.filter((key) => SHOT_MEDIA_FIELDS.has(key));
|
|
1256
|
+
throw new Error(`${op} does not take ${unknown.map((k) => `\`${k}\``).join(", ")}. ` +
|
|
1257
|
+
(media.length > 0
|
|
1258
|
+
? "A shot's still and clip are written by render_storyboard_stills / " +
|
|
1259
|
+
"render_storyboard_clips, not by an edit. "
|
|
1260
|
+
: "") +
|
|
1261
|
+
`Accepted: ${[...SHOT_EDIT_FIELDS].join(", ")}.`);
|
|
1262
|
+
}
|
|
1263
|
+
/** The board fields `set_board` may set. */
|
|
1264
|
+
const BOARD_EDIT_FIELDS = new Set([
|
|
1265
|
+
"brief",
|
|
1266
|
+
"style",
|
|
1267
|
+
"aspect_ratio",
|
|
1268
|
+
"entity_ids",
|
|
1269
|
+
"image_model",
|
|
1270
|
+
"video_model"
|
|
1271
|
+
]);
|
|
1272
|
+
/**
|
|
1273
|
+
* The board-level twin of {@link assertKnownShotFields}.
|
|
1274
|
+
*
|
|
1275
|
+
* `set_board` used to keep whatever it was handed and act on the keys it knew,
|
|
1276
|
+
* so `{op: "set_board", image_model: …}` reported success and left the board's
|
|
1277
|
+
* model null — and the render then refused for want of a model the caller had
|
|
1278
|
+
* just set.
|
|
1279
|
+
*/
|
|
1280
|
+
function assertKnownBoardFields(args) {
|
|
1281
|
+
const unknown = Object.keys(args).filter((key) => !BOARD_EDIT_FIELDS.has(key));
|
|
1282
|
+
if (unknown.length === 0)
|
|
1283
|
+
return;
|
|
1284
|
+
throw new Error(`set_board does not take ${unknown.map((k) => `\`${k}\``).join(", ")}. ` +
|
|
1285
|
+
`Accepted: ${[...BOARD_EDIT_FIELDS].join(", ")}.`);
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Read `covered_by` on an edit: which shot's clip holds this shot's picture,
|
|
1289
|
+
* and the window of it this shot uses.
|
|
1290
|
+
*
|
|
1291
|
+
* A model that renders a fixed 5.2s window covers several 1.5-2.2s beats in
|
|
1292
|
+
* one generation, and the clip lands on one shot. Before this the siblings had
|
|
1293
|
+
* no way to say so: they stayed `has_clip: false`, the board read as half
|
|
1294
|
+
* unrendered when the cut was locked, and the default clip selection offered
|
|
1295
|
+
* to generate them again. The covering shot must own its clip — a window
|
|
1296
|
+
* measured against a window has no source length behind it.
|
|
1297
|
+
*/
|
|
1298
|
+
function parseShotCoverage(shot, shots, value) {
|
|
1299
|
+
if (value === null)
|
|
1300
|
+
return null;
|
|
1301
|
+
const raw = isString(value) ? { shot_id: value } : value;
|
|
1302
|
+
if (!isRecord(raw)) {
|
|
1303
|
+
throw new Error("covered_by must be {shot_id, start_seconds?, end_seconds?} or null. " +
|
|
1304
|
+
"A bare shot id is accepted and means the whole clip.");
|
|
1305
|
+
}
|
|
1306
|
+
const ref = raw["shot_id"] ?? raw["target"] ?? raw["shot"];
|
|
1307
|
+
if (!isString(ref) || ref.trim() === "") {
|
|
1308
|
+
throw new Error("covered_by needs a `shot_id` naming the covering shot.");
|
|
1309
|
+
}
|
|
1310
|
+
const cover = findShot([...shots], ref.trim());
|
|
1311
|
+
if (!cover)
|
|
1312
|
+
throw new Error(`covered_by names no shot: "${ref}".`);
|
|
1313
|
+
if (cover.id === shot.id) {
|
|
1314
|
+
throw new Error("A shot cannot cover itself.");
|
|
1315
|
+
}
|
|
1316
|
+
if (cover.covered_by) {
|
|
1317
|
+
throw new Error(`Shot ${cover.id} is itself covered by ${cover.covered_by.shot_id}; ` +
|
|
1318
|
+
"point at the shot that owns the clip.");
|
|
1319
|
+
}
|
|
1320
|
+
const seconds = (key) => {
|
|
1321
|
+
const given = raw[key];
|
|
1322
|
+
if (given === undefined || given === null)
|
|
1323
|
+
return undefined;
|
|
1324
|
+
const n = Number(given);
|
|
1325
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
1326
|
+
throw new Error(`covered_by.${key} must be a number of seconds >= 0.`);
|
|
1327
|
+
}
|
|
1328
|
+
return n;
|
|
1329
|
+
};
|
|
1330
|
+
const start = seconds("start_seconds");
|
|
1331
|
+
const end = seconds("end_seconds");
|
|
1332
|
+
if (start !== undefined && end !== undefined && end <= start) {
|
|
1333
|
+
throw new Error(`covered_by.end_seconds (${end}) must be after start_seconds (${start}).`);
|
|
1334
|
+
}
|
|
1335
|
+
const coverage = { shot_id: cover.id };
|
|
1336
|
+
if (start !== undefined)
|
|
1337
|
+
coverage.start_seconds = start;
|
|
1338
|
+
if (end !== undefined)
|
|
1339
|
+
coverage.end_seconds = end;
|
|
1340
|
+
return coverage;
|
|
1341
|
+
}
|
|
1342
|
+
/** The shot fields an edit may set. Media and status stay the render tools'. */
|
|
1343
|
+
function applyShotFields(shot, args, shots = []) {
|
|
1344
|
+
const next = { ...shot };
|
|
1345
|
+
if (args["action"] !== undefined)
|
|
1346
|
+
next.action = String(args["action"]);
|
|
1347
|
+
if (args["slug"] !== undefined)
|
|
1348
|
+
next.slug = String(args["slug"]);
|
|
1349
|
+
if (args["camera"] !== undefined)
|
|
1350
|
+
next.camera = args["camera"];
|
|
1351
|
+
if (args["motion"] !== undefined)
|
|
1352
|
+
next.motion = String(args["motion"]);
|
|
1353
|
+
if (args["dialogue"] !== undefined)
|
|
1354
|
+
next.dialogue = String(args["dialogue"]);
|
|
1355
|
+
if (args["narration"] !== undefined)
|
|
1356
|
+
next.narration = String(args["narration"]);
|
|
1357
|
+
if (args["notes"] !== undefined)
|
|
1358
|
+
next.notes = String(args["notes"]);
|
|
1359
|
+
if (args["duration_seconds"] !== undefined) {
|
|
1360
|
+
const seconds = Number(args["duration_seconds"]);
|
|
1361
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
1362
|
+
throw new Error("duration_seconds must be a positive number.");
|
|
1363
|
+
}
|
|
1364
|
+
next.duration_seconds = seconds;
|
|
1365
|
+
}
|
|
1366
|
+
if (args["duration_source"] !== undefined) {
|
|
1367
|
+
const source = String(args["duration_source"]);
|
|
1368
|
+
if (source !== "audio" && source !== "manual") {
|
|
1369
|
+
throw new Error('duration_source must be "audio" or "manual".');
|
|
1370
|
+
}
|
|
1371
|
+
next.duration_source = source;
|
|
1372
|
+
}
|
|
1373
|
+
if (args["render_mode"] !== undefined) {
|
|
1374
|
+
const mode = String(args["render_mode"]);
|
|
1375
|
+
if (mode !== "keyframe" && mode !== "direct" && mode !== "reference") {
|
|
1376
|
+
throw new Error('render_mode must be "keyframe", "direct", or "reference".');
|
|
1377
|
+
}
|
|
1378
|
+
next.render_mode = mode;
|
|
1379
|
+
}
|
|
1380
|
+
if (Array.isArray(args["entity_ids"])) {
|
|
1381
|
+
next.entity_ids = args["entity_ids"].map(String);
|
|
1382
|
+
}
|
|
1383
|
+
if (args["location_id"] !== undefined) {
|
|
1384
|
+
next.location_id = optionalString(args["location_id"]) ?? null;
|
|
1385
|
+
}
|
|
1386
|
+
if (args["scene_id"] !== undefined) {
|
|
1387
|
+
const sceneId = optionalString(args["scene_id"]);
|
|
1388
|
+
if (sceneId) {
|
|
1389
|
+
next.scene_id = sceneId;
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
delete next.scene_id;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (args["covered_by"] !== undefined) {
|
|
1396
|
+
const coverage = parseShotCoverage(next, shots, args["covered_by"]);
|
|
1397
|
+
next.covered_by = coverage;
|
|
1398
|
+
// Status is the render tools' to write, with one exception: coverage is
|
|
1399
|
+
// the only way a shot's picture arrives without a render, and a covered
|
|
1400
|
+
// shot left at `keyframe_ready` is the drift this field exists to remove.
|
|
1401
|
+
if (coverage) {
|
|
1402
|
+
next.status = "rendered";
|
|
1403
|
+
}
|
|
1404
|
+
else if (!next.clip) {
|
|
1405
|
+
next.status = next.keyframe ? "keyframe_ready" : "planned";
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return next;
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* Apply one board operation. Returns its summary, or throws with the reason.
|
|
1412
|
+
*
|
|
1413
|
+
* `entities` is the resolved library `set_style` reads — the document holds
|
|
1414
|
+
* ids, and the kinds and descriptors live in the asset rows the caller loaded.
|
|
1415
|
+
*/
|
|
1416
|
+
function applyBoardOp(doc, { op, args }, entities = []) {
|
|
1417
|
+
switch (op) {
|
|
1418
|
+
case "add_shot": {
|
|
1419
|
+
assertKnownShotFields("add_shot", args);
|
|
1420
|
+
if (!isString(args["action"]) || args["action"].trim() === "") {
|
|
1421
|
+
throw new Error("add_shot needs a non-empty `action` describing the shot.");
|
|
1422
|
+
}
|
|
1423
|
+
const shot = applyShotFields({
|
|
1424
|
+
type: "shot",
|
|
1425
|
+
id: newDocId("shot"),
|
|
1426
|
+
index: doc.shots.length,
|
|
1427
|
+
action: "",
|
|
1428
|
+
status: "planned"
|
|
1429
|
+
}, args, doc.shots);
|
|
1430
|
+
// An explicit anchor is the scene-safe insert: the new shot lands in
|
|
1431
|
+
// the anchor's scene, which a bare index cannot say.
|
|
1432
|
+
const anchorRef = optionalString(args["after_shot_id"]);
|
|
1433
|
+
if (anchorRef !== undefined) {
|
|
1434
|
+
const anchor = findShot(doc.shots, anchorRef);
|
|
1435
|
+
if (!anchor)
|
|
1436
|
+
throw new Error(`after_shot_id names no shot: "${anchorRef}".`);
|
|
1437
|
+
const ordered = [...doc.shots].sort((a, b) => a.index - b.index);
|
|
1438
|
+
const at = ordered.findIndex((s) => s.id === anchor.id) + 1;
|
|
1439
|
+
ordered.splice(at, 0, withScene(shot, anchor.scene_id ?? null));
|
|
1440
|
+
applyStructural(doc, ordered);
|
|
1441
|
+
return { id: shot.id, index: at };
|
|
1442
|
+
}
|
|
1443
|
+
const at = isNumber(args["index"])
|
|
1444
|
+
? Math.max(0, Math.min(Math.trunc(args["index"]), doc.shots.length))
|
|
1445
|
+
: doc.shots.length;
|
|
1446
|
+
const shots = [...doc.shots];
|
|
1447
|
+
shots.splice(at, 0, shot);
|
|
1448
|
+
applyStructural(doc, shots);
|
|
1449
|
+
return { id: shot.id, index: at };
|
|
1450
|
+
}
|
|
1451
|
+
case "update_shot": {
|
|
1452
|
+
assertKnownShotFields("update_shot", args);
|
|
1453
|
+
const target = String(args["target"] ?? "");
|
|
1454
|
+
const shot = findShot(doc.shots, target);
|
|
1455
|
+
if (!shot)
|
|
1456
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1457
|
+
const updated = applyShotFields(shot, args, doc.shots);
|
|
1458
|
+
doc.shots = doc.shots.map((s) => (s.id === shot.id ? updated : s));
|
|
1459
|
+
return { id: updated.id, action: updated.action };
|
|
1460
|
+
}
|
|
1461
|
+
case "remove_shot": {
|
|
1462
|
+
const target = String(args["target"] ?? "");
|
|
1463
|
+
const shot = findShot(doc.shots, target);
|
|
1464
|
+
if (!shot)
|
|
1465
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1466
|
+
// Coverage pointing at a shot that is gone would read as a picture that
|
|
1467
|
+
// exists and assemble as nothing, so the dependents go back to needing
|
|
1468
|
+
// their own render.
|
|
1469
|
+
const uncovered = doc.shots
|
|
1470
|
+
.filter((s) => s.covered_by?.shot_id === shot.id)
|
|
1471
|
+
.map((s) => s.id);
|
|
1472
|
+
doc.shots = renumberShots(doc.shots
|
|
1473
|
+
.filter((s) => s.id !== shot.id)
|
|
1474
|
+
.map((s) => s.covered_by?.shot_id === shot.id
|
|
1475
|
+
? {
|
|
1476
|
+
...s,
|
|
1477
|
+
covered_by: null,
|
|
1478
|
+
status: s.keyframe ? "keyframe_ready" : "planned"
|
|
1479
|
+
}
|
|
1480
|
+
: s));
|
|
1481
|
+
return uncovered.length > 0
|
|
1482
|
+
? { removed: shot.id, uncovered }
|
|
1483
|
+
: { removed: shot.id };
|
|
1484
|
+
}
|
|
1485
|
+
case "reorder_shot": {
|
|
1486
|
+
const target = String(args["target"] ?? "");
|
|
1487
|
+
const shot = findShot(doc.shots, target);
|
|
1488
|
+
if (!shot)
|
|
1489
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1490
|
+
const to = Number(args["index"]);
|
|
1491
|
+
if (!Number.isInteger(to) || to < 0 || to >= doc.shots.length) {
|
|
1492
|
+
throw new Error(`reorder_shot needs an \`index\` in [0, ${doc.shots.length - 1}].`);
|
|
1493
|
+
}
|
|
1494
|
+
const ordered = [...doc.shots].sort((a, b) => a.index - b.index);
|
|
1495
|
+
const from = ordered.findIndex((s) => s.id === shot.id);
|
|
1496
|
+
const [moved] = ordered.splice(from, 1);
|
|
1497
|
+
ordered.splice(to, 0, moved);
|
|
1498
|
+
doc.shots = renumberShots(ordered);
|
|
1499
|
+
return { id: shot.id, index: to };
|
|
1500
|
+
}
|
|
1501
|
+
case "move_shot": {
|
|
1502
|
+
const target = String(args["target"] ?? "");
|
|
1503
|
+
const shot = findShot(doc.shots, target);
|
|
1504
|
+
if (!shot)
|
|
1505
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1506
|
+
const seeded = materializeLegacyScene(doc);
|
|
1507
|
+
const moved = doc.shots.find((s) => s.id === shot.id);
|
|
1508
|
+
const sceneGiven = args["scene_id"] !== undefined;
|
|
1509
|
+
const sceneId = sceneGiven
|
|
1510
|
+
? (optionalString(args["scene_id"]) ?? null)
|
|
1511
|
+
: (moved.scene_id ?? seeded ?? null);
|
|
1512
|
+
if (sceneId !== null && !docScenes(doc).some((s) => s.id === sceneId)) {
|
|
1513
|
+
throw new Error(`move_shot names no scene on this board: "${sceneId}".`);
|
|
1514
|
+
}
|
|
1515
|
+
const position = Number(args["position"] ?? args["index"]);
|
|
1516
|
+
if (!Number.isInteger(position) || position < 0) {
|
|
1517
|
+
throw new Error("move_shot needs a `position`: the 0-based place inside the target scene.");
|
|
1518
|
+
}
|
|
1519
|
+
const ordered = sceneGroups(doc.shots).flatMap((g) => g.shots);
|
|
1520
|
+
const from = ordered.findIndex((s) => s.id === moved.id);
|
|
1521
|
+
const rest = ordered.filter((s) => s.id !== moved.id);
|
|
1522
|
+
const run = [];
|
|
1523
|
+
rest.forEach((s, i) => {
|
|
1524
|
+
if ((s.scene_id ?? null) === sceneId)
|
|
1525
|
+
run.push(i);
|
|
1526
|
+
});
|
|
1527
|
+
// A target scene the move empties has no run to count from, so the shot
|
|
1528
|
+
// holds its place and only changes scene.
|
|
1529
|
+
const at = run.length > 0
|
|
1530
|
+
? run[0] + Math.min(position, run.length)
|
|
1531
|
+
: Math.min(Math.max(from, 0), rest.length);
|
|
1532
|
+
applyStructural(doc, [
|
|
1533
|
+
...rest.slice(0, at),
|
|
1534
|
+
withScene(moved, sceneId),
|
|
1535
|
+
...rest.slice(at)
|
|
1536
|
+
]);
|
|
1537
|
+
const landed = doc.shots.find((s) => s.id === moved.id);
|
|
1538
|
+
return { id: landed.id, index: landed.index, scene_id: sceneId };
|
|
1539
|
+
}
|
|
1540
|
+
case "duplicate_shot": {
|
|
1541
|
+
const target = String(args["target"] ?? "");
|
|
1542
|
+
const shot = findShot(doc.shots, target);
|
|
1543
|
+
if (!shot)
|
|
1544
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1545
|
+
const ordered = [...doc.shots].sort((a, b) => a.index - b.index);
|
|
1546
|
+
const at = ordered.findIndex((s) => s.id === shot.id);
|
|
1547
|
+
const copy = {
|
|
1548
|
+
...shot,
|
|
1549
|
+
id: newDocId("shot"),
|
|
1550
|
+
// The copy covers no script line, so the link fields go with the
|
|
1551
|
+
// original and an ERT read off a line is now the user's own.
|
|
1552
|
+
duration_source: "manual"
|
|
1553
|
+
};
|
|
1554
|
+
delete copy.script_line_ids;
|
|
1555
|
+
delete copy.script_text_snapshot;
|
|
1556
|
+
delete copy.covered_by;
|
|
1557
|
+
ordered.splice(at + 1, 0, copy);
|
|
1558
|
+
applyStructural(doc, ordered);
|
|
1559
|
+
return { id: copy.id, index: at + 1, source: shot.id };
|
|
1560
|
+
}
|
|
1561
|
+
case "set_setup": {
|
|
1562
|
+
const unknown = Object.keys(args).filter((key) => !["brief", "genre", "stage"].includes(key));
|
|
1563
|
+
if (unknown.length > 0) {
|
|
1564
|
+
throw new Error(`set_setup does not take ${unknown.map((k) => `\`${k}\``).join(", ")}. Accepted: brief, genre, stage.`);
|
|
1565
|
+
}
|
|
1566
|
+
if (args["brief"] !== undefined)
|
|
1567
|
+
doc.brief = String(args["brief"]);
|
|
1568
|
+
if (args["genre"] !== undefined)
|
|
1569
|
+
doc.genre = String(args["genre"]);
|
|
1570
|
+
if (args["stage"] !== undefined) {
|
|
1571
|
+
const stage = String(args["stage"]);
|
|
1572
|
+
if (!SETUP_STAGES.includes(stage)) {
|
|
1573
|
+
throw new Error(`stage must be one of ${SETUP_STAGES.join(", ")}; got "${stage}".`);
|
|
1574
|
+
}
|
|
1575
|
+
doc.setupStage = stage;
|
|
1576
|
+
}
|
|
1577
|
+
return { brief: doc.brief, genre: doc.genre, stage: doc.setupStage };
|
|
1578
|
+
}
|
|
1579
|
+
case "update_scene": {
|
|
1580
|
+
const sceneId = String(args["scene_id"] ?? "");
|
|
1581
|
+
const scenes = docScenes(doc);
|
|
1582
|
+
const target = scenes.find((scene) => scene.id === sceneId);
|
|
1583
|
+
if (!target) {
|
|
1584
|
+
throw new Error(`No scene matches "${sceneId}".`);
|
|
1585
|
+
}
|
|
1586
|
+
const next = { ...target };
|
|
1587
|
+
if (args["slugline"] !== undefined)
|
|
1588
|
+
next.slugline = String(args["slugline"]);
|
|
1589
|
+
if (args["lighting"] !== undefined)
|
|
1590
|
+
next.lighting = String(args["lighting"]);
|
|
1591
|
+
setDocScenes(doc, scenes.map((scene) => (scene.id === sceneId ? next : scene)));
|
|
1592
|
+
return { id: next.id, slugline: next.slugline, lighting: next.lighting };
|
|
1593
|
+
}
|
|
1594
|
+
case "create_scene": {
|
|
1595
|
+
materializeLegacyScene(doc);
|
|
1596
|
+
const scene = {
|
|
1597
|
+
type: "scene",
|
|
1598
|
+
id: newDocId("scene"),
|
|
1599
|
+
slugline: optionalString(args["slugline"]) ?? ""
|
|
1600
|
+
};
|
|
1601
|
+
const groups = sceneGroups(doc.shots);
|
|
1602
|
+
const afterRef = optionalString(args["after_scene_id"]);
|
|
1603
|
+
const after = afterRef
|
|
1604
|
+
? groups.findIndex((g) => g.sceneId === afterRef)
|
|
1605
|
+
: -1;
|
|
1606
|
+
if (afterRef && after === -1) {
|
|
1607
|
+
throw new Error(`after_scene_id names no scene: "${afterRef}".`);
|
|
1608
|
+
}
|
|
1609
|
+
const ordered = groups.flatMap((g) => g.shots);
|
|
1610
|
+
// Right after the last shot of `after_scene_id`, so the new scene lands
|
|
1611
|
+
// in the position its first shot's index gives it.
|
|
1612
|
+
const at = after === -1
|
|
1613
|
+
? ordered.length
|
|
1614
|
+
: groups.slice(0, after + 1).reduce((n, g) => n + g.shots.length, 0);
|
|
1615
|
+
// A scene with no shot has no position, so it would neither render nor
|
|
1616
|
+
// survive the next operation: it opens holding one blank shot.
|
|
1617
|
+
const shotId = newDocId("shot");
|
|
1618
|
+
ordered.splice(at, 0, blankShot(shotId, scene.id));
|
|
1619
|
+
setDocScenes(doc, [...docScenes(doc), scene]);
|
|
1620
|
+
applyStructural(doc, ordered);
|
|
1621
|
+
return { id: scene.id, shot_id: shotId };
|
|
1622
|
+
}
|
|
1623
|
+
case "merge_scene": {
|
|
1624
|
+
const sceneId = String(args["scene_id"] ?? "");
|
|
1625
|
+
const groups = sceneGroups(doc.shots);
|
|
1626
|
+
const at = groups.findIndex((g) => g.sceneId === sceneId);
|
|
1627
|
+
if (at === -1)
|
|
1628
|
+
throw new Error(`No scene matches "${sceneId}".`);
|
|
1629
|
+
if (at === 0) {
|
|
1630
|
+
throw new Error(`Scene ${sceneId} is the first scene; there is nothing before it to merge into.`);
|
|
1631
|
+
}
|
|
1632
|
+
const into = groups[at - 1].sceneId;
|
|
1633
|
+
// The emptied scene is dropped by the reindex that follows.
|
|
1634
|
+
applyStructural(doc, groups.flatMap((g) => g === groups[at] ? g.shots.map((s) => withScene(s, into)) : g.shots));
|
|
1635
|
+
return { merged: sceneId, into };
|
|
1636
|
+
}
|
|
1637
|
+
case "set_style": {
|
|
1638
|
+
const entityId = optionalString(args["entity_id"]);
|
|
1639
|
+
if (entityId) {
|
|
1640
|
+
const chosen = entities.find((e) => e.id === entityId);
|
|
1641
|
+
if (!chosen) {
|
|
1642
|
+
throw new Error(`No entity "${entityId}" is in the library. Call list_entities, or pass \`style\` instead.`);
|
|
1643
|
+
}
|
|
1644
|
+
if (chosen.kind !== "style") {
|
|
1645
|
+
throw new Error(`Entity "${entityId}" is a ${chosen.kind}, not a style. Cast it with set_board's entity_ids.`);
|
|
1646
|
+
}
|
|
1647
|
+
const styleIds = new Set(entities.filter((e) => e.kind === "style").map((e) => e.id));
|
|
1648
|
+
doc.style = chosen.descriptor;
|
|
1649
|
+
doc.entityIds = [
|
|
1650
|
+
...(doc.entityIds ?? []).filter((id) => !styleIds.has(id)),
|
|
1651
|
+
entityId
|
|
1652
|
+
];
|
|
1653
|
+
// A shot's explicit list is its whole selection, so a style missing
|
|
1654
|
+
// from it reads as an exclusion. Styles are board-wide.
|
|
1655
|
+
doc.shots = doc.shots.map((shot) => {
|
|
1656
|
+
if (!shot.entity_ids)
|
|
1657
|
+
return shot;
|
|
1658
|
+
return {
|
|
1659
|
+
...shot,
|
|
1660
|
+
entity_ids: [
|
|
1661
|
+
...shot.entity_ids.filter((id) => !styleIds.has(id)),
|
|
1662
|
+
entityId
|
|
1663
|
+
]
|
|
1664
|
+
};
|
|
1665
|
+
});
|
|
1666
|
+
return {
|
|
1667
|
+
style: doc.style,
|
|
1668
|
+
style_entity_id: entityId,
|
|
1669
|
+
entity_ids: doc.entityIds
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
const descriptor = optionalString(args["style"]) ?? optionalString(args["descriptor"]);
|
|
1673
|
+
if (descriptor === undefined) {
|
|
1674
|
+
throw new Error("set_style needs an `entity_id` or a `style` descriptor.");
|
|
1675
|
+
}
|
|
1676
|
+
doc.style = descriptor;
|
|
1677
|
+
return { style: doc.style, style_entity_id: null };
|
|
1678
|
+
}
|
|
1679
|
+
case "select_version": {
|
|
1680
|
+
const target = String(args["target"] ?? "");
|
|
1681
|
+
const shot = findShot(doc.shots, target);
|
|
1682
|
+
if (!shot)
|
|
1683
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1684
|
+
const { kind, index, versions } = readVersionTarget(shot, args);
|
|
1685
|
+
const chosen = versions[index];
|
|
1686
|
+
const updated = kind === "keyframe"
|
|
1687
|
+
? {
|
|
1688
|
+
...shot,
|
|
1689
|
+
keyframe: chosen,
|
|
1690
|
+
keyframe_versions: versions
|
|
1691
|
+
}
|
|
1692
|
+
: {
|
|
1693
|
+
...shot,
|
|
1694
|
+
clip: chosen,
|
|
1695
|
+
clip_versions: versions
|
|
1696
|
+
};
|
|
1697
|
+
doc.shots = doc.shots.map((s) => (s.id === shot.id ? updated : s));
|
|
1698
|
+
return { id: shot.id, kind, version: index };
|
|
1699
|
+
}
|
|
1700
|
+
case "delete_version": {
|
|
1701
|
+
const target = String(args["target"] ?? "");
|
|
1702
|
+
const shot = findShot(doc.shots, target);
|
|
1703
|
+
if (!shot)
|
|
1704
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1705
|
+
const { kind, index, versions } = readVersionTarget(shot, args);
|
|
1706
|
+
const remaining = versions.filter((_, i) => i !== index);
|
|
1707
|
+
const selected = kind === "keyframe" ? shot.keyframe : shot.clip;
|
|
1708
|
+
const refId = (v) => v?.asset_id ?? v?.uri ?? "";
|
|
1709
|
+
const removedSelected = !!selected && refId(versions[index]) === refId(selected);
|
|
1710
|
+
// The next version at the same position becomes selected, or the last
|
|
1711
|
+
// one when the removed version was at the end.
|
|
1712
|
+
const next = remaining.length === 0
|
|
1713
|
+
? null
|
|
1714
|
+
: removedSelected
|
|
1715
|
+
? remaining[Math.min(index, remaining.length - 1)]
|
|
1716
|
+
: (selected ?? null);
|
|
1717
|
+
const updated = { ...shot };
|
|
1718
|
+
if (kind === "keyframe") {
|
|
1719
|
+
updated.keyframe = next;
|
|
1720
|
+
updated.keyframe_versions = remaining;
|
|
1721
|
+
if (remaining.length === 0 && !updated.clip)
|
|
1722
|
+
updated.status = "planned";
|
|
1723
|
+
}
|
|
1724
|
+
else {
|
|
1725
|
+
updated.clip = next;
|
|
1726
|
+
updated.clip_versions = remaining;
|
|
1727
|
+
if (remaining.length === 0) {
|
|
1728
|
+
updated.status = updated.keyframe ? "keyframe_ready" : "planned";
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
doc.shots = doc.shots.map((s) => (s.id === shot.id ? updated : s));
|
|
1732
|
+
return { id: shot.id, kind, removed: index, remaining: remaining.length };
|
|
1733
|
+
}
|
|
1734
|
+
case "add_keyframe_version": {
|
|
1735
|
+
const target = String(args["target"] ?? "");
|
|
1736
|
+
const shot = findShot(doc.shots, target);
|
|
1737
|
+
if (!shot)
|
|
1738
|
+
throw new Error(`No shot matches "${target}".`);
|
|
1739
|
+
const assetId = optionalString(args["asset_id"]);
|
|
1740
|
+
if (!assetId) {
|
|
1741
|
+
throw new Error("add_keyframe_version needs an `asset_id` naming a stored image.");
|
|
1742
|
+
}
|
|
1743
|
+
const keyframe = {
|
|
1744
|
+
type: "image",
|
|
1745
|
+
asset_id: assetId,
|
|
1746
|
+
uri: `asset://${assetId}`
|
|
1747
|
+
};
|
|
1748
|
+
const flipOf = optionalString(args["flip_of"]);
|
|
1749
|
+
if (flipOf) {
|
|
1750
|
+
// Provenance for a flip or an editor pass. It carries no render
|
|
1751
|
+
// record, so the still never reads stale.
|
|
1752
|
+
keyframe.flip_of = flipOf;
|
|
1753
|
+
}
|
|
1754
|
+
const versions = versionList(shot, "keyframe");
|
|
1755
|
+
const updated = {
|
|
1756
|
+
...shot,
|
|
1757
|
+
keyframe,
|
|
1758
|
+
keyframe_versions: [...versions, keyframe],
|
|
1759
|
+
status: shot.status === "planned" ? "keyframe_ready" : shot.status
|
|
1760
|
+
};
|
|
1761
|
+
doc.shots = doc.shots.map((s) => (s.id === shot.id ? updated : s));
|
|
1762
|
+
return { id: shot.id, asset_id: assetId, versions: versions.length + 1 };
|
|
1763
|
+
}
|
|
1764
|
+
case "set_board": {
|
|
1765
|
+
assertKnownBoardFields(args);
|
|
1766
|
+
if (args["brief"] !== undefined)
|
|
1767
|
+
doc.brief = String(args["brief"]);
|
|
1768
|
+
if (args["style"] !== undefined)
|
|
1769
|
+
doc.style = String(args["style"]);
|
|
1770
|
+
if (args["aspect_ratio"] !== undefined) {
|
|
1771
|
+
doc.aspectRatio = String(args["aspect_ratio"]);
|
|
1772
|
+
}
|
|
1773
|
+
if (Array.isArray(args["entity_ids"])) {
|
|
1774
|
+
doc.entityIds = args["entity_ids"].map(String);
|
|
1775
|
+
}
|
|
1776
|
+
// The board's default models. Without these the only way to set one was
|
|
1777
|
+
// the editor, so a headless caller passed them here, saw the op succeed,
|
|
1778
|
+
// and then had `render_storyboard_stills` refuse for want of a model —
|
|
1779
|
+
// the keys were being dropped silently.
|
|
1780
|
+
const model = (key) => {
|
|
1781
|
+
const value = args[key];
|
|
1782
|
+
if (value === null)
|
|
1783
|
+
return null;
|
|
1784
|
+
return isRecord(value) ? value : undefined;
|
|
1785
|
+
};
|
|
1786
|
+
const imageModel = model("image_model");
|
|
1787
|
+
if (args["image_model"] !== undefined) {
|
|
1788
|
+
if (imageModel === undefined) {
|
|
1789
|
+
throw new Error("set_board: image_model must be a model object (use find_model) or null");
|
|
1790
|
+
}
|
|
1791
|
+
doc.imageModel = imageModel;
|
|
1792
|
+
}
|
|
1793
|
+
const videoModel = model("video_model");
|
|
1794
|
+
if (args["video_model"] !== undefined) {
|
|
1795
|
+
if (videoModel === undefined) {
|
|
1796
|
+
throw new Error("set_board: video_model must be a model object (use find_model) or null");
|
|
1797
|
+
}
|
|
1798
|
+
doc.videoModel = videoModel;
|
|
1799
|
+
}
|
|
1800
|
+
return {
|
|
1801
|
+
brief: doc.brief,
|
|
1802
|
+
style: doc.style,
|
|
1803
|
+
aspect_ratio: doc.aspectRatio,
|
|
1804
|
+
entity_ids: doc.entityIds,
|
|
1805
|
+
image_model: doc.imageModel,
|
|
1806
|
+
video_model: doc.videoModel
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* The library `set_style` reads: the board's own cast, plus every entity a
|
|
1813
|
+
* `set_style` op names. The named one need not be cast yet — applying a preset
|
|
1814
|
+
* is how it gets cast.
|
|
1815
|
+
*/
|
|
1816
|
+
async function loadStyleEntities(run, doc, ops) {
|
|
1817
|
+
const named = ops
|
|
1818
|
+
.filter((parsed) => parsed.op === "set_style")
|
|
1819
|
+
.map((parsed) => optionalString(parsed.args["entity_id"]))
|
|
1820
|
+
.filter((id) => !!id);
|
|
1821
|
+
if (named.length === 0)
|
|
1822
|
+
return [];
|
|
1823
|
+
const cast = await loadBoardEntities(run.context, doc);
|
|
1824
|
+
const missing = named.filter((id) => !cast.some((e) => e.id === id));
|
|
1825
|
+
if (missing.length === 0)
|
|
1826
|
+
return cast;
|
|
1827
|
+
const extra = await loadBoardEntities(run.context, {
|
|
1828
|
+
...doc,
|
|
1829
|
+
entityIds: missing
|
|
1830
|
+
});
|
|
1831
|
+
return [...cast, ...extra];
|
|
1832
|
+
}
|
|
1833
|
+
const editStoryboard = {
|
|
1834
|
+
spec: editStoryboardSpec,
|
|
1835
|
+
impl: async (run, params) => {
|
|
1836
|
+
const ops = parseBoardOps(params["ops"]);
|
|
1837
|
+
if (isError(ops))
|
|
1838
|
+
return ops;
|
|
1839
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
1840
|
+
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
1841
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
1842
|
+
if (isError(board))
|
|
1843
|
+
return board;
|
|
1844
|
+
const { row, doc } = board;
|
|
1845
|
+
// A failing op is recorded and the script continues: stopping at the
|
|
1846
|
+
// first error hides every problem behind it.
|
|
1847
|
+
const records = [];
|
|
1848
|
+
const entities = await loadStyleEntities(run, doc, ops);
|
|
1849
|
+
const resolvedOps = [];
|
|
1850
|
+
for (const parsed of ops) {
|
|
1851
|
+
try {
|
|
1852
|
+
const result = applyBoardOp(doc, parsed, entities);
|
|
1853
|
+
records.push({
|
|
1854
|
+
op: parsed.op,
|
|
1855
|
+
ok: true,
|
|
1856
|
+
result
|
|
1857
|
+
});
|
|
1858
|
+
// Resolve non-id targets (index/slug) to the canonical shot id so
|
|
1859
|
+
// the merge adapter can attribute the write to the real unit.
|
|
1860
|
+
const canonicalId = typeof result?.["id"] === "string"
|
|
1861
|
+
? result["id"]
|
|
1862
|
+
: typeof result?.["removed"] === "string"
|
|
1863
|
+
? result["removed"]
|
|
1864
|
+
: undefined;
|
|
1865
|
+
if (canonicalId &&
|
|
1866
|
+
(parsed.op === "update_shot" ||
|
|
1867
|
+
parsed.op === "remove_shot" ||
|
|
1868
|
+
parsed.op === "reorder_shot" ||
|
|
1869
|
+
parsed.op === "move_shot" ||
|
|
1870
|
+
parsed.op === "select_version" ||
|
|
1871
|
+
parsed.op === "delete_version" ||
|
|
1872
|
+
parsed.op === "add_keyframe_version")) {
|
|
1873
|
+
resolvedOps.push({
|
|
1874
|
+
tool: parsed.op,
|
|
1875
|
+
input: { ...parsed.args, target: canonicalId, id: canonicalId }
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
else {
|
|
1879
|
+
resolvedOps.push({ tool: parsed.op, input: parsed.args });
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
catch (e) {
|
|
1883
|
+
records.push({
|
|
1884
|
+
op: parsed.op,
|
|
1885
|
+
ok: false,
|
|
1886
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1887
|
+
});
|
|
1888
|
+
resolvedOps.push({ tool: parsed.op, input: parsed.args });
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
const saved = await Storyboard.updateFieldsIfUnchanged(row.id, row.updated_at, {
|
|
1892
|
+
document: JSON.stringify(doc)
|
|
1893
|
+
},
|
|
1894
|
+
// The ops ride on the write so an open editor can merge this change
|
|
1895
|
+
// per shot instead of treating the board as replaced.
|
|
1896
|
+
{ ops: resolvedOps });
|
|
1897
|
+
if (!saved)
|
|
1898
|
+
continue;
|
|
1899
|
+
const failed = records.filter((record) => !record.ok);
|
|
1900
|
+
return {
|
|
1901
|
+
storyboard_id: row.id,
|
|
1902
|
+
updated_at: saved.updated_at,
|
|
1903
|
+
applied: records.length - failed.length,
|
|
1904
|
+
failed: failed.length,
|
|
1905
|
+
ops: records,
|
|
1906
|
+
shots: [...doc.shots]
|
|
1907
|
+
.sort((a, b) => a.index - b.index)
|
|
1908
|
+
.map((shot) => ({
|
|
1909
|
+
id: shot.id,
|
|
1910
|
+
index: shot.index,
|
|
1911
|
+
slug: shot.slug,
|
|
1912
|
+
action: shot.action,
|
|
1913
|
+
status: shot.status
|
|
1914
|
+
}))
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
return {
|
|
1918
|
+
error: `Storyboard ${String(params["storyboard_id"])} is being modified concurrently; nothing was saved. Retry the call.`
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
// ---------------------------------------------------------------------------
|
|
1923
|
+
// direct_storyboard
|
|
1924
|
+
// ---------------------------------------------------------------------------
|
|
1925
|
+
const DEFAULT_DIRECTED_SHOTS = 6;
|
|
1926
|
+
/**
|
|
1927
|
+
* The Director, headless. The browser runs the same three protocol functions
|
|
1928
|
+
* from `useDirectScreenplay`; keeping the prompt, the schema and the parse in
|
|
1929
|
+
* `@nodetool-ai/protocol` is what makes a board directed here and one directed
|
|
1930
|
+
* in the editor the same artifact.
|
|
1931
|
+
*/
|
|
1932
|
+
const directStoryboard = {
|
|
1933
|
+
spec: directStoryboardSpec,
|
|
1934
|
+
impl: async (run, params) => {
|
|
1935
|
+
const context = run.context;
|
|
1936
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
1937
|
+
if (isError(board))
|
|
1938
|
+
return board;
|
|
1939
|
+
const { row, doc } = board;
|
|
1940
|
+
const brief = doc.brief.trim();
|
|
1941
|
+
if (!brief) {
|
|
1942
|
+
return {
|
|
1943
|
+
error: `Storyboard ${row.id} has no brief, so there is nothing to direct. Write one with edit_storyboard's set_setup op.`
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
if (doc.shots.length > 0 && params["redirect"] !== true) {
|
|
1947
|
+
return {
|
|
1948
|
+
error: `Storyboard ${row.id} already has ${doc.shots.length} shots. Pass redirect: true to run the Director over them again — retained shots keep their ids and their rendered media.`
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1951
|
+
const model = resolveModel(params, doc.directorModel, "director", "generate_text");
|
|
1952
|
+
if (isError(model))
|
|
1953
|
+
return model;
|
|
1954
|
+
const { DIRECTOR_SYSTEM_PROMPT, SCREENPLAY_TOOL_DESCRIPTION, SCREENPLAY_TOOL_NAME, buildDirectorPrompt, buildScreenplaySchema, clampShotCount, fallbackScreenplay, parseScreenplay } = await import("@nodetool-ai/protocol");
|
|
1955
|
+
const { generateStructured } = await import("@nodetool-ai/runtime");
|
|
1956
|
+
const shotCount = clampShotCount(isNumber(params["shot_count"])
|
|
1957
|
+
? params["shot_count"]
|
|
1958
|
+
: DEFAULT_DIRECTED_SHOTS);
|
|
1959
|
+
const aspectRatio = doc.aspectRatio || "16:9";
|
|
1960
|
+
// The cast reaches the model so the screenplay names entities by their
|
|
1961
|
+
// exact names, which is what activates them per shot.
|
|
1962
|
+
const entities = await loadBoardEntities(context, doc);
|
|
1963
|
+
const castLines = entities.map((e) => `- ${e.name} (${e.kind})${e.descriptor ? `: ${e.descriptor}` : ""}`);
|
|
1964
|
+
const genre = doc.genre.trim();
|
|
1965
|
+
const directedBrief = [
|
|
1966
|
+
brief,
|
|
1967
|
+
genre ? `Genre: ${genre}` : "",
|
|
1968
|
+
castLines.length > 0
|
|
1969
|
+
? `Cast & ingredients — reference these by exact name in the shots:\n${castLines.join("\n")}`
|
|
1970
|
+
: ""
|
|
1971
|
+
]
|
|
1972
|
+
.filter((part) => part !== "")
|
|
1973
|
+
.join("\n\n");
|
|
1974
|
+
const provider = await context.getProvider(model.provider);
|
|
1975
|
+
const raw = await generateStructured(provider, {
|
|
1976
|
+
model: model.model,
|
|
1977
|
+
maxTokens: 8192,
|
|
1978
|
+
messages: [
|
|
1979
|
+
{ role: "system", content: DIRECTOR_SYSTEM_PROMPT },
|
|
1980
|
+
{
|
|
1981
|
+
role: "user",
|
|
1982
|
+
content: buildDirectorPrompt(directedBrief, doc.style, shotCount, aspectRatio)
|
|
1983
|
+
}
|
|
1984
|
+
],
|
|
1985
|
+
toolName: SCREENPLAY_TOOL_NAME,
|
|
1986
|
+
toolDescription: SCREENPLAY_TOOL_DESCRIPTION,
|
|
1987
|
+
schema: buildScreenplaySchema(shotCount)
|
|
1988
|
+
});
|
|
1989
|
+
const parsed = raw
|
|
1990
|
+
? parseScreenplay(raw, {
|
|
1991
|
+
shotCount,
|
|
1992
|
+
aspectRatio,
|
|
1993
|
+
genre: genre || undefined
|
|
1994
|
+
})
|
|
1995
|
+
: null;
|
|
1996
|
+
// No usable answer — a provider without tool support, or the fake provider
|
|
1997
|
+
// — falls back to placeholder shots derived from the brief, the same rule
|
|
1998
|
+
// the Director node and the editor apply. A provider error still throws.
|
|
1999
|
+
const screenplay = parsed && parsed.shots.length > 0
|
|
2000
|
+
? parsed
|
|
2001
|
+
: fallbackScreenplay({
|
|
2002
|
+
brief,
|
|
2003
|
+
style: doc.style,
|
|
2004
|
+
shotCount,
|
|
2005
|
+
aspectRatio
|
|
2006
|
+
});
|
|
2007
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
2008
|
+
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
2009
|
+
const current = await loadBoard(run, row.id);
|
|
2010
|
+
if (isError(current))
|
|
2011
|
+
return current;
|
|
2012
|
+
const next = current.doc;
|
|
2013
|
+
// A screenplay describes direction. Its media can predate renders made
|
|
2014
|
+
// while the Director ran, so a retained shot keeps its own.
|
|
2015
|
+
const existing = new Map(next.shots.map((shot) => [shot.id, shot]));
|
|
2016
|
+
next.shots = screenplay.shots.map((shot, index) => {
|
|
2017
|
+
const held = existing.get(shot.id);
|
|
2018
|
+
if (!held)
|
|
2019
|
+
return { ...shot, index };
|
|
2020
|
+
return {
|
|
2021
|
+
...held,
|
|
2022
|
+
...shot,
|
|
2023
|
+
index,
|
|
2024
|
+
keyframe: held.keyframe,
|
|
2025
|
+
keyframe_versions: held.keyframe_versions,
|
|
2026
|
+
clip: held.clip,
|
|
2027
|
+
clip_versions: held.clip_versions,
|
|
2028
|
+
covered_by: held.covered_by,
|
|
2029
|
+
status: held.status
|
|
2030
|
+
};
|
|
2031
|
+
});
|
|
2032
|
+
next.screenplay = screenplay;
|
|
2033
|
+
next.style = screenplay.style_bible ?? next.style;
|
|
2034
|
+
next.aspectRatio = screenplay.aspect_ratio ?? next.aspectRatio;
|
|
2035
|
+
if (screenplay.genre)
|
|
2036
|
+
next.genre = screenplay.genre;
|
|
2037
|
+
const saved = await Storyboard.updateFieldsIfUnchanged(current.row.id, current.row.updated_at, { document: JSON.stringify(next) });
|
|
2038
|
+
if (!saved)
|
|
2039
|
+
continue;
|
|
2040
|
+
return {
|
|
2041
|
+
storyboard_id: current.row.id,
|
|
2042
|
+
updated_at: saved.updated_at,
|
|
2043
|
+
title: screenplay.title,
|
|
2044
|
+
genre: next.genre,
|
|
2045
|
+
redirected: params["redirect"] === true,
|
|
2046
|
+
scenes: (screenplay.scenes ?? []).map((scene) => ({
|
|
2047
|
+
id: scene.id,
|
|
2048
|
+
slugline: scene.slugline,
|
|
2049
|
+
lighting: scene.lighting
|
|
2050
|
+
})),
|
|
2051
|
+
shots: next.shots.map((shot) => ({
|
|
2052
|
+
id: shot.id,
|
|
2053
|
+
index: shot.index,
|
|
2054
|
+
slug: shot.slug,
|
|
2055
|
+
action: shot.action,
|
|
2056
|
+
scene_id: shot.scene_id,
|
|
2057
|
+
status: shot.status,
|
|
2058
|
+
has_keyframe: !!shot.keyframe,
|
|
2059
|
+
has_clip: !!shot.clip
|
|
2060
|
+
}))
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
return {
|
|
2064
|
+
error: `Storyboard ${row.id} is being modified concurrently; the screenplay was directed but could not be saved. Retry the call.`
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
};
|
|
2068
|
+
// ---------------------------------------------------------------------------
|
|
2069
|
+
// extract_script_from_storyboard
|
|
2070
|
+
// ---------------------------------------------------------------------------
|
|
2071
|
+
//
|
|
2072
|
+
// The board is the consumer of the words: it projects line text into shots and
|
|
2073
|
+
// reads take durations for timing. Extraction moves ownership of the words to a
|
|
2074
|
+
// script resource and leaves the keys behind — `script_id` on the screenplay,
|
|
2075
|
+
// `script_line_ids` and a text snapshot on each shot.
|
|
2076
|
+
const extractScriptFromStoryboard = {
|
|
2077
|
+
spec: extractScriptFromStoryboardSpec,
|
|
2078
|
+
impl: async (run, params) => {
|
|
2079
|
+
const context = run.context;
|
|
2080
|
+
const board = await loadBoard(run, params["storyboard_id"]);
|
|
2081
|
+
if (isError(board))
|
|
2082
|
+
return board;
|
|
2083
|
+
const { row, doc } = board;
|
|
2084
|
+
const relink = params["relink"] === true;
|
|
2085
|
+
const screenplay = boardScreenplay(row, doc);
|
|
2086
|
+
const linkedScriptId = screenplay.script_id ?? null;
|
|
2087
|
+
if (linkedScriptId && !relink) {
|
|
2088
|
+
return {
|
|
2089
|
+
error: `Storyboard ${row.id} already links script ${linkedScriptId}. Pass relink: true to re-project the board's words onto that script.`
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
const { extractScriptFromScreenplay, joinLineTexts, validateScriptLink } = await import("@nodetool-ai/protocol");
|
|
2093
|
+
const entities = await loadBoardEntities(context, doc);
|
|
2094
|
+
const extracted = extractScriptFromScreenplay(screenplay, entities);
|
|
2095
|
+
const lines = extracted.document.sections.flatMap((s) => s.lines);
|
|
2096
|
+
if (lines.length === 0) {
|
|
2097
|
+
return {
|
|
2098
|
+
error: "No shot on this board carries dialogue or narration, so there is nothing to extract. Write the words with edit_storyboard first."
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
const textByLineId = new Map(lines.map((line) => [line.id, line.text]));
|
|
2102
|
+
/** Stamp the projection onto whatever shots the board holds right now. */
|
|
2103
|
+
const project = (shots) => shots.map((shot) => {
|
|
2104
|
+
const lineIds = extracted.lineIdsByShotId[shot.id];
|
|
2105
|
+
const next = { ...shot };
|
|
2106
|
+
if (!lineIds || lineIds.length === 0) {
|
|
2107
|
+
delete next.script_line_ids;
|
|
2108
|
+
delete next.script_text_snapshot;
|
|
2109
|
+
return next;
|
|
2110
|
+
}
|
|
2111
|
+
next.script_line_ids = lineIds;
|
|
2112
|
+
next.script_text_snapshot = joinLineTexts(lineIds.map((id) => textByLineId.get(id) ?? ""));
|
|
2113
|
+
return next;
|
|
2114
|
+
});
|
|
2115
|
+
// Bound as a namespace so the `Script` / `Storyboard` row types imported at
|
|
2116
|
+
// the top of this file stay visible here.
|
|
2117
|
+
const models = await import("@nodetool-ai/models");
|
|
2118
|
+
const document = JSON.stringify(extracted.document);
|
|
2119
|
+
// The link is stamped only after the row it names exists (design §7), so a
|
|
2120
|
+
// failed second write leaves an unlinked-but-valid pair rather than a
|
|
2121
|
+
// half-link no validation would accept.
|
|
2122
|
+
let scriptId;
|
|
2123
|
+
let reused = false;
|
|
2124
|
+
const existing = relink && linkedScriptId
|
|
2125
|
+
? await models.Script.findById(linkedScriptId)
|
|
2126
|
+
: null;
|
|
2127
|
+
if (existing && existing.user_id === context.userId) {
|
|
2128
|
+
const saved = await models.Script.updateFieldsIfUnchanged(existing.id, existing.updated_at, { document });
|
|
2129
|
+
if (!saved) {
|
|
2130
|
+
return {
|
|
2131
|
+
error: `Script ${existing.id} is being modified concurrently; nothing was written. Retry the call.`
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
scriptId = saved.id;
|
|
2135
|
+
reused = true;
|
|
2136
|
+
}
|
|
2137
|
+
else {
|
|
2138
|
+
const name = isString(params["name"]) && params["name"]
|
|
2139
|
+
? params["name"]
|
|
2140
|
+
: `${row.name} script`;
|
|
2141
|
+
const created = await models.Script.create({
|
|
2142
|
+
user_id: context.userId,
|
|
2143
|
+
project_id: row.project_id,
|
|
2144
|
+
name,
|
|
2145
|
+
document
|
|
2146
|
+
});
|
|
2147
|
+
scriptId = created.id;
|
|
2148
|
+
}
|
|
2149
|
+
const validation = validateScriptLink({ ...screenplay, script_id: scriptId, shots: project(doc.shots) }, extracted.document);
|
|
2150
|
+
if (validation.errors.length > 0) {
|
|
2151
|
+
return {
|
|
2152
|
+
error: `The projected link is invalid, so the board was left unlinked: ${validation.errors
|
|
2153
|
+
.map((issue) => issue.message)
|
|
2154
|
+
.join(" ")}`,
|
|
2155
|
+
script_id: scriptId
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
2159
|
+
const current = await models.Storyboard.findById(row.id);
|
|
2160
|
+
if (!current)
|
|
2161
|
+
return { error: `Storyboard ${row.id} was not found.` };
|
|
2162
|
+
const currentDoc = current.toDocument();
|
|
2163
|
+
const shots = project(currentDoc.shots);
|
|
2164
|
+
const saved = await models.Storyboard.updateFieldsIfUnchanged(current.id, current.updated_at, {
|
|
2165
|
+
document: JSON.stringify({
|
|
2166
|
+
...currentDoc,
|
|
2167
|
+
shots,
|
|
2168
|
+
screenplay: {
|
|
2169
|
+
...boardScreenplay(current, currentDoc),
|
|
2170
|
+
script_id: scriptId
|
|
2171
|
+
}
|
|
2172
|
+
})
|
|
2173
|
+
}, { ops: [{ tool: "set_link", input: { script_id: scriptId } }] });
|
|
2174
|
+
if (!saved)
|
|
2175
|
+
continue;
|
|
2176
|
+
// Last write, and only now that the board carries the forward link: the
|
|
2177
|
+
// back-pointer never names a board that failed to link (design §7).
|
|
2178
|
+
const stamped = await stampScriptStoryboardId(scriptId, current.id, context.userId);
|
|
2179
|
+
if (isError(stamped)) {
|
|
2180
|
+
return {
|
|
2181
|
+
error: `Storyboard ${current.id} links script ${scriptId}, but the script's back-pointer could not be written: ${stamped.error} The board is valid; the script reads as unlinked until this is retried with relink: true.`,
|
|
2182
|
+
storyboard_id: current.id,
|
|
2183
|
+
script_id: scriptId
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
return {
|
|
2187
|
+
ok: true,
|
|
2188
|
+
storyboard_id: current.id,
|
|
2189
|
+
script_id: scriptId,
|
|
2190
|
+
relinked: reused,
|
|
2191
|
+
line_count: lines.length,
|
|
2192
|
+
cast_count: extracted.document.cast.length,
|
|
2193
|
+
linked_shot_ids: shots
|
|
2194
|
+
.filter((shot) => (shot.script_line_ids ?? []).length > 0)
|
|
2195
|
+
.map((shot) => shot.id),
|
|
2196
|
+
warnings: validation.warnings.map((issue) => issue.message)
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
return {
|
|
2200
|
+
error: `Storyboard ${row.id} is being modified concurrently, so script ${scriptId} was written but the board could not be linked to it. Retry with relink: true.`,
|
|
2201
|
+
script_id: scriptId
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
/** Every storyboard capability, in the order the tool file declared them. */
|
|
2206
|
+
/**
|
|
2207
|
+
* Delete a storyboard the caller owns.
|
|
2208
|
+
*
|
|
2209
|
+
* The ownership check and the version cascade are `Storyboard.deleteOwned`, the
|
|
2210
|
+
* same function the tRPC route calls — a delete is not a place for two copies
|
|
2211
|
+
* of one rule, and version rows outliving their document would be unreachable
|
|
2212
|
+
* garbage. Missing and not-yours are one answer.
|
|
2213
|
+
*/
|
|
2214
|
+
const deleteStoryboard = {
|
|
2215
|
+
spec: deleteStoryboardSpec,
|
|
2216
|
+
impl: async (run, params) => {
|
|
2217
|
+
const userId = run.context.userId;
|
|
2218
|
+
if (!userId)
|
|
2219
|
+
return { error: "No user is bound to this session." };
|
|
2220
|
+
const { Storyboard } = await import("@nodetool-ai/models");
|
|
2221
|
+
const id = String(params["storyboard_id"]);
|
|
2222
|
+
const deleted = await Storyboard.deleteOwned(userId, id);
|
|
2223
|
+
return deleted
|
|
2224
|
+
? { storyboard_id: id, deleted: true }
|
|
2225
|
+
: { error: `Storyboard ${id} was not found, or it is not yours.` };
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
export const STORYBOARD_CAPABILITIES = [
|
|
2229
|
+
listStoryboards,
|
|
2230
|
+
createStoryboard,
|
|
2231
|
+
getStoryboard,
|
|
2232
|
+
renderStoryboardStills,
|
|
2233
|
+
renderStoryboardClips,
|
|
2234
|
+
reviseStoryboardClip,
|
|
2235
|
+
assembleStoryboardTimeline,
|
|
2236
|
+
editStoryboard,
|
|
2237
|
+
directStoryboard,
|
|
2238
|
+
extractScriptFromStoryboard,
|
|
2239
|
+
deleteStoryboard
|
|
2240
|
+
];
|
|
2241
|
+
export const module = {
|
|
2242
|
+
module: "storyboards",
|
|
2243
|
+
exports: STORYBOARD_CAPABILITIES
|
|
2244
|
+
};
|
|
2245
|
+
export { listStoryboards, createStoryboard, getStoryboard, renderStoryboardStills, renderStoryboardClips, reviseStoryboardClip, assembleStoryboardTimeline, editStoryboard, directStoryboard, extractScriptFromStoryboard, deleteStoryboard };
|
|
2246
|
+
//# sourceMappingURL=storyboards.js.map
|