@nodetool-ai/agents 0.8.1 → 0.8.2
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/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/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/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-source.js +1 -1
- package/dist/capabilities/game-asset-source.js.map +1 -1
- package/dist/capabilities/game.d.ts +14 -2
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +209 -39
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.d.ts +2 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -1
- package/dist/capabilities/game.specs.js +25 -11
- 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 +433 -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 +7 -2
- 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.d.ts +23 -24
- package/dist/capabilities/lazy-tool.d.ts.map +1 -1
- package/dist/capabilities/lazy-tool.js +29 -35
- 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 +301 -260
- package/dist/capabilities/media.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 +2 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +28 -0
- 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/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 +2 -18
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +315 -94
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts +3 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +42 -3
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/timeline-bridge.d.ts +359 -0
- package/dist/capabilities/timeline-bridge.d.ts.map +1 -0
- package/dist/capabilities/timeline-bridge.js +1016 -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/timelines.d.ts +64 -4
- package/dist/capabilities/timelines.d.ts.map +1 -1
- package/dist/capabilities/timelines.js +566 -67
- 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 +173 -8
- 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/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +53 -26
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +7 -1
- 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/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/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 +4737 -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-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 +872 -16
- 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/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -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 +2 -0
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +56 -11
- 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.map +1 -1
- package/dist/tools/builtin-tools.js +13 -0
- 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 +24 -22
- 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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { finishStoryboard, layoutStoryboard, previewStoryboardDesign } from "./finish-storyboard.js";
|
|
1
2
|
/**
|
|
2
3
|
* The `storyboards` capability module.
|
|
3
4
|
*
|
|
@@ -15,10 +16,15 @@
|
|
|
15
16
|
*
|
|
16
17
|
* Design: docs/tool-class-retirement-design.md § "Migration".
|
|
17
18
|
*/
|
|
18
|
-
import {
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
21
|
+
import { mp4DurationSeconds, withSpan } from "@nodetool-ai/runtime";
|
|
22
|
+
import { storyboardMotionDesign, storyboardShotGraphics } from "@nodetool-ai/protocol/api-schemas/storyboards.js";
|
|
23
|
+
import { assertProductionGenerationAllowed, isFullResourceId, isShortResourceId, productionRequirement } from "@nodetool-ai/protocol";
|
|
19
24
|
import { stampScriptStoryboardId } from "./script-link.js";
|
|
20
25
|
import { listStoryboardsSpec, createStoryboardSpec, getStoryboardSpec, renderStoryboardStillsSpec, renderStoryboardClipsSpec, reviseStoryboardClipSpec, assembleStoryboardTimelineSpec, editStoryboardSpec, directStoryboardSpec, extractScriptFromStoryboardSpec, deleteStoryboardSpec } from "./storyboards.specs.js";
|
|
21
|
-
import {
|
|
26
|
+
import { measureStoryboardSources } from "./storyboard-measured-sources.js";
|
|
27
|
+
import { clampConcurrency } from "./concurrency.js";
|
|
22
28
|
import { isNonBlankString, isNumber, isRecord, isString } from "../utils/type-guards.js";
|
|
23
29
|
import { resolveProjectId } from "./project-scope.js";
|
|
24
30
|
/** Shots one call may render, so a stray `targets: "all"` cannot bankrupt a run. */
|
|
@@ -323,7 +329,8 @@ const listStoryboards = {
|
|
|
323
329
|
with_keyframe: doc.shots.filter((s) => !!s.keyframe).length,
|
|
324
330
|
with_clip: doc.shots.filter((s) => !!s.clip).length,
|
|
325
331
|
timeline_id: row.timeline_id ?? undefined,
|
|
326
|
-
updated_at: row.updated_at
|
|
332
|
+
updated_at: row.updated_at,
|
|
333
|
+
revision: row.revision
|
|
327
334
|
};
|
|
328
335
|
})
|
|
329
336
|
};
|
|
@@ -342,7 +349,8 @@ function createdBoardSummary(row) {
|
|
|
342
349
|
name: row.name,
|
|
343
350
|
project_id: row.project_id,
|
|
344
351
|
shots: doc.shots.length,
|
|
345
|
-
updated_at: row.updated_at
|
|
352
|
+
updated_at: row.updated_at,
|
|
353
|
+
revision: row.revision
|
|
346
354
|
};
|
|
347
355
|
}
|
|
348
356
|
const createStoryboard = {
|
|
@@ -408,6 +416,7 @@ const getStoryboard = {
|
|
|
408
416
|
const drifted = new Set(link.drifted_shot_ids);
|
|
409
417
|
return {
|
|
410
418
|
id: row.id,
|
|
419
|
+
revision: row.revision,
|
|
411
420
|
name: row.name,
|
|
412
421
|
brief: doc.brief,
|
|
413
422
|
style: doc.style,
|
|
@@ -418,6 +427,7 @@ const getStoryboard = {
|
|
|
418
427
|
timeline_id: row.timeline_id ?? undefined,
|
|
419
428
|
narration: doc.screenplay?.narration ?? undefined,
|
|
420
429
|
music_prompt: doc.screenplay?.music_prompt ?? undefined,
|
|
430
|
+
motion_design: doc.screenplay?.motion_design ?? null,
|
|
421
431
|
script_id: link.script_id,
|
|
422
432
|
script_link: link,
|
|
423
433
|
shots: [...doc.shots]
|
|
@@ -429,6 +439,8 @@ const getStoryboard = {
|
|
|
429
439
|
action: shot.action,
|
|
430
440
|
camera: shot.camera,
|
|
431
441
|
motion: shot.motion,
|
|
442
|
+
graphics: shot.graphics,
|
|
443
|
+
production: shot.production,
|
|
432
444
|
duration_seconds: shot.duration_seconds,
|
|
433
445
|
status: shot.status,
|
|
434
446
|
has_keyframe: !!shot.keyframe,
|
|
@@ -531,6 +543,71 @@ async function filterStale(selected, params, doc, entities, kind) {
|
|
|
531
543
|
.map((shot) => shot.id)
|
|
532
544
|
};
|
|
533
545
|
}
|
|
546
|
+
function modelOverrides(params) {
|
|
547
|
+
const options = {};
|
|
548
|
+
if (isNonBlankString(params["provider"])) {
|
|
549
|
+
options.provider = params["provider"];
|
|
550
|
+
}
|
|
551
|
+
if (isNonBlankString(params["model"])) {
|
|
552
|
+
options.model = params["model"];
|
|
553
|
+
}
|
|
554
|
+
return options;
|
|
555
|
+
}
|
|
556
|
+
function missingPlanModel(plan) {
|
|
557
|
+
if (plan.model.provider && plan.model.model) {
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
error: `No ${plan.kind === "keyframe" ? "still" : "clip"} model is set for shot ${plan.shotId}. Pass provider + model (use find_model), or set one on the shot or board.`
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
async function clipModelError(context, plans) {
|
|
565
|
+
const groups = new Map();
|
|
566
|
+
for (const plan of plans) {
|
|
567
|
+
const error = missingPlanModel(plan);
|
|
568
|
+
if (error) {
|
|
569
|
+
return error;
|
|
570
|
+
}
|
|
571
|
+
if (plan.preflightError) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const key = JSON.stringify([plan.model.provider, plan.model.model]);
|
|
575
|
+
const group = groups.get(key) ?? [];
|
|
576
|
+
group.push(plan);
|
|
577
|
+
groups.set(key, group);
|
|
578
|
+
}
|
|
579
|
+
for (const group of groups.values()) {
|
|
580
|
+
const model = group[0].model;
|
|
581
|
+
let supportedTasks = model.supportedTasks;
|
|
582
|
+
try {
|
|
583
|
+
const provider = await context.getProvider(model.provider);
|
|
584
|
+
const models = await provider.getAvailableVideoModels();
|
|
585
|
+
supportedTasks =
|
|
586
|
+
models.find((entry) => entry.id === model.model)?.supportedTasks ??
|
|
587
|
+
supportedTasks;
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
// Legacy providers can lack discovery, so their persisted task declarations apply.
|
|
591
|
+
}
|
|
592
|
+
const unsupported = group.find((plan) => {
|
|
593
|
+
const task = plan.mode === "direct" ? "text_to_video" : plan.mode === "reference" ? "reference_to_video" : "image_to_video";
|
|
594
|
+
return supportedTasks?.length && !supportedTasks.includes(task);
|
|
595
|
+
});
|
|
596
|
+
if (unsupported) {
|
|
597
|
+
const task = unsupported.mode === "direct" ? "text_to_video" : unsupported.mode === "reference" ? "reference_to_video" : "image_to_video";
|
|
598
|
+
return {
|
|
599
|
+
error: `The selected clip model ${model.provider}/${model.model} does not support ${task}; choose a model that supports the selected shot mode.`
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return undefined;
|
|
604
|
+
}
|
|
605
|
+
function uniformModel(plans) {
|
|
606
|
+
const model = plans[0]?.model;
|
|
607
|
+
return model && plans.every((plan) => plan.model.provider === model.provider && plan.model.model === model.model)
|
|
608
|
+
? { provider: model.provider, model: model.model }
|
|
609
|
+
: {};
|
|
610
|
+
}
|
|
534
611
|
const renderStoryboardStills = {
|
|
535
612
|
spec: renderStoryboardStillsSpec,
|
|
536
613
|
impl: async (run, params) => {
|
|
@@ -539,14 +616,11 @@ const renderStoryboardStills = {
|
|
|
539
616
|
if (isError(board))
|
|
540
617
|
return board;
|
|
541
618
|
const { row, doc } = board;
|
|
542
|
-
const model = resolveModel(params, doc.imageModel, "still", "text_to_image");
|
|
543
|
-
if (isError(model))
|
|
544
|
-
return model;
|
|
545
619
|
const { shotRenderMode } = await import("@nodetool-ai/protocol");
|
|
546
620
|
// A direct shot renders its clip from the prompt, so it needs no still.
|
|
547
621
|
// Naming it in `targets` still renders one — a board frame to look at is
|
|
548
622
|
// worth having even when the clip does not come from it.
|
|
549
|
-
const selected = selectShots(doc.shots, params["targets"], (s) => !s.keyframe &&
|
|
623
|
+
const selected = selectShots(doc.shots, params["targets"], (s) => (params["stale_only"] === true ? !!s.keyframe : !s.keyframe) &&
|
|
550
624
|
shotRenderMode(s) !== "direct" &&
|
|
551
625
|
!shotHasPicture(s, doc.shots));
|
|
552
626
|
if (isError(selected))
|
|
@@ -565,6 +639,10 @@ const renderStoryboardStills = {
|
|
|
565
639
|
: "No shot needs a still: each already has one, or renders its clip directly."
|
|
566
640
|
};
|
|
567
641
|
}
|
|
642
|
+
const protectedStill = chosen.find((shot) => shot.production?.protected_inputs?.some((input) => input.asset_id));
|
|
643
|
+
if (protectedStill) {
|
|
644
|
+
return { error: `Shot ${protectedStill.id} protects source assets; the current still renderer cannot prove source fidelity. Use those assets directly in Timeline materialization or a policy-aware finishing path.` };
|
|
645
|
+
}
|
|
568
646
|
if (chosen.length > MAX_SHOTS_PER_CALL) {
|
|
569
647
|
return {
|
|
570
648
|
error: `${chosen.length} shots selected, over the ${MAX_SHOTS_PER_CALL}-shot per-call limit. Pass targets in batches.`
|
|
@@ -575,22 +653,34 @@ const renderStoryboardStills = {
|
|
|
575
653
|
// says: `style` and `model` can be overridden per call. Recording the
|
|
576
654
|
// override is the point — a version rendered against something other than
|
|
577
655
|
// the board's settings reads stale against the board, correctly.
|
|
578
|
-
const planOptions =
|
|
579
|
-
provider: model.provider,
|
|
580
|
-
model: model.model
|
|
581
|
-
};
|
|
656
|
+
const planOptions = modelOverrides(params);
|
|
582
657
|
if (isString(params["style"])) {
|
|
583
658
|
planOptions.style = params["style"];
|
|
584
659
|
}
|
|
585
660
|
const plans = planShotRenders(doc, entities, "keyframe", chosen.map((shot) => shot.id), planOptions);
|
|
586
|
-
const
|
|
587
|
-
|
|
661
|
+
const { imageModelSupportsTask } = await import("@nodetool-ai/runtime");
|
|
662
|
+
for (const plan of plans) {
|
|
663
|
+
const modelError = missingPlanModel(plan);
|
|
664
|
+
if (modelError) {
|
|
665
|
+
return modelError;
|
|
666
|
+
}
|
|
667
|
+
plan.stillModelTakesImages = await imageModelSupportsTask(context, plan.model, "image_to_image", plan.model.supportedTasks);
|
|
668
|
+
}
|
|
669
|
+
const outcomes = await withSpan("render", {
|
|
670
|
+
"document.id": row.id,
|
|
671
|
+
"render.kind": "keyframe",
|
|
672
|
+
"render.shot_count": plans.length
|
|
673
|
+
}, async (span) => {
|
|
674
|
+
const rendered = await renderShots(renderHost(context), { id: row.id }, plans, { concurrency: clampConcurrency(params["concurrency"]) });
|
|
675
|
+
if (rendered.some((outcome) => !outcome.ok)) {
|
|
676
|
+
span?.setStatus({ code: SpanStatusCode.ERROR });
|
|
677
|
+
}
|
|
678
|
+
return rendered;
|
|
588
679
|
});
|
|
589
680
|
const results = outcomes.map((outcome) => outcomeRow(outcome, false));
|
|
590
681
|
return {
|
|
591
682
|
storyboard_id: row.id,
|
|
592
|
-
|
|
593
|
-
model: model.model,
|
|
683
|
+
...uniformModel(plans),
|
|
594
684
|
rendered: results.filter((r) => r.ok).length,
|
|
595
685
|
failed: results.filter((r) => !r.ok).length,
|
|
596
686
|
skipped,
|
|
@@ -613,43 +703,23 @@ const renderStoryboardClips = {
|
|
|
613
703
|
override !== "reference") {
|
|
614
704
|
return { error: 'mode must be "keyframe", "direct", or "reference".' };
|
|
615
705
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
}
|
|
706
|
+
const candidates = selectShots(doc.shots, params["targets"], (shot) => (params["stale_only"] === true ? !!shot.clip : !shotHasPicture(shot, doc.shots)) &&
|
|
707
|
+
!shot.covered_by?.shot_id);
|
|
708
|
+
if (isError(candidates)) {
|
|
709
|
+
return candidates;
|
|
651
710
|
}
|
|
652
711
|
const entities = await loadBoardEntities(context, doc);
|
|
712
|
+
const { planShotRenders, renderShots } = await import("@nodetool-ai/storyboard");
|
|
713
|
+
const readinessOptions = modelOverrides(params);
|
|
714
|
+
if (override !== undefined) {
|
|
715
|
+
readinessOptions.mode = override;
|
|
716
|
+
}
|
|
717
|
+
const ready = new Set(planShotRenders(doc, entities, "clip", candidates.map((shot) => shot.id), readinessOptions)
|
|
718
|
+
.filter((plan) => plan.mode !== "keyframe" || !!plan.sourceKeyframe)
|
|
719
|
+
.map((plan) => plan.shotId));
|
|
720
|
+
const selected = params["targets"] === undefined || params["targets"] === null
|
|
721
|
+
? candidates.filter((shot) => ready.has(shot.id))
|
|
722
|
+
: candidates;
|
|
653
723
|
const fresh = await filterStale(selected, params, doc, entities, "clip");
|
|
654
724
|
const skipped = fresh.skipped;
|
|
655
725
|
const chosen = fresh.shots;
|
|
@@ -663,13 +733,16 @@ const renderStoryboardClips = {
|
|
|
663
733
|
: '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
734
|
};
|
|
665
735
|
}
|
|
736
|
+
const forbiddenClip = chosen.find((shot) => shot.production?.media_strategy === "still_motion_graphics");
|
|
737
|
+
if (forbiddenClip) {
|
|
738
|
+
return { error: `Shot ${forbiddenClip.id} is still_motion_graphics; video generation is forbidden. Materialize its still/assets as Timeline layers instead.` };
|
|
739
|
+
}
|
|
666
740
|
if (chosen.length > MAX_SHOTS_PER_CALL) {
|
|
667
741
|
return {
|
|
668
742
|
error: `${chosen.length} shots selected, over the ${MAX_SHOTS_PER_CALL}-shot per-call limit. Pass targets in batches.`
|
|
669
743
|
};
|
|
670
744
|
}
|
|
671
745
|
const { scriptLinesById } = await import("@nodetool-ai/timeline");
|
|
672
|
-
const { planShotRenders, renderShots } = await import("@nodetool-ai/storyboard");
|
|
673
746
|
// A linked board times its shots from the words they cover, so a clip is
|
|
674
747
|
// rendered long enough to hold its voiceover (design §2.3). A shot pinned
|
|
675
748
|
// to `manual`, an unvoiced line, or an unlinked board keeps
|
|
@@ -678,8 +751,7 @@ const renderStoryboardClips = {
|
|
|
678
751
|
// As in the stills path: the record says what this call rendered with, so
|
|
679
752
|
// a per-call model or style override reads stale against the board.
|
|
680
753
|
const planOptions = {
|
|
681
|
-
|
|
682
|
-
model: model.model,
|
|
754
|
+
...modelOverrides(params),
|
|
683
755
|
style: isString(params["style"]) ? params["style"] : doc.style || "",
|
|
684
756
|
scriptLines: scriptLinesById(scriptDoc?.sections ?? [])
|
|
685
757
|
};
|
|
@@ -687,18 +759,31 @@ const renderStoryboardClips = {
|
|
|
687
759
|
planOptions.mode = override;
|
|
688
760
|
}
|
|
689
761
|
const plans = planShotRenders(doc, entities, "clip", chosen.map((shot) => shot.id), planOptions);
|
|
762
|
+
const modelError = await clipModelError(context, plans);
|
|
763
|
+
if (modelError) {
|
|
764
|
+
return modelError;
|
|
765
|
+
}
|
|
690
766
|
const renderOptions = {
|
|
691
767
|
concurrency: clampConcurrency(params["concurrency"])
|
|
692
768
|
};
|
|
693
769
|
if (isString(params["resolution"])) {
|
|
694
770
|
renderOptions.resolution = params["resolution"];
|
|
695
771
|
}
|
|
696
|
-
const outcomes = await
|
|
772
|
+
const outcomes = await withSpan("render", {
|
|
773
|
+
"document.id": row.id,
|
|
774
|
+
"render.kind": "clip",
|
|
775
|
+
"render.shot_count": plans.length
|
|
776
|
+
}, async (span) => {
|
|
777
|
+
const rendered = await renderShots(renderHost(context), { id: row.id }, plans, renderOptions);
|
|
778
|
+
if (rendered.some((outcome) => !outcome.ok)) {
|
|
779
|
+
span?.setStatus({ code: SpanStatusCode.ERROR });
|
|
780
|
+
}
|
|
781
|
+
return rendered;
|
|
782
|
+
});
|
|
697
783
|
const results = outcomes.map((outcome) => outcomeRow(outcome, true));
|
|
698
784
|
return {
|
|
699
785
|
storyboard_id: row.id,
|
|
700
|
-
|
|
701
|
-
model: model.model,
|
|
786
|
+
...uniformModel(plans),
|
|
702
787
|
rendered: results.filter((r) => r.ok).length,
|
|
703
788
|
failed: results.filter((r) => !r.ok).length,
|
|
704
789
|
skipped,
|
|
@@ -723,6 +808,15 @@ const reviseStoryboardClip = {
|
|
|
723
808
|
error: `No shot matches "${String(params["target"])}". Call get_storyboard to list shot ids.`
|
|
724
809
|
};
|
|
725
810
|
}
|
|
811
|
+
if (shot.production?.media_strategy === "still_motion_graphics") {
|
|
812
|
+
return { error: `Shot ${shot.id} is still_motion_graphics; video revision is forbidden.` };
|
|
813
|
+
}
|
|
814
|
+
try {
|
|
815
|
+
assertProductionGenerationAllowed(shot.production, "video_to_video");
|
|
816
|
+
}
|
|
817
|
+
catch (error) {
|
|
818
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
819
|
+
}
|
|
726
820
|
if (!shot.clip) {
|
|
727
821
|
return {
|
|
728
822
|
error: "Shot has no clip to revise. Run render_storyboard_clips first."
|
|
@@ -739,6 +833,13 @@ const reviseStoryboardClip = {
|
|
|
739
833
|
error: "The shot's clip could not be read back from storage."
|
|
740
834
|
};
|
|
741
835
|
}
|
|
836
|
+
const current = await loadBoard(run, row.id);
|
|
837
|
+
if (isError(current))
|
|
838
|
+
return current;
|
|
839
|
+
const currentShot = current.doc.shots.find((value) => value.id === shot.id);
|
|
840
|
+
if (!currentShot)
|
|
841
|
+
return { error: `Shot ${shot.id} no longer exists.` };
|
|
842
|
+
assertProductionGenerationAllowed(currentShot.production, "video_to_video");
|
|
742
843
|
const saved = await renderMedia(context, {
|
|
743
844
|
provider: model.provider,
|
|
744
845
|
capability: "video_to_video",
|
|
@@ -786,7 +887,7 @@ const assembleStoryboardTimeline = {
|
|
|
786
887
|
return board;
|
|
787
888
|
const { row, doc } = board;
|
|
788
889
|
const { Storyboard, TimelineSequence } = await import("@nodetool-ai/models");
|
|
789
|
-
const { buildLinkedTimeline, buildStoryboardTimeline, foreignTimelineParts, frameSizeForAspect } = await import("@nodetool-ai/timeline");
|
|
890
|
+
const { buildLinkedTimeline, buildStoryboardTimeline, foreignTimelineParts, assertStoryboardReassemblyAllowed, frameSizeForAspect } = await import("@nodetool-ai/timeline");
|
|
790
891
|
// A board that links a script is cut against the words: shot lengths come
|
|
791
892
|
// from the takes and every voiced line gets its own clip. A link pointing
|
|
792
893
|
// at a script that is gone is a warning, not a failure — the board still
|
|
@@ -799,33 +900,7 @@ const assembleStoryboardTimeline = {
|
|
|
799
900
|
if (scriptId && !script) {
|
|
800
901
|
warnings.push(`Script ${scriptId} is linked but could not be loaded, so the board was assembled on its own.`);
|
|
801
902
|
}
|
|
802
|
-
const
|
|
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);
|
|
903
|
+
const measuredShots = await measureStoryboardSources(doc.shots, context);
|
|
829
904
|
const assembled = script
|
|
830
905
|
? buildLinkedTimeline({
|
|
831
906
|
boardId: row.id,
|
|
@@ -865,9 +940,9 @@ const assembleStoryboardTimeline = {
|
|
|
865
940
|
`(${worst.usedMs}ms in the cut, ${worst.sourceMs}ms rendered). ` +
|
|
866
941
|
`Re-render those shots at the length their lines need.`);
|
|
867
942
|
}
|
|
868
|
-
if (assembled.
|
|
943
|
+
if (assembled.durationMs === 0) {
|
|
869
944
|
return {
|
|
870
|
-
error: "No shot has
|
|
945
|
+
error: "No shot has an allowed still, video, or graphics source. Add graphics intent or use render_storyboard_stills / render_storyboard_clips where allowed by the shot production policy.",
|
|
871
946
|
skipped_shot_ids: assembled.skippedShotIds,
|
|
872
947
|
skipped_line_ids: skippedLineIds
|
|
873
948
|
};
|
|
@@ -934,11 +1009,12 @@ const assembleStoryboardTimeline = {
|
|
|
934
1009
|
};
|
|
935
1010
|
}
|
|
936
1011
|
const previous = current.toDocument();
|
|
1012
|
+
assertStoryboardReassemblyAllowed(previous, row.id);
|
|
937
1013
|
const foreign = foreignTimelineParts(previous, (clip) => clip.storyboardBoardId === row.id ||
|
|
938
1014
|
(!!scriptId && clip.scriptId === scriptId));
|
|
939
1015
|
const tracks = [...assembled.tracks, ...foreign.tracks];
|
|
940
1016
|
const clips = [...assembled.clips, ...foreign.clips];
|
|
941
|
-
const durationMs = clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs),
|
|
1017
|
+
const durationMs = clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), assembled.durationMs);
|
|
942
1018
|
const nextDocument = {
|
|
943
1019
|
...previous,
|
|
944
1020
|
tracks,
|
|
@@ -1215,6 +1291,8 @@ const SHOT_EDIT_FIELDS = new Set([
|
|
|
1215
1291
|
"slug",
|
|
1216
1292
|
"camera",
|
|
1217
1293
|
"motion",
|
|
1294
|
+
"graphics",
|
|
1295
|
+
"production",
|
|
1218
1296
|
"dialogue",
|
|
1219
1297
|
"narration",
|
|
1220
1298
|
"notes",
|
|
@@ -1267,8 +1345,16 @@ const BOARD_EDIT_FIELDS = new Set([
|
|
|
1267
1345
|
"aspect_ratio",
|
|
1268
1346
|
"entity_ids",
|
|
1269
1347
|
"image_model",
|
|
1270
|
-
"video_model"
|
|
1348
|
+
"video_model",
|
|
1349
|
+
"narration",
|
|
1350
|
+
"music_prompt",
|
|
1351
|
+
"motion_design"
|
|
1271
1352
|
]);
|
|
1353
|
+
const screenplayDirection = z.object({
|
|
1354
|
+
narration: z.string().optional(),
|
|
1355
|
+
music_prompt: z.string().optional(),
|
|
1356
|
+
motion_design: storyboardMotionDesign.nullable().optional()
|
|
1357
|
+
});
|
|
1272
1358
|
/**
|
|
1273
1359
|
* The board-level twin of {@link assertKnownShotFields}.
|
|
1274
1360
|
*
|
|
@@ -1350,6 +1436,34 @@ function applyShotFields(shot, args, shots = []) {
|
|
|
1350
1436
|
next.camera = args["camera"];
|
|
1351
1437
|
if (args["motion"] !== undefined)
|
|
1352
1438
|
next.motion = String(args["motion"]);
|
|
1439
|
+
if (args["graphics"] !== undefined) {
|
|
1440
|
+
if (args["graphics"] === null) {
|
|
1441
|
+
delete next.graphics;
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
const parsed = storyboardShotGraphics.safeParse(args["graphics"]);
|
|
1445
|
+
if (!parsed.success) {
|
|
1446
|
+
throw new Error(`graphics must be a valid Storyboard graphics object: ${parsed.error.issues
|
|
1447
|
+
.map((issue) => issue.path.join(".") || issue.message)
|
|
1448
|
+
.join(", ")}.`);
|
|
1449
|
+
}
|
|
1450
|
+
next.graphics = parsed.data;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (args["production"] !== undefined) {
|
|
1454
|
+
if (args["production"] === null) {
|
|
1455
|
+
delete next.production;
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
const parsed = productionRequirement.safeParse(args["production"]);
|
|
1459
|
+
if (!parsed.success) {
|
|
1460
|
+
throw new Error(`production must be a valid production requirement: ${parsed.error.issues
|
|
1461
|
+
.map((issue) => issue.path.join(".") || issue.message)
|
|
1462
|
+
.join(", ")}.`);
|
|
1463
|
+
}
|
|
1464
|
+
next.production = parsed.data;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1353
1467
|
if (args["dialogue"] !== undefined)
|
|
1354
1468
|
next.dialogue = String(args["dialogue"]);
|
|
1355
1469
|
if (args["narration"] !== undefined)
|
|
@@ -1763,6 +1877,13 @@ function applyBoardOp(doc, { op, args }, entities = []) {
|
|
|
1763
1877
|
}
|
|
1764
1878
|
case "set_board": {
|
|
1765
1879
|
assertKnownBoardFields(args);
|
|
1880
|
+
const direction = screenplayDirection.safeParse(args);
|
|
1881
|
+
if (!direction.success) {
|
|
1882
|
+
throw new Error("set_board has invalid screenplay direction: " +
|
|
1883
|
+
direction.error.issues
|
|
1884
|
+
.map((issue) => `${issue.path.join(".") || "direction"}: ${issue.message}`)
|
|
1885
|
+
.join("; "));
|
|
1886
|
+
}
|
|
1766
1887
|
if (args["brief"] !== undefined)
|
|
1767
1888
|
doc.brief = String(args["brief"]);
|
|
1768
1889
|
if (args["style"] !== undefined)
|
|
@@ -1797,13 +1918,45 @@ function applyBoardOp(doc, { op, args }, entities = []) {
|
|
|
1797
1918
|
}
|
|
1798
1919
|
doc.videoModel = videoModel;
|
|
1799
1920
|
}
|
|
1921
|
+
if (args["narration"] !== undefined ||
|
|
1922
|
+
args["music_prompt"] !== undefined ||
|
|
1923
|
+
args["motion_design"] !== undefined) {
|
|
1924
|
+
const needsScreenplay = args["narration"] !== undefined ||
|
|
1925
|
+
args["music_prompt"] !== undefined ||
|
|
1926
|
+
direction.data.motion_design !== null;
|
|
1927
|
+
if (needsScreenplay) {
|
|
1928
|
+
doc.screenplay = doc.screenplay ?? {
|
|
1929
|
+
type: "screenplay",
|
|
1930
|
+
id: newDocId("sp"),
|
|
1931
|
+
title: "",
|
|
1932
|
+
shots: []
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
if (doc.screenplay && direction.data.narration !== undefined) {
|
|
1936
|
+
doc.screenplay.narration = direction.data.narration;
|
|
1937
|
+
}
|
|
1938
|
+
if (doc.screenplay && direction.data.music_prompt !== undefined) {
|
|
1939
|
+
doc.screenplay.music_prompt = direction.data.music_prompt;
|
|
1940
|
+
}
|
|
1941
|
+
if (doc.screenplay && direction.data.motion_design !== undefined) {
|
|
1942
|
+
if (direction.data.motion_design === null) {
|
|
1943
|
+
delete doc.screenplay.motion_design;
|
|
1944
|
+
}
|
|
1945
|
+
else {
|
|
1946
|
+
doc.screenplay.motion_design = direction.data.motion_design;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1800
1950
|
return {
|
|
1801
1951
|
brief: doc.brief,
|
|
1802
1952
|
style: doc.style,
|
|
1803
1953
|
aspect_ratio: doc.aspectRatio,
|
|
1804
1954
|
entity_ids: doc.entityIds,
|
|
1805
1955
|
image_model: doc.imageModel,
|
|
1806
|
-
video_model: doc.videoModel
|
|
1956
|
+
video_model: doc.videoModel,
|
|
1957
|
+
narration: doc.screenplay?.narration,
|
|
1958
|
+
music_prompt: doc.screenplay?.music_prompt,
|
|
1959
|
+
motion_design: doc.screenplay?.motion_design
|
|
1807
1960
|
};
|
|
1808
1961
|
}
|
|
1809
1962
|
}
|
|
@@ -1830,6 +1983,50 @@ async function loadStyleEntities(run, doc, ops) {
|
|
|
1830
1983
|
});
|
|
1831
1984
|
return [...cast, ...extra];
|
|
1832
1985
|
}
|
|
1986
|
+
/** Resolve source refs before any edit writes. Guest IDs stay short, stored refs stay canonical. */
|
|
1987
|
+
async function resolveStoryboardEditSources(run, ops) {
|
|
1988
|
+
const { Asset } = await import("@nodetool-ai/models");
|
|
1989
|
+
const canonical = new Map();
|
|
1990
|
+
const resolve = async (id) => {
|
|
1991
|
+
if (!isFullResourceId(id) && !isShortResourceId(id)) {
|
|
1992
|
+
return id;
|
|
1993
|
+
}
|
|
1994
|
+
const cached = canonical.get(id);
|
|
1995
|
+
if (cached) {
|
|
1996
|
+
return cached;
|
|
1997
|
+
}
|
|
1998
|
+
const asset = run.context.userId ? await Asset.find(run.context.userId, id) : null;
|
|
1999
|
+
if (!asset) {
|
|
2000
|
+
throw new Error(`Source asset or entity ${id} is unavailable.`);
|
|
2001
|
+
}
|
|
2002
|
+
canonical.set(id, asset.id);
|
|
2003
|
+
return asset.id;
|
|
2004
|
+
};
|
|
2005
|
+
const resolved = structuredClone(ops);
|
|
2006
|
+
for (const { args } of resolved) {
|
|
2007
|
+
const references = [args];
|
|
2008
|
+
const graphics = args["graphics"];
|
|
2009
|
+
if (isRecord(graphics) && Array.isArray(graphics["elements"])) {
|
|
2010
|
+
references.push(...graphics["elements"].filter(isRecord));
|
|
2011
|
+
}
|
|
2012
|
+
const production = args["production"];
|
|
2013
|
+
if (isRecord(production) && Array.isArray(production["protected_inputs"])) {
|
|
2014
|
+
references.push(...production["protected_inputs"].filter(isRecord));
|
|
2015
|
+
}
|
|
2016
|
+
for (const reference of references) {
|
|
2017
|
+
for (const field of ["asset_id", "entity_id", "location_id"]) {
|
|
2018
|
+
const id = reference[field];
|
|
2019
|
+
if (isString(id)) {
|
|
2020
|
+
reference[field] = await resolve(id);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
if (Array.isArray(args["entity_ids"])) {
|
|
2025
|
+
args["entity_ids"] = await Promise.all(args["entity_ids"].map(async (id) => isString(id) ? resolve(id) : id));
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return resolved;
|
|
2029
|
+
}
|
|
1833
2030
|
const editStoryboard = {
|
|
1834
2031
|
spec: editStoryboardSpec,
|
|
1835
2032
|
impl: async (run, params) => {
|
|
@@ -1842,12 +2039,22 @@ const editStoryboard = {
|
|
|
1842
2039
|
if (isError(board))
|
|
1843
2040
|
return board;
|
|
1844
2041
|
const { row, doc } = board;
|
|
2042
|
+
if (params["expected_revision"] !== undefined && params["expected_revision"] !== row.revision) {
|
|
2043
|
+
return { error: "Storyboard revision conflict. Refresh the board before editing." };
|
|
2044
|
+
}
|
|
1845
2045
|
// A failing op is recorded and the script continues: stopping at the
|
|
1846
2046
|
// first error hides every problem behind it.
|
|
1847
2047
|
const records = [];
|
|
1848
|
-
|
|
2048
|
+
let sourceOps;
|
|
2049
|
+
try {
|
|
2050
|
+
sourceOps = await resolveStoryboardEditSources(run, ops);
|
|
2051
|
+
}
|
|
2052
|
+
catch (error) {
|
|
2053
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
2054
|
+
}
|
|
2055
|
+
const entities = await loadStyleEntities(run, doc, sourceOps);
|
|
1849
2056
|
const resolvedOps = [];
|
|
1850
|
-
for (const parsed of
|
|
2057
|
+
for (const parsed of sourceOps) {
|
|
1851
2058
|
try {
|
|
1852
2059
|
const result = applyBoardOp(doc, parsed, entities);
|
|
1853
2060
|
records.push({
|
|
@@ -1888,6 +2095,9 @@ const editStoryboard = {
|
|
|
1888
2095
|
resolvedOps.push({ tool: parsed.op, input: parsed.args });
|
|
1889
2096
|
}
|
|
1890
2097
|
}
|
|
2098
|
+
if (doc.screenplay) {
|
|
2099
|
+
doc.screenplay.shots = doc.shots;
|
|
2100
|
+
}
|
|
1891
2101
|
const saved = await Storyboard.updateFieldsIfUnchanged(row.id, row.updated_at, {
|
|
1892
2102
|
document: JSON.stringify(doc)
|
|
1893
2103
|
},
|
|
@@ -1900,6 +2110,7 @@ const editStoryboard = {
|
|
|
1900
2110
|
return {
|
|
1901
2111
|
storyboard_id: row.id,
|
|
1902
2112
|
updated_at: saved.updated_at,
|
|
2113
|
+
revision: saved.revision,
|
|
1903
2114
|
applied: records.length - failed.length,
|
|
1904
2115
|
failed: failed.length,
|
|
1905
2116
|
ops: records,
|
|
@@ -2021,6 +2232,8 @@ const directStoryboard = {
|
|
|
2021
2232
|
...held,
|
|
2022
2233
|
...shot,
|
|
2023
2234
|
index,
|
|
2235
|
+
graphics: held.graphics,
|
|
2236
|
+
production: held.production,
|
|
2024
2237
|
keyframe: held.keyframe,
|
|
2025
2238
|
keyframe_versions: held.keyframe_versions,
|
|
2026
2239
|
clip: held.clip,
|
|
@@ -2029,7 +2242,11 @@ const directStoryboard = {
|
|
|
2029
2242
|
status: held.status
|
|
2030
2243
|
};
|
|
2031
2244
|
});
|
|
2032
|
-
|
|
2245
|
+
const previousMotionDesign = next.screenplay?.motion_design;
|
|
2246
|
+
next.screenplay = { ...screenplay, shots: next.shots };
|
|
2247
|
+
if (previousMotionDesign !== undefined) {
|
|
2248
|
+
next.screenplay.motion_design = previousMotionDesign;
|
|
2249
|
+
}
|
|
2033
2250
|
next.style = screenplay.style_bible ?? next.style;
|
|
2034
2251
|
next.aspectRatio = screenplay.aspect_ratio ?? next.aspectRatio;
|
|
2035
2252
|
if (screenplay.genre)
|
|
@@ -2040,6 +2257,7 @@ const directStoryboard = {
|
|
|
2040
2257
|
return {
|
|
2041
2258
|
storyboard_id: current.row.id,
|
|
2042
2259
|
updated_at: saved.updated_at,
|
|
2260
|
+
revision: saved.revision,
|
|
2043
2261
|
title: screenplay.title,
|
|
2044
2262
|
genre: next.genre,
|
|
2045
2263
|
redirected: params["redirect"] === true,
|
|
@@ -2226,6 +2444,9 @@ const deleteStoryboard = {
|
|
|
2226
2444
|
}
|
|
2227
2445
|
};
|
|
2228
2446
|
export const STORYBOARD_CAPABILITIES = [
|
|
2447
|
+
finishStoryboard,
|
|
2448
|
+
layoutStoryboard,
|
|
2449
|
+
previewStoryboardDesign,
|
|
2229
2450
|
listStoryboards,
|
|
2230
2451
|
createStoryboard,
|
|
2231
2452
|
getStoryboard,
|
|
@@ -2242,5 +2463,5 @@ export const module = {
|
|
|
2242
2463
|
module: "storyboards",
|
|
2243
2464
|
exports: STORYBOARD_CAPABILITIES
|
|
2244
2465
|
};
|
|
2245
|
-
export { listStoryboards, createStoryboard, getStoryboard, renderStoryboardStills, renderStoryboardClips, reviseStoryboardClip, assembleStoryboardTimeline, editStoryboard, directStoryboard, extractScriptFromStoryboard, deleteStoryboard };
|
|
2466
|
+
export { finishStoryboard, layoutStoryboard, previewStoryboardDesign, listStoryboards, createStoryboard, getStoryboard, renderStoryboardStills, renderStoryboardClips, reviseStoryboardClip, assembleStoryboardTimeline, editStoryboard, directStoryboard, extractScriptFromStoryboard, deleteStoryboard };
|
|
2246
2467
|
//# sourceMappingURL=storyboards.js.map
|