@nodetool-ai/agents 0.8.1 → 0.8.3
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/dist/.nodetool-build-stamp +1 -1
- package/dist/analysis/audio-dsp.d.ts +3 -2
- package/dist/analysis/audio-dsp.d.ts.map +1 -1
- package/dist/analysis/audio-dsp.js +11 -8
- package/dist/analysis/audio-dsp.js.map +1 -1
- package/dist/analysis/media-decode.d.ts +13 -0
- package/dist/analysis/media-decode.d.ts.map +1 -1
- package/dist/analysis/media-decode.js +33 -6
- package/dist/analysis/media-decode.js.map +1 -1
- package/dist/apify/assets.d.ts.map +1 -1
- package/dist/apify/assets.js +4 -0
- package/dist/apify/assets.js.map +1 -1
- package/dist/author-graph.d.ts +2 -3
- package/dist/author-graph.d.ts.map +1 -1
- package/dist/author-graph.js +3 -5
- package/dist/author-graph.js.map +1 -1
- package/dist/browser-gate.d.ts +2 -2
- package/dist/browser-gate.js +2 -2
- package/dist/capabilities/GamePublishRequest.d.ts +14 -0
- package/dist/capabilities/GamePublishRequest.d.ts.map +1 -0
- package/dist/capabilities/GamePublishRequest.js +2 -0
- package/dist/capabilities/GamePublishRequest.js.map +1 -0
- package/dist/capabilities/adapters.d.ts +4 -13
- package/dist/capabilities/adapters.d.ts.map +1 -1
- package/dist/capabilities/adapters.js +9 -14
- package/dist/capabilities/adapters.js.map +1 -1
- package/dist/capabilities/agent-activity.d.ts +27 -0
- package/dist/capabilities/agent-activity.d.ts.map +1 -0
- package/dist/capabilities/agent-activity.js +103 -0
- package/dist/capabilities/agent-activity.js.map +1 -0
- package/dist/capabilities/agentic-storyboard-finish.d.ts +67 -0
- package/dist/capabilities/agentic-storyboard-finish.d.ts.map +1 -0
- package/dist/capabilities/agentic-storyboard-finish.js +1321 -0
- package/dist/capabilities/agentic-storyboard-finish.js.map +1 -0
- package/dist/capabilities/agents.d.ts +5 -2
- package/dist/capabilities/agents.d.ts.map +1 -1
- package/dist/capabilities/agents.js +14 -4
- package/dist/capabilities/agents.js.map +1 -1
- package/dist/capabilities/agents.specs.d.ts +3 -0
- package/dist/capabilities/agents.specs.d.ts.map +1 -1
- package/dist/capabilities/agents.specs.js +71 -1
- package/dist/capabilities/agents.specs.js.map +1 -1
- package/dist/capabilities/analysis.d.ts.map +1 -1
- package/dist/capabilities/analysis.js +33 -22
- package/dist/capabilities/analysis.js.map +1 -1
- package/dist/capabilities/analysis.specs.js +2 -2
- package/dist/capabilities/analysis.specs.js.map +1 -1
- package/dist/capabilities/apps.d.ts.map +1 -1
- package/dist/capabilities/apps.js +5 -3
- package/dist/capabilities/apps.js.map +1 -1
- package/dist/capabilities/args.d.ts +2 -5
- package/dist/capabilities/args.d.ts.map +1 -1
- package/dist/capabilities/args.js +2 -5
- package/dist/capabilities/args.js.map +1 -1
- package/dist/capabilities/assets.d.ts.map +1 -1
- package/dist/capabilities/assets.js +40 -10
- package/dist/capabilities/assets.js.map +1 -1
- package/dist/capabilities/browser.d.ts +1 -1
- package/dist/capabilities/browser.js +1 -1
- package/dist/capabilities/browser.specs.d.ts +1 -1
- package/dist/capabilities/browser.specs.js +1 -1
- package/dist/capabilities/code.d.ts +5 -0
- package/dist/capabilities/code.d.ts.map +1 -1
- package/dist/capabilities/code.js +128 -89
- package/dist/capabilities/code.js.map +1 -1
- package/dist/capabilities/collections.d.ts +4 -6
- package/dist/capabilities/collections.d.ts.map +1 -1
- package/dist/capabilities/collections.js +16 -11
- package/dist/capabilities/collections.js.map +1 -1
- package/dist/capabilities/derive-shot.d.ts +45 -0
- package/dist/capabilities/derive-shot.d.ts.map +1 -0
- package/dist/capabilities/derive-shot.js +82 -0
- package/dist/capabilities/derive-shot.js.map +1 -0
- package/dist/capabilities/documents.d.ts +2 -1
- package/dist/capabilities/documents.d.ts.map +1 -1
- package/dist/capabilities/documents.js +12 -6
- package/dist/capabilities/documents.js.map +1 -1
- package/dist/capabilities/email.d.ts +2 -1
- package/dist/capabilities/email.d.ts.map +1 -1
- package/dist/capabilities/email.js +5 -3
- package/dist/capabilities/email.js.map +1 -1
- package/dist/capabilities/errors.d.ts +19 -0
- package/dist/capabilities/errors.d.ts.map +1 -0
- package/dist/capabilities/errors.js +109 -0
- package/dist/capabilities/errors.js.map +1 -0
- package/dist/capabilities/errors.specs.d.ts +11 -0
- package/dist/capabilities/errors.specs.d.ts.map +1 -0
- package/dist/capabilities/errors.specs.js +100 -0
- package/dist/capabilities/errors.specs.js.map +1 -0
- package/dist/capabilities/example-timelines.d.ts.map +1 -1
- package/dist/capabilities/example-timelines.js +52 -10
- package/dist/capabilities/example-timelines.js.map +1 -1
- package/dist/capabilities/example-timelines.specs.d.ts +1 -0
- package/dist/capabilities/example-timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/example-timelines.specs.js +21 -3
- package/dist/capabilities/example-timelines.specs.js.map +1 -1
- package/dist/capabilities/finish-storyboard.d.ts +5 -0
- package/dist/capabilities/finish-storyboard.d.ts.map +1 -0
- package/dist/capabilities/finish-storyboard.js +278 -0
- package/dist/capabilities/finish-storyboard.js.map +1 -0
- package/dist/capabilities/game-asset-browser.d.ts +11 -0
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -0
- package/dist/capabilities/game-asset-browser.js +152 -0
- package/dist/capabilities/game-asset-browser.js.map +1 -0
- package/dist/capabilities/game-asset-source.js +1 -1
- package/dist/capabilities/game-asset-source.js.map +1 -1
- package/dist/capabilities/game.d.ts +44 -2
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +408 -160
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.d.ts +4 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -1
- package/dist/capabilities/game.specs.js +42 -13
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts +17 -0
- package/dist/capabilities/game3d.d.ts.map +1 -0
- package/dist/capabilities/game3d.js +434 -0
- package/dist/capabilities/game3d.js.map +1 -0
- package/dist/capabilities/gate-from-context.d.ts +1 -1
- package/dist/capabilities/gate-from-context.js +1 -1
- package/dist/capabilities/generations.d.ts +2 -0
- package/dist/capabilities/generations.d.ts.map +1 -1
- package/dist/capabilities/generations.js +14 -23
- package/dist/capabilities/generations.js.map +1 -1
- package/dist/capabilities/index.d.ts +2 -2
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +2 -2
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/invoke.d.ts +14 -25
- package/dist/capabilities/invoke.d.ts.map +1 -1
- package/dist/capabilities/invoke.js +72 -35
- package/dist/capabilities/invoke.js.map +1 -1
- package/dist/capabilities/jobs.d.ts.map +1 -1
- package/dist/capabilities/jobs.js +47 -3
- package/dist/capabilities/jobs.js.map +1 -1
- package/dist/capabilities/jobs.specs.d.ts.map +1 -1
- package/dist/capabilities/jobs.specs.js +3 -1
- package/dist/capabilities/jobs.specs.js.map +1 -1
- package/dist/capabilities/js-scripts.d.ts +19 -1
- package/dist/capabilities/js-scripts.d.ts.map +1 -1
- package/dist/capabilities/js-scripts.js +109 -2
- package/dist/capabilities/js-scripts.js.map +1 -1
- package/dist/capabilities/js-scripts.specs.d.ts +2 -0
- package/dist/capabilities/js-scripts.specs.d.ts.map +1 -1
- package/dist/capabilities/js-scripts.specs.js +50 -0
- package/dist/capabilities/js-scripts.specs.js.map +1 -1
- package/dist/capabilities/lazy-tool-core.d.ts +23 -0
- package/dist/capabilities/lazy-tool-core.d.ts.map +1 -0
- package/dist/capabilities/lazy-tool-core.js +56 -0
- package/dist/capabilities/lazy-tool-core.js.map +1 -0
- package/dist/capabilities/lazy-tool.d.ts +3 -23
- package/dist/capabilities/lazy-tool.d.ts.map +1 -1
- package/dist/capabilities/lazy-tool.js +6 -64
- package/dist/capabilities/lazy-tool.js.map +1 -1
- package/dist/capabilities/legacy-tools.d.ts +15 -0
- package/dist/capabilities/legacy-tools.d.ts.map +1 -0
- package/dist/capabilities/legacy-tools.js +98 -0
- package/dist/capabilities/legacy-tools.js.map +1 -0
- package/dist/capabilities/media.d.ts +0 -9
- package/dist/capabilities/media.d.ts.map +1 -1
- package/dist/capabilities/media.js +314 -261
- package/dist/capabilities/media.js.map +1 -1
- package/dist/capabilities/media.specs.d.ts.map +1 -1
- package/dist/capabilities/media.specs.js +17 -0
- package/dist/capabilities/media.specs.js.map +1 -1
- package/dist/capabilities/metadata.d.ts +62 -0
- package/dist/capabilities/metadata.d.ts.map +1 -0
- package/dist/capabilities/metadata.js +114 -0
- package/dist/capabilities/metadata.js.map +1 -0
- package/dist/capabilities/model3d.d.ts.map +1 -1
- package/dist/capabilities/model3d.js +4 -0
- package/dist/capabilities/model3d.js.map +1 -1
- package/dist/capabilities/models.d.ts +2 -2
- package/dist/capabilities/models.d.ts.map +1 -1
- package/dist/capabilities/models.js +5 -3
- package/dist/capabilities/models.js.map +1 -1
- package/dist/capabilities/registry.d.ts +6 -19
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +72 -113
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/capabilities/run-agent.d.ts +19 -0
- package/dist/capabilities/run-agent.d.ts.map +1 -0
- package/dist/capabilities/run-agent.js +183 -0
- package/dist/capabilities/run-agent.js.map +1 -0
- package/dist/capabilities/runs.d.ts +9 -0
- package/dist/capabilities/runs.d.ts.map +1 -0
- package/dist/capabilities/runs.js +46 -0
- package/dist/capabilities/runs.js.map +1 -0
- package/dist/capabilities/runs.specs.d.ts +39 -0
- package/dist/capabilities/runs.specs.d.ts.map +1 -0
- package/dist/capabilities/runs.specs.js +52 -0
- package/dist/capabilities/runs.specs.js.map +1 -0
- package/dist/capabilities/scripts.d.ts.map +1 -1
- package/dist/capabilities/scripts.js +11 -5
- package/dist/capabilities/scripts.js.map +1 -1
- package/dist/capabilities/settings.d.ts.map +1 -1
- package/dist/capabilities/settings.js +4 -2
- package/dist/capabilities/settings.js.map +1 -1
- package/dist/capabilities/sketches.d.ts.map +1 -1
- package/dist/capabilities/sketches.js +4 -2
- package/dist/capabilities/sketches.js.map +1 -1
- package/dist/capabilities/skills.d.ts.map +1 -1
- package/dist/capabilities/skills.js +22 -2
- package/dist/capabilities/skills.js.map +1 -1
- package/dist/capabilities/storyboard-layout-check.d.ts +33 -0
- package/dist/capabilities/storyboard-layout-check.d.ts.map +1 -0
- package/dist/capabilities/storyboard-layout-check.js +229 -0
- package/dist/capabilities/storyboard-layout-check.js.map +1 -0
- package/dist/capabilities/storyboard-measured-sources.d.ts +10 -0
- package/dist/capabilities/storyboard-measured-sources.d.ts.map +1 -0
- package/dist/capabilities/storyboard-measured-sources.js +67 -0
- package/dist/capabilities/storyboard-measured-sources.js.map +1 -0
- package/dist/capabilities/storyboard-review-times.d.ts +4 -0
- package/dist/capabilities/storyboard-review-times.d.ts.map +1 -0
- package/dist/capabilities/storyboard-review-times.js +39 -0
- package/dist/capabilities/storyboard-review-times.js.map +1 -0
- package/dist/capabilities/storyboards.d.ts +12 -18
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +779 -96
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts +9 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +173 -5
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/threads.js +1 -1
- package/dist/capabilities/threads.js.map +1 -1
- package/dist/capabilities/timeline-bridge.d.ts +368 -0
- package/dist/capabilities/timeline-bridge.d.ts.map +1 -0
- package/dist/capabilities/timeline-bridge.js +1008 -0
- package/dist/capabilities/timeline-bridge.js.map +1 -0
- package/dist/capabilities/timeline-clip-frames.d.ts +17 -0
- package/dist/capabilities/timeline-clip-frames.d.ts.map +1 -0
- package/dist/capabilities/timeline-clip-frames.js +84 -0
- package/dist/capabilities/timeline-clip-frames.js.map +1 -0
- package/dist/capabilities/timeline-media-edit.d.ts +8 -0
- package/dist/capabilities/timeline-media-edit.d.ts.map +1 -0
- package/dist/capabilities/timeline-media-edit.js +94 -0
- package/dist/capabilities/timeline-media-edit.js.map +1 -0
- package/dist/capabilities/timeline-native-media.d.ts.map +1 -1
- package/dist/capabilities/timeline-native-media.js +40 -0
- package/dist/capabilities/timeline-native-media.js.map +1 -1
- package/dist/capabilities/timeline-operations.d.ts +52 -0
- package/dist/capabilities/timeline-operations.d.ts.map +1 -0
- package/dist/capabilities/timeline-operations.js +413 -0
- package/dist/capabilities/timeline-operations.js.map +1 -0
- package/dist/capabilities/timelines.d.ts +28 -3
- package/dist/capabilities/timelines.d.ts.map +1 -1
- package/dist/capabilities/timelines.js +410 -294
- package/dist/capabilities/timelines.js.map +1 -1
- package/dist/capabilities/timelines.specs.d.ts +8 -0
- package/dist/capabilities/timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/timelines.specs.js +178 -9
- package/dist/capabilities/timelines.specs.js.map +1 -1
- package/dist/capabilities/types.d.ts +3 -1
- package/dist/capabilities/types.d.ts.map +1 -1
- package/dist/capabilities/types.js.map +1 -1
- package/dist/capabilities/ui.d.ts.map +1 -1
- package/dist/capabilities/ui.js +24 -2
- package/dist/capabilities/ui.js.map +1 -1
- package/dist/capabilities/web.d.ts.map +1 -1
- package/dist/capabilities/web.js +4 -0
- package/dist/capabilities/web.js.map +1 -1
- package/dist/capabilities/workflows.d.ts +3 -1
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +244 -39
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts +2 -0
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +72 -2
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/codeact/action-diagnostics.d.ts.map +1 -1
- package/dist/codeact/action-diagnostics.js +51 -1
- package/dist/codeact/action-diagnostics.js.map +1 -1
- package/dist/codeact/codeact-executor.d.ts.map +1 -1
- package/dist/codeact/codeact-executor.js +16 -8
- package/dist/codeact/codeact-executor.js.map +1 -1
- package/dist/codeact/compact-ids.d.ts.map +1 -1
- package/dist/codeact/compact-ids.js +5 -1
- package/dist/codeact/compact-ids.js.map +1 -1
- package/dist/codeact/graph-model.d.ts +1 -1
- package/dist/codeact/graph-model.d.ts.map +1 -1
- package/dist/codeact/graph-model.js +2 -3
- package/dist/codeact/graph-model.js.map +1 -1
- package/dist/codeact/nodetool-api.d.ts +1 -1
- package/dist/codeact/nodetool-api.d.ts.map +1 -1
- package/dist/codeact/nodetool-api.js +202 -284
- package/dist/codeact/nodetool-api.js.map +1 -1
- package/dist/codeact/sandbox-packages.d.ts +0 -7
- package/dist/codeact/sandbox-packages.d.ts.map +1 -1
- package/dist/codeact/sandbox-packages.js +1 -10
- package/dist/codeact/sandbox-packages.js.map +1 -1
- package/dist/codeact/tool-api.d.ts +6 -3
- package/dist/codeact/tool-api.d.ts.map +1 -1
- package/dist/codeact/tool-api.js +22 -15
- package/dist/codeact/tool-api.js.map +1 -1
- package/dist/evals/codeact-api-cases.d.ts.map +1 -1
- package/dist/evals/codeact-api-cases.js +2 -0
- package/dist/evals/codeact-api-cases.js.map +1 -1
- package/dist/evals/codeact-api-runs.d.ts +3 -0
- package/dist/evals/codeact-api-runs.d.ts.map +1 -0
- package/dist/evals/codeact-api-runs.js +33 -0
- package/dist/evals/codeact-api-runs.js.map +1 -0
- package/dist/evals/surfaces/game.d.ts +8 -2
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +460 -2
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/evals/surfaces/sketch.d.ts +2 -0
- package/dist/evals/surfaces/sketch.d.ts.map +1 -1
- package/dist/evals/surfaces/sketch.js +11 -3
- package/dist/evals/surfaces/sketch.js.map +1 -1
- package/dist/evals/surfaces/timeline.d.ts +4 -357
- package/dist/evals/surfaces/timeline.d.ts.map +1 -1
- package/dist/evals/surfaces/timeline.js +9 -2621
- package/dist/evals/surfaces/timeline.js.map +1 -1
- package/dist/evals/tool-loop-bridge.d.ts +4 -0
- package/dist/evals/tool-loop-bridge.d.ts.map +1 -1
- package/dist/evals/tool-loop-bridge.js.map +1 -1
- package/dist/evals/tool-loop-eval.d.ts +2 -0
- package/dist/evals/tool-loop-eval.d.ts.map +1 -1
- package/dist/evals/tool-loop-eval.js +6 -1
- package/dist/evals/tool-loop-eval.js.map +1 -1
- package/dist/evals/tool-loop-permission.d.ts +1 -15
- package/dist/evals/tool-loop-permission.d.ts.map +1 -1
- package/dist/evals/tool-loop-permission.js +23 -41
- package/dist/evals/tool-loop-permission.js.map +1 -1
- package/dist/game-authoring.d.ts +9573 -0
- package/dist/game-authoring.d.ts.map +1 -0
- package/dist/game-authoring.js +146 -0
- package/dist/game-authoring.js.map +1 -0
- package/dist/game-build.d.ts +3 -0
- package/dist/game-build.d.ts.map +1 -0
- package/dist/game-build.js +3 -0
- package/dist/game-build.js.map +1 -0
- package/dist/game-code-bake.d.ts +26 -0
- package/dist/game-code-bake.d.ts.map +1 -0
- package/dist/game-code-bake.js +98 -0
- package/dist/game-code-bake.js.map +1 -0
- package/dist/game-examples.d.ts +2227 -18
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/game-examples.js +5 -4
- package/dist/game-examples.js.map +1 -1
- package/dist/generation-cancel.d.ts +31 -0
- package/dist/generation-cancel.d.ts.map +1 -0
- package/dist/generation-cancel.js +28 -0
- package/dist/generation-cancel.js.map +1 -0
- package/dist/host-access-gate.d.ts +29 -0
- package/dist/host-access-gate.d.ts.map +1 -0
- package/dist/host-access-gate.js +39 -0
- package/dist/host-access-gate.js.map +1 -0
- package/dist/host-modules/dispatcher.d.ts +7 -1
- package/dist/host-modules/dispatcher.d.ts.map +1 -1
- package/dist/host-modules/dispatcher.js +13 -2
- package/dist/host-modules/dispatcher.js.map +1 -1
- package/dist/host-modules/docx.d.ts.map +1 -1
- package/dist/host-modules/docx.js +2 -2
- package/dist/host-modules/docx.js.map +1 -1
- package/dist/host-modules/epub.d.ts.map +1 -1
- package/dist/host-modules/epub.js +2 -2
- package/dist/host-modules/epub.js.map +1 -1
- package/dist/host-modules/fabric.d.ts.map +1 -1
- package/dist/host-modules/fabric.js +1 -1
- package/dist/host-modules/fabric.js.map +1 -1
- package/dist/host-modules/limits.d.ts +10 -8
- package/dist/host-modules/limits.d.ts.map +1 -1
- package/dist/host-modules/limits.js +14 -9
- package/dist/host-modules/limits.js.map +1 -1
- package/dist/host-modules/mammoth.d.ts.map +1 -1
- package/dist/host-modules/mammoth.js +2 -2
- package/dist/host-modules/mammoth.js.map +1 -1
- package/dist/host-modules/ocr.d.ts.map +1 -1
- package/dist/host-modules/ocr.js +1 -1
- package/dist/host-modules/ocr.js.map +1 -1
- package/dist/host-modules/pdf.d.ts.map +1 -1
- package/dist/host-modules/pdf.js +2 -2
- package/dist/host-modules/pdf.js.map +1 -1
- package/dist/host-modules/pdflib.d.ts.map +1 -1
- package/dist/host-modules/pdflib.js +2 -2
- package/dist/host-modules/pdflib.js.map +1 -1
- package/dist/host-modules/pptx.d.ts.map +1 -1
- package/dist/host-modules/pptx.js +2 -2
- package/dist/host-modules/pptx.js.map +1 -1
- package/dist/host-modules/pptxgen.d.ts.map +1 -1
- package/dist/host-modules/pptxgen.js +2 -2
- package/dist/host-modules/pptxgen.js.map +1 -1
- package/dist/host-modules/tfjs.d.ts.map +1 -1
- package/dist/host-modules/tfjs.js +4 -4
- package/dist/host-modules/tfjs.js.map +1 -1
- package/dist/host-modules/xlsx.d.ts.map +1 -1
- package/dist/host-modules/xlsx.js +2 -2
- package/dist/host-modules/xlsx.js.map +1 -1
- package/dist/index.d.ts +18 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -3
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox-worker/engine.d.ts +4 -0
- package/dist/js-sandbox-worker/engine.d.ts.map +1 -0
- package/dist/js-sandbox-worker/engine.js +45 -0
- package/dist/js-sandbox-worker/engine.js.map +1 -0
- package/dist/js-sandbox-worker/host.js +1 -1
- package/dist/js-sandbox-worker/host.js.map +1 -1
- package/dist/js-sandbox-worker/interpreter.d.ts +1 -1
- package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -1
- package/dist/js-sandbox-worker/interpreter.js +94 -2
- package/dist/js-sandbox-worker/interpreter.js.map +1 -1
- package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -1
- package/dist/js-sandbox-worker/worker-entry.js +3 -2
- package/dist/js-sandbox-worker/worker-entry.js.map +1 -1
- package/dist/js-sandbox.d.ts +9 -1
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +76 -15
- package/dist/js-sandbox.js.map +1 -1
- package/dist/js-script-app-runner.d.ts +2 -0
- package/dist/js-script-app-runner.d.ts.map +1 -1
- package/dist/js-script-app-runner.js +8 -3
- package/dist/js-script-app-runner.js.map +1 -1
- package/dist/js-script-sandbox.d.ts +12 -18
- package/dist/js-script-sandbox.d.ts.map +1 -1
- package/dist/js-script-sandbox.js +3 -49
- package/dist/js-script-sandbox.js.map +1 -1
- package/dist/prompts/product-knowledge.d.ts +1 -1
- package/dist/prompts/product-knowledge.d.ts.map +1 -1
- package/dist/prompts/product-knowledge.js +82 -41
- package/dist/prompts/product-knowledge.js.map +1 -1
- package/dist/sandbox-pack-resolution.d.ts +21 -0
- package/dist/sandbox-pack-resolution.d.ts.map +1 -0
- package/dist/sandbox-pack-resolution.js +42 -0
- package/dist/sandbox-pack-resolution.js.map +1 -0
- package/dist/sandbox-toolbelt.d.ts +3 -1
- package/dist/sandbox-toolbelt.d.ts.map +1 -1
- package/dist/sandbox-toolbelt.js +8 -2
- package/dist/sandbox-toolbelt.js.map +1 -1
- package/dist/skill-prompt.d.ts +11 -1
- package/dist/skill-prompt.d.ts.map +1 -1
- package/dist/skill-prompt.js +17 -6
- package/dist/skill-prompt.js.map +1 -1
- package/dist/system-skills.d.ts +7 -1
- package/dist/system-skills.d.ts.map +1 -1
- package/dist/system-skills.js +2 -2
- package/dist/system-skills.js.map +1 -1
- package/dist/timeline-capture.d.ts +21 -0
- package/dist/timeline-capture.d.ts.map +1 -0
- package/dist/timeline-capture.js +564 -0
- package/dist/timeline-capture.js.map +1 -0
- package/dist/timeline-code-bake.d.ts +75 -0
- package/dist/timeline-code-bake.d.ts.map +1 -0
- package/dist/timeline-code-bake.js +327 -0
- package/dist/timeline-code-bake.js.map +1 -0
- package/dist/timeline-preview/frames.d.ts +26 -0
- package/dist/timeline-preview/frames.d.ts.map +1 -1
- package/dist/timeline-preview/frames.js +155 -70
- package/dist/timeline-preview/frames.js.map +1 -1
- package/dist/tools/base-tool.d.ts +1 -0
- package/dist/tools/base-tool.d.ts.map +1 -1
- package/dist/tools/base-tool.js +1 -1
- package/dist/tools/base-tool.js.map +1 -1
- package/dist/tools/builtin-tools.d.ts +4 -3
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +24 -3
- package/dist/tools/builtin-tools.js.map +1 -1
- package/dist/tools/external-capability-tools.d.ts +1 -1
- package/dist/tools/external-capability-tools.js +1 -1
- package/dist/tools/mcp-tool-support.d.ts +10 -9
- package/dist/tools/mcp-tool-support.d.ts.map +1 -1
- package/dist/tools/mcp-tool-support.js +14 -13
- package/dist/tools/mcp-tool-support.js.map +1 -1
- package/dist/tools/mcp-tools.d.ts +6 -4
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +65 -62
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/tools/tool-permissions.d.ts +1 -1
- package/dist/tools/tool-permissions.js +1 -1
- package/package.json +124 -41
- package/dist/capabilities/gate-tools.d.ts +0 -18
- package/dist/capabilities/gate-tools.d.ts.map +0 -1
- package/dist/capabilities/gate-tools.js +0 -66
- package/dist/capabilities/gate-tools.js.map +0 -1
- package/dist/tsconfig.tests.tmp.tsbuildinfo +0 -1
|
@@ -0,0 +1,1008 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Saved-document timeline adapter. Tool schemas parse calls, the shared ops
|
|
3
|
+
* engine edits the document, and host hooks resolve assets, persist derived
|
|
4
|
+
* formats, start jobs, and sample frames. Eval surfaces use this adapter too.
|
|
5
|
+
*/
|
|
6
|
+
import { isShortResourceId } from "@nodetool-ai/protocol";
|
|
7
|
+
import { buildTimelineToolContracts } from "@nodetool-ai/protocol/api-schemas/timeline-tool-contracts.js";
|
|
8
|
+
import { APPLY_TRANSITION_AT_CUT_DESCRIPTION, SET_BAKED_ANIMATION_DESCRIPTION, SET_GENERATED_MATTE_DESCRIPTION, applyTransitionAtCutParams, buildTransition, setBakedAnimationParams, setGeneratedMatteParams, textStylePatchParams } from "@nodetool-ai/protocol/api-schemas/timeline-tool-params.js";
|
|
9
|
+
import { uiToolParams } from "@nodetool-ai/protocol/api-schemas/ui-tool-contract.js";
|
|
10
|
+
import { parseWithTypeCoercion } from "@nodetool-ai/runtime";
|
|
11
|
+
import { ANIMATED_PROPERTIES, DEFAULT_BEAT_TOLERANCE_MS, DEFAULT_MIDI_INSTRUMENT, DEFAULT_TEMPO, STAGGER_UNITS, activeTakeIdOf, applyTakeToClip, captureMediaEditSourceContext, composeGenerativeTakePatch, createTimeOrderedUuid, createMediaEditRequest, ensureBaselineTake, formatBarsBeats, makeClip, makeTrack, planTransitionAtCut, resolveTempo, visibleNotes } from "@nodetool-ai/timeline";
|
|
12
|
+
import { applyTimelineOp, timelineOpFromToolArgs } from "@nodetool-ai/timeline/ops";
|
|
13
|
+
import { computeActiveLayers, parseSvgPath } from "@nodetool-ai/timeline/scene";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
export { unwrapClipParams } from "@nodetool-ai/timeline/ops";
|
|
16
|
+
/**
|
|
17
|
+
* The `ui_timeline_*` contracts, shared with the browser registry
|
|
18
|
+
* (`web/src/lib/tools/builtin/timeline.ts`). The vocabulary comes from
|
|
19
|
+
* `@nodetool-ai/timeline`, which the protocol package sits below.
|
|
20
|
+
*/
|
|
21
|
+
const CONTRACTS = buildTimelineToolContracts({
|
|
22
|
+
staggerUnits: STAGGER_UNITS,
|
|
23
|
+
animatedProperties: ANIMATED_PROPERTIES,
|
|
24
|
+
beatToleranceMs: DEFAULT_BEAT_TOLERANCE_MS
|
|
25
|
+
});
|
|
26
|
+
const generatedTransitionCandidateParams = applyTransitionAtCutParams;
|
|
27
|
+
const transitionCandidateParams = z.object({
|
|
28
|
+
candidate_id: z.string().trim().min(1)
|
|
29
|
+
});
|
|
30
|
+
const transitionCandidateLifecycleParams = applyTransitionAtCutParams
|
|
31
|
+
.partial()
|
|
32
|
+
.extend({ candidate_id: z.string().trim().min(1).optional() })
|
|
33
|
+
.superRefine((value, context) => {
|
|
34
|
+
if (value.candidate_id === undefined &&
|
|
35
|
+
(value.outgoingClipId === undefined || value.incomingClipId === undefined)) {
|
|
36
|
+
context.addIssue({
|
|
37
|
+
code: "custom",
|
|
38
|
+
message: "Provide outgoingClipId and incomingClipId to create a candidate, or candidate_id to apply one."
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
/** Units a failed lookup names before it stops and points at get_state. */
|
|
43
|
+
const MAX_LISTED_UNITS = 12;
|
|
44
|
+
function tool(name, description, parameters, impl) {
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
parameters,
|
|
49
|
+
execute: (args) => {
|
|
50
|
+
const parsed = parseWithTypeCoercion(parameters, args ?? {});
|
|
51
|
+
return impl(parsed);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* `set_clip_params` takes the style as a patch merged over the clip's own, so
|
|
57
|
+
* every field is optional. The shared contract still declares the whole-bag
|
|
58
|
+
* `textStyleParams`, which refuses `{textStyle: {fontFamily}}` on a finished
|
|
59
|
+
* title. Both hosts override that one field; when protocol carries
|
|
60
|
+
* `textStylePatchParams` the two overrides go together.
|
|
61
|
+
*/
|
|
62
|
+
const CONTRACT_SHAPE_OVERRIDES = {
|
|
63
|
+
ui_timeline_set_clip_params: { textStyle: textStylePatchParams.optional() }
|
|
64
|
+
};
|
|
65
|
+
/** A tool whose name, description and argument shape both hosts share. */
|
|
66
|
+
function sharedTool(name, impl) {
|
|
67
|
+
const contract = CONTRACTS[name];
|
|
68
|
+
const shape = { ...contract.shape, ...CONTRACT_SHAPE_OVERRIDES[name] };
|
|
69
|
+
// No host fields: this bridge drives one implicit sequence, so there is no
|
|
70
|
+
// `timeline_id` to disambiguate.
|
|
71
|
+
return tool(name, contract.description, uiToolParams({ ...contract, shape }), impl);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Keys a caller wraps the whole patch in.
|
|
75
|
+
*
|
|
76
|
+
* `set_clip_params` reads its fields off the op itself — `{op, target,
|
|
77
|
+
* textStyle}` — but a REST-shaped guess sends `{op, target, params: {…}}`, and
|
|
78
|
+
* that used to be refused as an unknown key with the real fields hidden one
|
|
79
|
+
* level down inside it. The wrapper says nothing the op does not already know,
|
|
80
|
+
* so it is unwrapped rather than argued with.
|
|
81
|
+
*/
|
|
82
|
+
const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
function capitalize(s) {
|
|
84
|
+
return s.length > 0 ? s[0].toUpperCase() + s.slice(1) : s;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build an in-memory timeline bridge whose tools share the `ui_timeline_*`
|
|
88
|
+
* contract but run headlessly against a plain sequence of tracks/clips (no
|
|
89
|
+
* Zustand stores, no rendering).
|
|
90
|
+
*/
|
|
91
|
+
export function createTimelineToolBridge(initial = {}) {
|
|
92
|
+
const seed = initial.sequence;
|
|
93
|
+
const resolveAsset = initial.resolveAsset;
|
|
94
|
+
const bakeAnimation = initial.bakeAnimation;
|
|
95
|
+
const bakeModel3DClip = initial.bakeModel3DClip;
|
|
96
|
+
const loadComposition = initial.loadComposition;
|
|
97
|
+
const retargetFormat = initial.retargetFormat;
|
|
98
|
+
const generateTransition = initial.generateTransition;
|
|
99
|
+
const sequenceId = initial.sequenceId ?? "seq_eval";
|
|
100
|
+
const sequenceName = initial.sequenceName ?? "Sequence";
|
|
101
|
+
const projectId = initial.projectId ?? "";
|
|
102
|
+
const fps = seed?.fps ?? initial.fps ?? 30;
|
|
103
|
+
const width = seed?.width ?? initial.width ?? 1920;
|
|
104
|
+
const height = seed?.height ?? initial.height ?? 1080;
|
|
105
|
+
let playheadMs = 0;
|
|
106
|
+
let selectedClipIds = [];
|
|
107
|
+
// Absent until a midi track or `set_tempo` puts one there — a document with
|
|
108
|
+
// no midi in it should not grow a tempo field it never asked for.
|
|
109
|
+
let tempo = seed?.tempo;
|
|
110
|
+
const tracks = [];
|
|
111
|
+
let clips = [];
|
|
112
|
+
let markers = [];
|
|
113
|
+
// Document-level, like markers: a track is named by `clipId`, and a clip
|
|
114
|
+
// follows one through its own `trackBinding` (P0 AI Video, Phase 2).
|
|
115
|
+
let mediaTracks = [];
|
|
116
|
+
let transitionCandidates = [];
|
|
117
|
+
let auditionedTransitionCandidateId;
|
|
118
|
+
let appliedTransitionCandidates = [];
|
|
119
|
+
const derivedSequences = [];
|
|
120
|
+
let setup = seed?.setup
|
|
121
|
+
? structuredClone(seed.setup)
|
|
122
|
+
: null;
|
|
123
|
+
const toolLog = [];
|
|
124
|
+
const previewTimesMs = [];
|
|
125
|
+
const previewedLayerKinds = new Set();
|
|
126
|
+
// Generated ids are 32 hex characters, like every other resource id. A
|
|
127
|
+
// per-call counter restarted at `link_1` on each call and joined a new split
|
|
128
|
+
// to a link group the seeded document already held.
|
|
129
|
+
const newId = initial.newId ?? (() => createTimeOrderedUuid());
|
|
130
|
+
const nextTrackId = () => newId("track");
|
|
131
|
+
const nextClipId = () => newId("clip");
|
|
132
|
+
const nextVersionId = () => newId("version");
|
|
133
|
+
const nextTransitionId = () => newId("transition");
|
|
134
|
+
const clipEnd = (clip) => clip.startMs + clip.durationMs;
|
|
135
|
+
function isAmbiguousCut(outgoing, incoming) {
|
|
136
|
+
const cutTimeMs = incoming.startMs;
|
|
137
|
+
return clips.some((candidate) => {
|
|
138
|
+
if (candidate.id === outgoing.id ||
|
|
139
|
+
candidate.id === incoming.id ||
|
|
140
|
+
candidate.trackId !== outgoing.trackId) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return ((candidate.startMs <= cutTimeMs && clipEnd(candidate) > cutTimeMs) ||
|
|
144
|
+
candidate.startMs === cutTimeMs);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function transitionSource(outgoing, incoming, durationMs) {
|
|
148
|
+
return {
|
|
149
|
+
sequenceId,
|
|
150
|
+
outgoingClipId: outgoing.id,
|
|
151
|
+
incomingClipId: incoming.id,
|
|
152
|
+
outgoingTakeId: activeTakeIdOf(outgoing),
|
|
153
|
+
incomingTakeId: activeTakeIdOf(incoming),
|
|
154
|
+
outgoingAssetId: outgoing.currentAssetId,
|
|
155
|
+
incomingAssetId: incoming.currentAssetId,
|
|
156
|
+
outgoingTransition: outgoing.transitionIn
|
|
157
|
+
? structuredClone(outgoing.transitionIn)
|
|
158
|
+
: undefined,
|
|
159
|
+
incomingTransition: incoming.transitionIn
|
|
160
|
+
? structuredClone(incoming.transitionIn)
|
|
161
|
+
: undefined,
|
|
162
|
+
outgoingStartMs: outgoing.startMs,
|
|
163
|
+
outgoingDurationMs: outgoing.durationMs,
|
|
164
|
+
incomingStartMs: incoming.startMs,
|
|
165
|
+
incomingDurationMs: incoming.durationMs,
|
|
166
|
+
cutTimeMs: incoming.startMs,
|
|
167
|
+
durationMs
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function assertFreshTransitionCandidate(candidate) {
|
|
171
|
+
const outgoing = clips.find((clip) => clip.id === candidate.source.outgoingClipId);
|
|
172
|
+
const incoming = clips.find((clip) => clip.id === candidate.source.incomingClipId);
|
|
173
|
+
if (!outgoing || !incoming) {
|
|
174
|
+
throw new Error("The generated transition candidate is stale because its selected clip no longer exists.");
|
|
175
|
+
}
|
|
176
|
+
if (isAmbiguousCut(outgoing, incoming)) {
|
|
177
|
+
throw new Error("The generated transition candidate is stale because the selected cut is ambiguous.");
|
|
178
|
+
}
|
|
179
|
+
const source = transitionSource(outgoing, incoming, candidate.source.durationMs);
|
|
180
|
+
if (JSON.stringify(source) !== JSON.stringify(candidate.source)) {
|
|
181
|
+
throw new Error("The generated transition candidate is stale because the selected clips or cut changed.");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function createTransitionCandidate(input) {
|
|
185
|
+
const outgoingClipId = input.outgoingClipId;
|
|
186
|
+
const incomingClipId = input.incomingClipId;
|
|
187
|
+
const outgoing = resolveClip(outgoingClipId);
|
|
188
|
+
const incoming = resolveClip(incomingClipId);
|
|
189
|
+
if (isAmbiguousCut(outgoing, incoming)) {
|
|
190
|
+
throw new Error("The selected cut is ambiguous. Choose two adjacent clips with no other clip crossing the cut.");
|
|
191
|
+
}
|
|
192
|
+
const planned = planTransitionAtCut(clips, {
|
|
193
|
+
outgoingClipId: outgoing.id,
|
|
194
|
+
incomingClipId: incoming.id,
|
|
195
|
+
durationMs: input.durationMs,
|
|
196
|
+
overlapMs: input.overlapMs,
|
|
197
|
+
type: input.type,
|
|
198
|
+
easing: input.easing,
|
|
199
|
+
color: input.color,
|
|
200
|
+
direction: input.direction,
|
|
201
|
+
softness: input.softness
|
|
202
|
+
});
|
|
203
|
+
if (!planned.ok) {
|
|
204
|
+
throw new Error(planned.error);
|
|
205
|
+
}
|
|
206
|
+
if (!generateTransition) {
|
|
207
|
+
throw new Error("Selected-cut transition generation is unavailable because this surface has no provider generator.");
|
|
208
|
+
}
|
|
209
|
+
const source = transitionSource(outgoing, incoming, planned.candidate.durationMs);
|
|
210
|
+
const request = {
|
|
211
|
+
durationMs: planned.candidate.durationMs,
|
|
212
|
+
overlapMs: planned.candidate.overlapMs,
|
|
213
|
+
type: planned.candidate.type,
|
|
214
|
+
easing: input.easing,
|
|
215
|
+
color: input.color,
|
|
216
|
+
direction: input.direction,
|
|
217
|
+
softness: input.softness
|
|
218
|
+
};
|
|
219
|
+
const generated = await generateTransition({
|
|
220
|
+
sequenceId,
|
|
221
|
+
source,
|
|
222
|
+
request
|
|
223
|
+
});
|
|
224
|
+
if (generated.generationId.trim().length === 0 ||
|
|
225
|
+
generated.assetId.trim().length === 0) {
|
|
226
|
+
throw new Error("Selected-cut transition generation returned no generation id or asset.");
|
|
227
|
+
}
|
|
228
|
+
const id = nextTransitionId();
|
|
229
|
+
const candidate = {
|
|
230
|
+
id,
|
|
231
|
+
generationId: generated.generationId,
|
|
232
|
+
kind: "generated_transition_at_cut",
|
|
233
|
+
status: "success",
|
|
234
|
+
asset: {
|
|
235
|
+
id: generated.assetId,
|
|
236
|
+
contentType: generated.contentType ?? "video/mp4"
|
|
237
|
+
},
|
|
238
|
+
source,
|
|
239
|
+
request,
|
|
240
|
+
operation: {
|
|
241
|
+
op: "apply_generated_transition_at_cut",
|
|
242
|
+
candidateId: id,
|
|
243
|
+
outgoingClipId: outgoing.id,
|
|
244
|
+
incomingClipId: incoming.id,
|
|
245
|
+
cutTimeMs: incoming.startMs,
|
|
246
|
+
durationMs: planned.candidate.durationMs,
|
|
247
|
+
undoable: true
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
transitionCandidates = [...transitionCandidates, candidate];
|
|
251
|
+
return candidate;
|
|
252
|
+
}
|
|
253
|
+
async function applyTransitionCandidate(candidateId) {
|
|
254
|
+
const candidate = transitionCandidates.find((entry) => entry.id === candidateId);
|
|
255
|
+
if (!candidate) {
|
|
256
|
+
throw new Error(`No generated transition candidate named "${candidateId}" exists.`);
|
|
257
|
+
}
|
|
258
|
+
assertFreshTransitionCandidate(candidate);
|
|
259
|
+
if (appliedTransitionCandidates.some((entry) => entry.id === candidate.id)) {
|
|
260
|
+
throw new Error(`Generated transition candidate "${candidate.id}" was already applied.`);
|
|
261
|
+
}
|
|
262
|
+
// Through the shared op, so a locked outgoing clip, its linked partners
|
|
263
|
+
// and its source length decide the growth the way they do in the editor.
|
|
264
|
+
const outcome = await applyTimelineOp(readState(), {
|
|
265
|
+
op: "apply_transition_at_cut",
|
|
266
|
+
outgoingClipId: candidate.source.outgoingClipId,
|
|
267
|
+
incomingClipId: candidate.source.incomingClipId,
|
|
268
|
+
...candidate.request
|
|
269
|
+
}, opContext);
|
|
270
|
+
if (outcome.error) {
|
|
271
|
+
throw new Error(outcome.error);
|
|
272
|
+
}
|
|
273
|
+
commitOutcome(outcome.state);
|
|
274
|
+
appliedTransitionCandidates = [
|
|
275
|
+
...appliedTransitionCandidates,
|
|
276
|
+
structuredClone(candidate)
|
|
277
|
+
];
|
|
278
|
+
if (auditionedTransitionCandidateId === candidate.id) {
|
|
279
|
+
auditionedTransitionCandidateId = undefined;
|
|
280
|
+
}
|
|
281
|
+
return {
|
|
282
|
+
candidate,
|
|
283
|
+
operation: candidate.operation,
|
|
284
|
+
description: `Apply generated transition candidate "${candidate.id}" to the selected cut as one undoable operation.`
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function addTrackInternal(type, name) {
|
|
288
|
+
const index = tracks.length;
|
|
289
|
+
const track = makeTrack({
|
|
290
|
+
id: nextTrackId(),
|
|
291
|
+
type,
|
|
292
|
+
name: name ?? `${capitalize(type)} ${index + 1}`,
|
|
293
|
+
index
|
|
294
|
+
});
|
|
295
|
+
if (type === "midi") {
|
|
296
|
+
// The track owns the voice, so a midi track without one plays nothing.
|
|
297
|
+
track.instrument = DEFAULT_MIDI_INSTRUMENT;
|
|
298
|
+
// Ticks are read against the tempo, so the first midi track is where a
|
|
299
|
+
// document that never had one gets it — written down rather than left
|
|
300
|
+
// implicit, so the next reader does not have to know the default.
|
|
301
|
+
tempo ??= DEFAULT_TEMPO;
|
|
302
|
+
}
|
|
303
|
+
tracks.push(track);
|
|
304
|
+
return track;
|
|
305
|
+
}
|
|
306
|
+
function findOrCreateTrack(type) {
|
|
307
|
+
const existing = tracks.find((t) => t.type === type);
|
|
308
|
+
if (existing) {
|
|
309
|
+
return existing;
|
|
310
|
+
}
|
|
311
|
+
return addTrackInternal(type);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The ids and names a caller could have used, for the message a failed
|
|
315
|
+
* lookup throws. Capped, because a long cut has hundreds of clips and an
|
|
316
|
+
* error listing all of them is one an agent stops reading.
|
|
317
|
+
*/
|
|
318
|
+
function validUnits(units, kind) {
|
|
319
|
+
if (units.length === 0) {
|
|
320
|
+
return `The timeline has no ${kind}s yet.`;
|
|
321
|
+
}
|
|
322
|
+
const shown = units
|
|
323
|
+
.slice(0, MAX_LISTED_UNITS)
|
|
324
|
+
.map((u) => `${u.id} ("${u.name}")`)
|
|
325
|
+
.join(", ");
|
|
326
|
+
const rest = units.length - MAX_LISTED_UNITS;
|
|
327
|
+
return rest > 0
|
|
328
|
+
? `Valid ${kind}s: ${shown}, and ${rest} more — call get_state for the full list.`
|
|
329
|
+
: `Valid ${kind}s: ${shown}.`;
|
|
330
|
+
}
|
|
331
|
+
function resolveClip(target) {
|
|
332
|
+
if (target.toLowerCase() === "selected") {
|
|
333
|
+
if (selectedClipIds.length !== 1) {
|
|
334
|
+
throw new Error(`"selected" requires exactly one selected clip (currently ${selectedClipIds.length}).`);
|
|
335
|
+
}
|
|
336
|
+
const clip = clips.find((c) => c.id === selectedClipIds[0]);
|
|
337
|
+
if (!clip) {
|
|
338
|
+
throw new Error("Selected clip no longer exists.");
|
|
339
|
+
}
|
|
340
|
+
return clip;
|
|
341
|
+
}
|
|
342
|
+
const byId = clips.find((c) => c.id === target);
|
|
343
|
+
if (byId) {
|
|
344
|
+
return byId;
|
|
345
|
+
}
|
|
346
|
+
if (isShortResourceId(target)) {
|
|
347
|
+
const matches = clips.filter((clip) => clip.id.startsWith(target));
|
|
348
|
+
if (matches.length === 1) {
|
|
349
|
+
return matches[0];
|
|
350
|
+
}
|
|
351
|
+
if (matches.length > 1) {
|
|
352
|
+
throw new Error(`Short clip id "${target}" matches more than one clip. Use the full id or name.`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const lower = target.toLowerCase();
|
|
356
|
+
const byName = clips.find((c) => c.name.toLowerCase() === lower);
|
|
357
|
+
if (byName) {
|
|
358
|
+
return byName;
|
|
359
|
+
}
|
|
360
|
+
throw new Error(`No clip found matching "${target}". ${validUnits(clips, "clip")}`);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* A unit by exact id or by an exact unique 12-character prefix — never by
|
|
364
|
+
* name. The AI edit tools take a durable destination, so a name match that
|
|
365
|
+
* could later point elsewhere is refused.
|
|
366
|
+
*/
|
|
367
|
+
function findByIdOrShortId(units, target, kind) {
|
|
368
|
+
const exact = units.find((unit) => unit.id === target);
|
|
369
|
+
if (exact || !isShortResourceId(target)) {
|
|
370
|
+
return exact;
|
|
371
|
+
}
|
|
372
|
+
const matches = units.filter((unit) => unit.id.startsWith(target));
|
|
373
|
+
if (matches.length > 1) {
|
|
374
|
+
throw new Error(`Short ${kind} id "${target}" matches more than one ${kind}. Use the full id.`);
|
|
375
|
+
}
|
|
376
|
+
return matches[0];
|
|
377
|
+
}
|
|
378
|
+
function serializeClip(c) {
|
|
379
|
+
const track = tracks.find((t) => t.id === c.trackId);
|
|
380
|
+
return {
|
|
381
|
+
id: c.id,
|
|
382
|
+
name: c.name,
|
|
383
|
+
trackId: c.trackId,
|
|
384
|
+
trackName: track?.name ?? null,
|
|
385
|
+
mediaType: c.mediaType,
|
|
386
|
+
sourceType: c.sourceType,
|
|
387
|
+
startMs: c.startMs,
|
|
388
|
+
durationMs: c.durationMs,
|
|
389
|
+
endMs: c.startMs + c.durationMs,
|
|
390
|
+
inPointMs: c.inPointMs,
|
|
391
|
+
outPointMs: c.outPointMs,
|
|
392
|
+
status: c.status,
|
|
393
|
+
currentAssetId: c.currentAssetId,
|
|
394
|
+
activeTakeId: activeTakeIdOf(c),
|
|
395
|
+
takeCount: c.versions?.length ?? 0,
|
|
396
|
+
prompt: c.prompt,
|
|
397
|
+
provider: c.provider,
|
|
398
|
+
model: c.model,
|
|
399
|
+
voice: c.voice,
|
|
400
|
+
animations: (c.animations ?? []).map((a) => ({
|
|
401
|
+
role: a.role,
|
|
402
|
+
preset: a.preset
|
|
403
|
+
})),
|
|
404
|
+
hidden: c.hidden ?? false,
|
|
405
|
+
muted: c.muted ?? false,
|
|
406
|
+
locked: c.locked,
|
|
407
|
+
opacity: c.opacity,
|
|
408
|
+
transform: c.transform,
|
|
409
|
+
textStyle: c.textStyle,
|
|
410
|
+
shapeStyle: c.shapeStyle,
|
|
411
|
+
captionStyle: c.caption?.style,
|
|
412
|
+
transitionIn: c.transitionIn,
|
|
413
|
+
mask: c.mask,
|
|
414
|
+
matte: c.matte,
|
|
415
|
+
timeRemap: c.timeRemap,
|
|
416
|
+
effects: c.effects,
|
|
417
|
+
parentId: c.parentId,
|
|
418
|
+
reframe: c.reframe
|
|
419
|
+
? {
|
|
420
|
+
mode: c.reframe.mode,
|
|
421
|
+
trackId: c.reframe.trackId,
|
|
422
|
+
safeMargin: c.reframe.safeMargin,
|
|
423
|
+
smoothing: c.reframe.smoothing,
|
|
424
|
+
sampleCount: c.reframe.samples?.length ?? 0,
|
|
425
|
+
keyframeCount: c.reframe.keyframes?.length ?? 0
|
|
426
|
+
}
|
|
427
|
+
: undefined,
|
|
428
|
+
// The notes themselves are the clip's bulk — a phrase is hundreds of
|
|
429
|
+
// them — so state reports how many there are and how many the window
|
|
430
|
+
// actually plays. `set_notes` sends the list; nothing reads it back.
|
|
431
|
+
noteCount: c.notes?.length,
|
|
432
|
+
audibleNoteCount: c.mediaType === "midi"
|
|
433
|
+
? visibleNotes(c, resolveTempo({ tempo }).bpm).length
|
|
434
|
+
: undefined,
|
|
435
|
+
// Where the phrase starts in musical time, so placing the next one is
|
|
436
|
+
// reading a bar number rather than dividing milliseconds by the tempo.
|
|
437
|
+
startBarsBeats: c.mediaType === "midi"
|
|
438
|
+
? formatBarsBeats(c.startMs, resolveTempo({ tempo }))
|
|
439
|
+
: undefined
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
// Seed from a real sequence when one was handed over, otherwise from the
|
|
443
|
+
// shorthand. Cloned, so the bridge never writes through to the caller's state.
|
|
444
|
+
if (seed) {
|
|
445
|
+
tracks.push(...structuredClone(seed.tracks));
|
|
446
|
+
clips.push(...structuredClone(seed.clips));
|
|
447
|
+
markers.push(...structuredClone(seed.markers ?? []));
|
|
448
|
+
mediaTracks.push(...structuredClone(seed.mediaTracks ?? []));
|
|
449
|
+
}
|
|
450
|
+
// Seed initial tracks and clips.
|
|
451
|
+
for (const t of seed ? [] : (initial.tracks ?? [])) {
|
|
452
|
+
addTrackInternal(t.type, t.name);
|
|
453
|
+
}
|
|
454
|
+
for (const c of seed ? [] : (initial.clips ?? [])) {
|
|
455
|
+
const track = tracks[c.trackIndex];
|
|
456
|
+
if (!track) {
|
|
457
|
+
throw new Error(`Initial clip "${c.name}" references trackIndex ${c.trackIndex}, but only ${tracks.length} track(s) exist.`);
|
|
458
|
+
}
|
|
459
|
+
clips.push(makeClip({
|
|
460
|
+
id: nextClipId(),
|
|
461
|
+
trackId: track.id,
|
|
462
|
+
name: c.name,
|
|
463
|
+
startMs: c.startMs,
|
|
464
|
+
durationMs: c.durationMs,
|
|
465
|
+
mediaType: c.mediaType ?? "video",
|
|
466
|
+
sourceType: "imported",
|
|
467
|
+
status: "generated"
|
|
468
|
+
}));
|
|
469
|
+
}
|
|
470
|
+
function readState() {
|
|
471
|
+
return {
|
|
472
|
+
fps,
|
|
473
|
+
width,
|
|
474
|
+
height,
|
|
475
|
+
tracks,
|
|
476
|
+
clips,
|
|
477
|
+
markers,
|
|
478
|
+
mediaTracks,
|
|
479
|
+
playheadMs,
|
|
480
|
+
selectedClipIds,
|
|
481
|
+
tempo,
|
|
482
|
+
setup,
|
|
483
|
+
transcript: seed?.transcript,
|
|
484
|
+
scriptEnabled: seed?.scriptEnabled,
|
|
485
|
+
templateId: seed?.templateId,
|
|
486
|
+
trackFolders: structuredClone(seed?.trackFolders),
|
|
487
|
+
storyboardMaterializations: structuredClone(seed?.storyboardMaterializations),
|
|
488
|
+
camera2d: structuredClone(seed?.camera2d ?? null)
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const generateFromBeats = async ({ model, provider, voice, music }) => {
|
|
492
|
+
const plan = setup?.beats ?? [];
|
|
493
|
+
if (plan.length === 0) {
|
|
494
|
+
throw new Error("There is no beat plan to generate from; call ui_timeline_plan_beats first.");
|
|
495
|
+
}
|
|
496
|
+
const videoTrack = findOrCreateTrack("video");
|
|
497
|
+
const wantsMusic = music ??
|
|
498
|
+
plan.some((beat) => beat.music === true);
|
|
499
|
+
const voiceTrack = voice !== undefined && plan.some((beat) => (beat.voiceover ?? "") !== "")
|
|
500
|
+
? findOrCreateTrack("audio")
|
|
501
|
+
: null;
|
|
502
|
+
const videoClipIds = [];
|
|
503
|
+
const voiceoverClipIds = [];
|
|
504
|
+
const planned = [];
|
|
505
|
+
let startMs = 0;
|
|
506
|
+
for (const [index, beat] of plan.entries()) {
|
|
507
|
+
const durationMs = Math.max(1, Math.round(beat.duration_ms));
|
|
508
|
+
const clip = makeClip({
|
|
509
|
+
id: nextClipId(),
|
|
510
|
+
trackId: videoTrack.id,
|
|
511
|
+
name: `Beat ${index + 1}`,
|
|
512
|
+
startMs,
|
|
513
|
+
durationMs,
|
|
514
|
+
mediaType: "video",
|
|
515
|
+
sourceType: "generated",
|
|
516
|
+
bindingKind: "text-to-video",
|
|
517
|
+
prompt: beat.prompt,
|
|
518
|
+
provider: provider,
|
|
519
|
+
model: model,
|
|
520
|
+
beatId: beat.id,
|
|
521
|
+
status: "draft"
|
|
522
|
+
});
|
|
523
|
+
if (beat.transition) {
|
|
524
|
+
clip.transitionIn = buildTransition({
|
|
525
|
+
type: beat.transition,
|
|
526
|
+
durationMs: 500
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
clips.push(clip);
|
|
530
|
+
videoClipIds.push(clip.id);
|
|
531
|
+
const line = (beat.voiceover ?? "").trim();
|
|
532
|
+
if (voiceTrack && line.length > 0) {
|
|
533
|
+
const voiceClip = makeClip({
|
|
534
|
+
id: nextClipId(),
|
|
535
|
+
trackId: voiceTrack.id,
|
|
536
|
+
name: `Beat ${index + 1} voiceover`,
|
|
537
|
+
startMs,
|
|
538
|
+
durationMs,
|
|
539
|
+
mediaType: "audio",
|
|
540
|
+
sourceType: "generated",
|
|
541
|
+
bindingKind: "text-to-audio",
|
|
542
|
+
prompt: line,
|
|
543
|
+
voice: voice,
|
|
544
|
+
beatId: beat.id,
|
|
545
|
+
status: "draft"
|
|
546
|
+
});
|
|
547
|
+
clips.push(voiceClip);
|
|
548
|
+
voiceoverClipIds.push(voiceClip.id);
|
|
549
|
+
}
|
|
550
|
+
planned.push({ ...beat, clip_id: clip.id });
|
|
551
|
+
startMs += durationMs;
|
|
552
|
+
}
|
|
553
|
+
let musicClipId = null;
|
|
554
|
+
if (wantsMusic) {
|
|
555
|
+
// One bed under the whole cut, never one per beat: overlapping music
|
|
556
|
+
// clips would play at once.
|
|
557
|
+
const musicTrack = findOrCreateTrack("audio");
|
|
558
|
+
const musicClip = makeClip({
|
|
559
|
+
id: nextClipId(),
|
|
560
|
+
trackId: musicTrack.id,
|
|
561
|
+
name: "Music",
|
|
562
|
+
startMs: 0,
|
|
563
|
+
durationMs: startMs,
|
|
564
|
+
mediaType: "audio",
|
|
565
|
+
sourceType: "generated",
|
|
566
|
+
bindingKind: "text-to-audio",
|
|
567
|
+
prompt: `Instrumental score under: ${setup?.brief ?? ""}`,
|
|
568
|
+
status: "draft"
|
|
569
|
+
});
|
|
570
|
+
clips.push(musicClip);
|
|
571
|
+
musicClipId = musicClip.id;
|
|
572
|
+
}
|
|
573
|
+
setup = {
|
|
574
|
+
stage: "done",
|
|
575
|
+
brief: setup?.brief ?? "",
|
|
576
|
+
format: setup?.format,
|
|
577
|
+
voiceover: setup?.voiceover,
|
|
578
|
+
beats: planned
|
|
579
|
+
};
|
|
580
|
+
return {
|
|
581
|
+
ok: true,
|
|
582
|
+
videoClipIds,
|
|
583
|
+
voiceoverClipIds,
|
|
584
|
+
musicClipId,
|
|
585
|
+
// Nothing renders headlessly: this authors the cut, and the clips are
|
|
586
|
+
// drafts until a renderer runs them.
|
|
587
|
+
startedClipIds: [],
|
|
588
|
+
note: "Clips created as drafts — this surface renders nothing."
|
|
589
|
+
};
|
|
590
|
+
};
|
|
591
|
+
const opContext = {
|
|
592
|
+
newId,
|
|
593
|
+
resolveAsset,
|
|
594
|
+
bakeAnimation,
|
|
595
|
+
bakeModel3DClip,
|
|
596
|
+
loadComposition,
|
|
597
|
+
parseSvgPath,
|
|
598
|
+
sequence: { id: sequenceId, name: sequenceName, projectId },
|
|
599
|
+
retargetFormat: async (sequence) => {
|
|
600
|
+
const stored = retargetFormat
|
|
601
|
+
? await retargetFormat(structuredClone(sequence))
|
|
602
|
+
: { sequenceId: sequence.id, name: sequence.name };
|
|
603
|
+
derivedSequences.push(structuredClone(sequence));
|
|
604
|
+
return { sequenceId: stored.sequenceId, name: stored.name };
|
|
605
|
+
},
|
|
606
|
+
generateFromBeats: async (_state, op) => {
|
|
607
|
+
const result = await generateFromBeats(op);
|
|
608
|
+
return { state: readState(), result, changedClipIds: [] };
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
function commitOutcome(next) {
|
|
612
|
+
tracks.splice(0, tracks.length, ...next.tracks);
|
|
613
|
+
clips = next.clips;
|
|
614
|
+
markers = next.markers;
|
|
615
|
+
mediaTracks = next.mediaTracks ?? [];
|
|
616
|
+
tempo = next.tempo;
|
|
617
|
+
setup = next.setup ?? null;
|
|
618
|
+
playheadMs = next.playheadMs;
|
|
619
|
+
selectedClipIds = next.selectedClipIds;
|
|
620
|
+
}
|
|
621
|
+
async function editOp(name, args) {
|
|
622
|
+
const outcome = await applyTimelineOp(readState(), timelineOpFromToolArgs(name, args), opContext);
|
|
623
|
+
if (outcome.error) {
|
|
624
|
+
throw new Error(outcome.error);
|
|
625
|
+
}
|
|
626
|
+
commitOutcome(outcome.state);
|
|
627
|
+
return name === "get_state"
|
|
628
|
+
? { sequenceId, ...outcome.result }
|
|
629
|
+
: outcome.result;
|
|
630
|
+
}
|
|
631
|
+
const tools = [
|
|
632
|
+
sharedTool("ui_timeline_get_state", async (args) => editOp("get_state", args)),
|
|
633
|
+
sharedTool("ui_timeline_add_track", async (args) => editOp("add_track", args)),
|
|
634
|
+
sharedTool("ui_timeline_move_track", async (args) => editOp("move_track", args)),
|
|
635
|
+
sharedTool("ui_timeline_add_text_clip", async (args) => editOp("add_text_clip", args)),
|
|
636
|
+
sharedTool("ui_timeline_delete_track", async (args) => editOp("delete_track", args)),
|
|
637
|
+
sharedTool("ui_timeline_add_media_clip", async (args) => editOp("add_media_clip", args)),
|
|
638
|
+
sharedTool("ui_timeline_add_shape_clip", async (args) => editOp("add_shape_clip", args)),
|
|
639
|
+
sharedTool("ui_timeline_add_model3d_clip", async (args) => editOp("add_model3d_clip", args)),
|
|
640
|
+
sharedTool("ui_timeline_set_model3d_style", async (args) => editOp("set_model3d_style", args)),
|
|
641
|
+
sharedTool("ui_timeline_bake_model3d_clip", async (args) => editOp("bake_model3d_clip", args)),
|
|
642
|
+
sharedTool("ui_timeline_generate_clip", async (args) => editOp("generate_clip", args)),
|
|
643
|
+
sharedTool("ui_timeline_get_clip_frames", async ({ target, timesMs, count, width }) => {
|
|
644
|
+
if (!initial.getClipFrames) {
|
|
645
|
+
throw new Error("This timeline host has no clip-frame renderer. Use a media-backed host.");
|
|
646
|
+
}
|
|
647
|
+
const clip = resolveClip(target);
|
|
648
|
+
const frames = await initial.getClipFrames(clip, {
|
|
649
|
+
timesMs: timesMs,
|
|
650
|
+
count: count,
|
|
651
|
+
width: width
|
|
652
|
+
});
|
|
653
|
+
return { ok: true, clip: serializeClip(clip), frames };
|
|
654
|
+
}),
|
|
655
|
+
sharedTool("ui_timeline_generatively_edit_clip", async ({ clip_id, instruction, provider, model }) => {
|
|
656
|
+
// The P0 request captures a durable destination. Unlike general
|
|
657
|
+
// timeline actions, this intentionally refuses names and selection.
|
|
658
|
+
const clip = findByIdOrShortId(clips, clip_id, "clip");
|
|
659
|
+
if (!clip) {
|
|
660
|
+
throw new Error(`No clip with id "${clip_id}" exists on this timeline. Call ui_timeline_get_state and pass the clip id.`);
|
|
661
|
+
}
|
|
662
|
+
const baseline = ensureBaselineTake(clip);
|
|
663
|
+
if (baseline !== clip) {
|
|
664
|
+
clips = clips.map((candidate) => candidate.id === clip.id ? baseline : candidate);
|
|
665
|
+
}
|
|
666
|
+
const source = captureMediaEditSourceContext(sequenceId, baseline);
|
|
667
|
+
if (!source.ok) {
|
|
668
|
+
throw new Error(source.error);
|
|
669
|
+
}
|
|
670
|
+
// Eval hosts have no selected model catalog. These deterministic
|
|
671
|
+
// placeholders only stand in for a real provider's returned asset;
|
|
672
|
+
// browser and server execution keep their normal model validation.
|
|
673
|
+
const resolvedProvider = provider ?? "headless";
|
|
674
|
+
const resolvedModel = model ?? "headless-video-to-video";
|
|
675
|
+
const request = createMediaEditRequest({
|
|
676
|
+
sourceContext: source.context,
|
|
677
|
+
instruction: instruction,
|
|
678
|
+
provider: resolvedProvider,
|
|
679
|
+
model: resolvedModel
|
|
680
|
+
});
|
|
681
|
+
const generated = initial.generateMediaEdit
|
|
682
|
+
? await initial.generateMediaEdit(request)
|
|
683
|
+
: { generationId: nextVersionId(), assetId: "" };
|
|
684
|
+
const generationId = generated.generationId;
|
|
685
|
+
const now = new Date().toISOString();
|
|
686
|
+
const next = composeGenerativeTakePatch(baseline, "restyle", {
|
|
687
|
+
assetId: generated.assetId || `generative://${baseline.id}/${generationId}`,
|
|
688
|
+
jobId: generationId,
|
|
689
|
+
createdAt: now,
|
|
690
|
+
workflowUpdatedAt: now,
|
|
691
|
+
provider: request.provider,
|
|
692
|
+
model: request.model,
|
|
693
|
+
prompt: request.instruction,
|
|
694
|
+
durationMs: request.sourceContext.timelineDurationMs,
|
|
695
|
+
mediaEdit: request
|
|
696
|
+
});
|
|
697
|
+
clips = clips.map((candidate) => candidate.id === baseline.id ? next : candidate);
|
|
698
|
+
const take = next.versions?.find((candidate) => candidate.id === generationId);
|
|
699
|
+
if (!take) {
|
|
700
|
+
throw new Error("The AI edit candidate could not be recorded.");
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
ok: true,
|
|
704
|
+
requestId: generationId,
|
|
705
|
+
generationId,
|
|
706
|
+
activeTakeId: activeTakeIdOf(next) ?? null,
|
|
707
|
+
candidate: {
|
|
708
|
+
id: take.id,
|
|
709
|
+
status: take.status,
|
|
710
|
+
source: take.source
|
|
711
|
+
},
|
|
712
|
+
clip: serializeClip(next)
|
|
713
|
+
};
|
|
714
|
+
}),
|
|
715
|
+
sharedTool("ui_timeline_apply_take", async ({ clip_id, take_id }) => {
|
|
716
|
+
const clip = findByIdOrShortId(clips, clip_id, "clip");
|
|
717
|
+
if (!clip) {
|
|
718
|
+
throw new Error(`No clip with id "${clip_id}" exists on this timeline. Call ui_timeline_get_state and pass the clip id.`);
|
|
719
|
+
}
|
|
720
|
+
if (clip.locked || tracks.find((t) => t.id === clip.trackId)?.locked) {
|
|
721
|
+
throw new Error(`Clip "${clip.name}" or its track is locked. Unlock it before editing.`);
|
|
722
|
+
}
|
|
723
|
+
const take = findByIdOrShortId(clip.versions ?? [], take_id, "take");
|
|
724
|
+
if (!take?.mediaEdit) {
|
|
725
|
+
throw new Error(`Take "${take_id}" is not an AI edit candidate for "${clip.name}". Apply only the candidate returned by ui_timeline_generatively_edit_clip.`);
|
|
726
|
+
}
|
|
727
|
+
const result = applyTakeToClip(clip, take.id);
|
|
728
|
+
if (result.error) {
|
|
729
|
+
throw new Error(result.error);
|
|
730
|
+
}
|
|
731
|
+
clips = clips.map((candidate) => candidate.id === clip.id ? result.clip : candidate);
|
|
732
|
+
return { ok: true, clip: serializeClip(result.clip) };
|
|
733
|
+
}),
|
|
734
|
+
sharedTool("ui_timeline_split_clip", async (args) => editOp("split_clip", args)),
|
|
735
|
+
sharedTool("ui_timeline_trim_clip", async (args) => editOp("trim_clip", args)),
|
|
736
|
+
sharedTool("ui_timeline_move_clip", async (args) => editOp("move_clip", args)),
|
|
737
|
+
sharedTool("ui_timeline_delete_clip", async (args) => editOp("delete_clip", args)),
|
|
738
|
+
sharedTool("ui_timeline_duplicate_clip", async (args) => editOp("duplicate_clip", args)),
|
|
739
|
+
sharedTool("ui_timeline_set_clip_params", async (args) => editOp("set_clip_params", args)),
|
|
740
|
+
sharedTool("ui_timeline_add_group", async (args) => editOp("add_group", args)),
|
|
741
|
+
sharedTool("ui_timeline_set_parent", async (args) => editOp("set_parent", args)),
|
|
742
|
+
sharedTool("ui_timeline_set_transition", async (args) => editOp("set_transition", args)),
|
|
743
|
+
tool("ui_timeline_generate_transition_at_cut", "Generate an inactive cut-level transition candidate for two explicitly adjacent clips. This does not change either clip or create a clip take. Preview the returned candidate before applying it.", generatedTransitionCandidateParams, async (args) => {
|
|
744
|
+
const candidate = await createTransitionCandidate(args);
|
|
745
|
+
return {
|
|
746
|
+
ok: true,
|
|
747
|
+
requestId: candidate.generationId,
|
|
748
|
+
generationId: candidate.generationId,
|
|
749
|
+
candidate,
|
|
750
|
+
source: candidate.source
|
|
751
|
+
};
|
|
752
|
+
}),
|
|
753
|
+
tool("ui_timeline_preview_transition_candidate", "Audition one generated cut-level transition candidate without changing the timeline document. Refuses candidates whose selected clips, source takes, or cut are stale.", transitionCandidateParams, async ({ candidate_id }) => {
|
|
754
|
+
const candidate = transitionCandidates.find((entry) => entry.id === candidate_id);
|
|
755
|
+
if (!candidate) {
|
|
756
|
+
throw new Error(`No generated transition candidate named "${candidate_id}" exists.`);
|
|
757
|
+
}
|
|
758
|
+
assertFreshTransitionCandidate(candidate);
|
|
759
|
+
auditionedTransitionCandidateId = candidate.id;
|
|
760
|
+
return { ok: true, previewOnly: true, candidate };
|
|
761
|
+
}),
|
|
762
|
+
tool("ui_timeline_apply_transition_at_cut", `${APPLY_TRANSITION_AT_CUT_DESCRIPTION} With clip ids and timing, this creates an inactive cut-level candidate. Apply a returned candidate_id explicitly to commit it as one undoable operation.`, transitionCandidateLifecycleParams, async (args) => {
|
|
763
|
+
if (args.candidate_id !== undefined) {
|
|
764
|
+
const applied = await applyTransitionCandidate(args.candidate_id);
|
|
765
|
+
return { ok: true, applied: true, ...applied };
|
|
766
|
+
}
|
|
767
|
+
const candidate = await createTransitionCandidate(args);
|
|
768
|
+
return {
|
|
769
|
+
ok: true,
|
|
770
|
+
applied: false,
|
|
771
|
+
requestId: candidate.generationId,
|
|
772
|
+
generationId: candidate.generationId,
|
|
773
|
+
candidate,
|
|
774
|
+
source: candidate.source,
|
|
775
|
+
next: "Preview with ui_timeline_preview_transition_candidate, then call this tool with candidate_id to apply."
|
|
776
|
+
};
|
|
777
|
+
}),
|
|
778
|
+
sharedTool("ui_timeline_set_mask", async (args) => editOp("set_mask", args)),
|
|
779
|
+
sharedTool("ui_timeline_set_matte", async (args) => editOp("set_matte", args)),
|
|
780
|
+
// Headless-only: the inspector's own controls write these knobs into the
|
|
781
|
+
// store. Both this and the ops module's `set_generated_matte` reach
|
|
782
|
+
// `selectGeneratedMatteVersion`, so the version list cannot fork.
|
|
783
|
+
tool("ui_timeline_set_generated_matte", SET_GENERATED_MATTE_DESCRIPTION, setGeneratedMatteParams, async (args) => editOp("set_generated_matte", args)),
|
|
784
|
+
sharedTool("ui_timeline_set_time_remap", async (args) => editOp("set_time_remap", args)),
|
|
785
|
+
sharedTool("ui_timeline_set_effects", async (args) => editOp("set_effects", args)),
|
|
786
|
+
sharedTool("ui_timeline_set_clip_binding", async (args) => editOp("set_clip_binding", args)),
|
|
787
|
+
sharedTool("ui_timeline_animate_clip", async (args) => editOp("animate_clip", args)),
|
|
788
|
+
// Headless-only: the browser writes curves through the inspector, not
|
|
789
|
+
// through an agent call. Both this and the ops module's `set_baked_animation`
|
|
790
|
+
// go through `buildBakedAnimation`, so the replace rule cannot fork.
|
|
791
|
+
tool("ui_timeline_set_baked_animation", SET_BAKED_ANIMATION_DESCRIPTION, setBakedAnimationParams, async (args) => editOp("set_baked_animation", args)),
|
|
792
|
+
sharedTool("ui_timeline_stagger_animations", async (args) => editOp("stagger_animations", args)),
|
|
793
|
+
sharedTool("ui_timeline_clear_animations", async (args) => editOp("clear_animations", args)),
|
|
794
|
+
sharedTool("ui_timeline_list_animation_presets", async (args) => editOp("list_animation_presets", args)),
|
|
795
|
+
sharedTool("ui_timeline_select_clip", async (args) => editOp("select_clip", args)),
|
|
796
|
+
sharedTool("ui_timeline_seek", async (args) => editOp("seek", args)),
|
|
797
|
+
sharedTool("ui_timeline_add_marker", async (args) => editOp("add_marker", args)),
|
|
798
|
+
// ── Guided video flow (PRD § 8.6) ───────────────────────────────────
|
|
799
|
+
sharedTool("ui_timeline_set_setup", async (args) => editOp("set_setup", args)),
|
|
800
|
+
sharedTool("ui_timeline_plan_beats", async (args) => editOp("plan_beats", args)),
|
|
801
|
+
sharedTool("ui_timeline_update_beat", async (args) => editOp("update_beat", args)),
|
|
802
|
+
sharedTool("ui_timeline_remove_beat", async (args) => editOp("remove_beat", args)),
|
|
803
|
+
sharedTool("ui_timeline_generate_from_beats", async (args) => editOp("generate_from_beats", args)),
|
|
804
|
+
sharedTool("ui_timeline_delete_marker", async (args) => editOp("delete_marker", args)),
|
|
805
|
+
sharedTool("ui_timeline_set_markers_from_beats", async (args) => editOp("set_markers_from_beats", args)),
|
|
806
|
+
sharedTool("ui_timeline_snap_to_beats", async (args) => editOp("snap_to_beats", args)),
|
|
807
|
+
// ── MIDI ────────────────────────────────────────────────────────────
|
|
808
|
+
// Notes live on the clip, the instrument on the track, and the tempo on
|
|
809
|
+
// the document, so each gets its own op.
|
|
810
|
+
sharedTool("ui_timeline_add_midi_clip", async (args) => editOp("add_midi_clip", args)),
|
|
811
|
+
sharedTool("ui_timeline_set_notes", async (args) => editOp("set_notes", args)),
|
|
812
|
+
sharedTool("ui_timeline_set_tempo", async (args) => editOp("set_tempo", args)),
|
|
813
|
+
sharedTool("ui_timeline_set_track_instrument", async (args) => editOp("set_track_instrument", args)),
|
|
814
|
+
// The note edits. Each runs the whole list through one pure function from
|
|
815
|
+
// `@nodetool-ai/timeline` and replaces it — the ids survive, so a
|
|
816
|
+
// selection and an undo in the editor still point at the same notes.
|
|
817
|
+
sharedTool("ui_timeline_transpose_clip", async (args) => editOp("transpose_clip", args)),
|
|
818
|
+
sharedTool("ui_timeline_quantize_notes", async (args) => editOp("quantize_notes", args)),
|
|
819
|
+
sharedTool("ui_timeline_scale_velocity", async (args) => editOp("scale_velocity", args)),
|
|
820
|
+
tool("ui_timeline_insert_composition", "Insert a stored composition — a group of clips with named parameters (a lower third, a title card, a callout) — at a timecode. Its children keep the layering the template declares: each template track becomes an overlay track of its own, front-most on top, reused across insertions. `params` overrides the template's defaults by name; anything omitted keeps its default. Call list_compositions for the ids and their parameters.", z.object({
|
|
821
|
+
composition_id: z
|
|
822
|
+
.string()
|
|
823
|
+
.describe("Composition id, from list_compositions."),
|
|
824
|
+
startMs: z
|
|
825
|
+
.number()
|
|
826
|
+
.describe("Where the group starts on the timeline, in ms."),
|
|
827
|
+
trackId: z
|
|
828
|
+
.string()
|
|
829
|
+
.optional()
|
|
830
|
+
.describe("Track for the group clip itself. Its children still get their own overlay tracks. Defaults to an overlay track."),
|
|
831
|
+
params: z
|
|
832
|
+
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
|
|
833
|
+
.optional()
|
|
834
|
+
.describe('Parameter overrides by name, e.g. {name: "Ada Lovelace"}. A name the template does not declare, or a value of the wrong type, is refused.')
|
|
835
|
+
}), async (args) => editOp("insert_composition", args)),
|
|
836
|
+
// Headless-only: the editor's ClipVersionHistory panel writes take
|
|
837
|
+
// switches, renames and deletes straight into TimelineStore via
|
|
838
|
+
// restoreVersion/renameTake/deleteTake. This op reaches the same
|
|
839
|
+
// `selectTake`/`renameTake`/`deleteTake` helpers, so the take list cannot
|
|
840
|
+
// fork between the two hosts (P0 AI Video, PRD § 8.10).
|
|
841
|
+
tool("ui_timeline_list_takes", "List a clip's take history (clip.versions): id, label, source, " +
|
|
842
|
+
"provider, model, createdAt, costCredits, durationMs, status, " +
|
|
843
|
+
"favorite, and which one is active.", z.object({ target: z.string().describe("Clip id or name.") }), async (args) => editOp("list_takes", args)),
|
|
844
|
+
tool("ui_timeline_select_take", "Make a stored take current on a clip — the same switch " +
|
|
845
|
+
"restoreVersion does in the editor. Refuses a take that is not " +
|
|
846
|
+
'`status: "success"`.', z.object({
|
|
847
|
+
target: z.string().describe("Clip id or name."),
|
|
848
|
+
takeId: z.string().describe("A take id, from list_takes.")
|
|
849
|
+
}), async (args) => editOp("select_take", args)),
|
|
850
|
+
tool("ui_timeline_rename_take", "Set a take's display label.", z.object({
|
|
851
|
+
target: z.string().describe("Clip id or name."),
|
|
852
|
+
takeId: z.string().describe("A take id, from list_takes."),
|
|
853
|
+
label: z.string().describe("The new display label.")
|
|
854
|
+
}), async (args) => editOp("rename_take", args)),
|
|
855
|
+
tool("ui_timeline_delete_take", "Remove a take from a clip's history. Refuses when `takeId` is the " +
|
|
856
|
+
"active take — select_take a different take first, then delete the " +
|
|
857
|
+
"one you no longer want; a clip must always keep at least one take.", z.object({
|
|
858
|
+
target: z.string().describe("Clip id or name."),
|
|
859
|
+
takeId: z.string().describe("A take id, from list_takes.")
|
|
860
|
+
}), async (args) => editOp("delete_take", args)),
|
|
861
|
+
// Subject/object tracks (P0 AI Video, Phase 2). The async provider call
|
|
862
|
+
// that fills a track's samples is the `track_object` capability, not an
|
|
863
|
+
// op — these four are the structural half over it, and they carry the
|
|
864
|
+
// same semantics as `applyTimelineOp`'s own cases so the two hosts cannot
|
|
865
|
+
// fork (`packages/timeline/src/ops/apply.ts`).
|
|
866
|
+
tool("ui_timeline_list_tracks", "List the document's subject/object tracks — id, clipId, name, kind, " +
|
|
867
|
+
"status, source window and sample count. The samples themselves are " +
|
|
868
|
+
"not returned: a track carries one per frame it covers, which is a " +
|
|
869
|
+
"wall of numbers no caller reads through a listing.", z.object({
|
|
870
|
+
target: z
|
|
871
|
+
.string()
|
|
872
|
+
.optional()
|
|
873
|
+
.describe("A clip id or name to filter by. Omit for every track.")
|
|
874
|
+
}), async (args) => editOp("list_tracks", args)),
|
|
875
|
+
tool("ui_timeline_delete_track_object", "Delete a subject/object track and unbind every clip that was " +
|
|
876
|
+
"following it — a clip left pointing at a track that is gone reads " +
|
|
877
|
+
"as bound while following nothing.", z.object({
|
|
878
|
+
trackId: z.string().describe("A track id, from list_tracks.")
|
|
879
|
+
}), async (args) => editOp("delete_track_object", args)),
|
|
880
|
+
tool("ui_timeline_bind_to_track", "Make a clip follow a subject/object track, so its position moves " +
|
|
881
|
+
'with the tracked subject. Only "position" and "position_scale" are ' +
|
|
882
|
+
"implemented; the other modes are named by the document format for a " +
|
|
883
|
+
"later phase and are refused here rather than silently doing nothing.", z.object({
|
|
884
|
+
target: z.string().describe("Clip id or name."),
|
|
885
|
+
trackId: z.string().describe("A track id, from list_tracks."),
|
|
886
|
+
mode: z.string().describe('"position" or "position_scale".'),
|
|
887
|
+
offset: z
|
|
888
|
+
.object({ x: z.number(), y: z.number() })
|
|
889
|
+
.optional()
|
|
890
|
+
.describe("Canvas-pixel offset added on top of the tracked point."),
|
|
891
|
+
scale: z
|
|
892
|
+
.number()
|
|
893
|
+
.optional()
|
|
894
|
+
.describe('Scale multiplier, "position_scale" only.'),
|
|
895
|
+
rotationOffset: z
|
|
896
|
+
.number()
|
|
897
|
+
.optional()
|
|
898
|
+
.describe("Radians added to the clip's rotation."),
|
|
899
|
+
smoothing: z
|
|
900
|
+
.number()
|
|
901
|
+
.optional()
|
|
902
|
+
.describe("0..1; higher follows the track more loosely.")
|
|
903
|
+
}), async (args) => editOp("bind_to_track", args)),
|
|
904
|
+
tool("ui_timeline_unbind_track", "Stop a clip following a track. A clip that follows none is a no-op.", z.object({ target: z.string().describe("Clip id or name.") }), async (args) => editOp("unbind_track", args)),
|
|
905
|
+
sharedTool("ui_timeline_retarget_format", async (args) => editOp("retarget_format", args)),
|
|
906
|
+
sharedTool("ui_timeline_set_reframe_subject", async (args) => editOp("set_reframe_subject", args)),
|
|
907
|
+
sharedTool("ui_timeline_add_reframe_keyframe", async (args) => editOp("add_reframe_keyframe", args)),
|
|
908
|
+
sharedTool("ui_timeline_clear_reframe", async (args) => editOp("clear_reframe", args))
|
|
909
|
+
];
|
|
910
|
+
// Opt-in, and outside the `ui_timeline_` prefix on purpose: `edit_timeline`
|
|
911
|
+
// builds this same bridge and matches its ops by that prefix, so a preview
|
|
912
|
+
// tool it can never call has no business in its op catalogue. A case that
|
|
913
|
+
// grades "did it look at the frame" asks for it.
|
|
914
|
+
if (initial.preview) {
|
|
915
|
+
tools.push(tool("preview_timeline_frame", "Look at the sequence at one or more timecodes: what is on screen, layered top of the stack first, with each layer's opacity and track index. This surface has no rasterizer, so it reports the layer stack rather than pixels — enough to see a layer that is covered, missing, or drawing nothing. Call it after an edit, before you say you are done.", z.object({
|
|
916
|
+
times_ms: z
|
|
917
|
+
.array(z.number().nonnegative())
|
|
918
|
+
.min(1)
|
|
919
|
+
.max(8)
|
|
920
|
+
.describe("Absolute timeline positions to look at, in ms.")
|
|
921
|
+
}), async ({ times_ms }) => {
|
|
922
|
+
const frames = times_ms.map((timeMs) => ({
|
|
923
|
+
time_ms: timeMs,
|
|
924
|
+
layers: computeActiveLayers(tracks, clips, timeMs)
|
|
925
|
+
.map((layer) => ({
|
|
926
|
+
clip_id: layer.clipId,
|
|
927
|
+
clip_name: clips.find((c) => c.id === layer.clipId)?.name ??
|
|
928
|
+
layer.clipId,
|
|
929
|
+
kind: layer.kind,
|
|
930
|
+
track_index: layer.trackIndex,
|
|
931
|
+
z_index: 1000 - layer.trackIndex,
|
|
932
|
+
opacity: layer.opacity,
|
|
933
|
+
text: layer.textStyle?.text
|
|
934
|
+
}))
|
|
935
|
+
// Top of the stack first, the order the skill's report describes.
|
|
936
|
+
.sort((a, b) => b.z_index - a.z_index)
|
|
937
|
+
}));
|
|
938
|
+
previewTimesMs.push(...times_ms);
|
|
939
|
+
for (const frame of frames) {
|
|
940
|
+
for (const layer of frame.layers) {
|
|
941
|
+
previewedLayerKinds.add(layer.kind);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return { ok: true, width, height, frames };
|
|
945
|
+
}));
|
|
946
|
+
}
|
|
947
|
+
// Recorded rather than counted: a predicate asks where the last edit sits
|
|
948
|
+
// relative to a preview, which a tally cannot answer. The push happens
|
|
949
|
+
// before the call, so a tool that throws is still in the transcript.
|
|
950
|
+
let pending = Promise.resolve();
|
|
951
|
+
const recorded = tools.map((entry) => ({
|
|
952
|
+
...entry,
|
|
953
|
+
execute: (args) => {
|
|
954
|
+
toolLog.push(entry.name);
|
|
955
|
+
const parsed = parseWithTypeCoercion(entry.parameters, args);
|
|
956
|
+
if (!isRecordValue(parsed)) {
|
|
957
|
+
throw new Error("Timeline tool arguments must be an object.");
|
|
958
|
+
}
|
|
959
|
+
const result = pending.then(() => entry.execute(parsed));
|
|
960
|
+
pending = result.catch(() => undefined);
|
|
961
|
+
return result;
|
|
962
|
+
}
|
|
963
|
+
}));
|
|
964
|
+
return {
|
|
965
|
+
tools: recorded,
|
|
966
|
+
finalState: () => ({
|
|
967
|
+
fps,
|
|
968
|
+
width,
|
|
969
|
+
height,
|
|
970
|
+
durationMs: clips.reduce((m, c) => Math.max(m, c.startMs + c.durationMs), 0),
|
|
971
|
+
playheadMs,
|
|
972
|
+
tracks: tracks.map((t) => ({
|
|
973
|
+
id: t.id,
|
|
974
|
+
name: t.name,
|
|
975
|
+
type: t.type,
|
|
976
|
+
index: t.index
|
|
977
|
+
})),
|
|
978
|
+
clips: clips.map((c) => ({
|
|
979
|
+
id: c.id,
|
|
980
|
+
name: c.name,
|
|
981
|
+
trackId: c.trackId,
|
|
982
|
+
mediaType: c.mediaType,
|
|
983
|
+
startMs: c.startMs,
|
|
984
|
+
durationMs: c.durationMs,
|
|
985
|
+
prompt: c.prompt,
|
|
986
|
+
animations: (c.animations ?? []).map((a) => ({
|
|
987
|
+
role: a.role,
|
|
988
|
+
preset: a.preset
|
|
989
|
+
}))
|
|
990
|
+
})),
|
|
991
|
+
documentTracks: tracks.map((t) => structuredClone(t)),
|
|
992
|
+
documentClips: clips.map((c) => structuredClone(c)),
|
|
993
|
+
markers: markers.map((m) => structuredClone(m)),
|
|
994
|
+
mediaTracks: mediaTracks.map((t) => structuredClone(t)),
|
|
995
|
+
derivedSequences: derivedSequences.map((sequence) => structuredClone(sequence)),
|
|
996
|
+
tempo: tempo ? structuredClone(tempo) : undefined,
|
|
997
|
+
camera2d: structuredClone(seed?.camera2d ?? null),
|
|
998
|
+
setup: setup ? structuredClone(setup) : null,
|
|
999
|
+
transitionCandidates: structuredClone(transitionCandidates),
|
|
1000
|
+
auditionedTransitionCandidateId,
|
|
1001
|
+
appliedTransitionCandidates: structuredClone(appliedTransitionCandidates),
|
|
1002
|
+
toolLog: [...toolLog],
|
|
1003
|
+
previewTimesMs: [...previewTimesMs],
|
|
1004
|
+
previewedLayerKinds: [...previewedLayerKinds]
|
|
1005
|
+
})
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
//# sourceMappingURL=timeline-bridge.js.map
|