@nodetool-ai/agents 0.8.2 → 0.8.4-a24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.nodetool-build-stamp +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/capabilities/agent-activity.js +1 -1
- package/dist/capabilities/agentic-storyboard-finish.d.ts.map +1 -1
- package/dist/capabilities/agentic-storyboard-finish.js +2 -1
- package/dist/capabilities/agentic-storyboard-finish.js.map +1 -1
- package/dist/capabilities/apps.specs.d.ts.map +1 -1
- package/dist/capabilities/apps.specs.js +4 -3
- package/dist/capabilities/apps.specs.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/code-grading.d.ts +4 -0
- package/dist/capabilities/code-grading.d.ts.map +1 -1
- package/dist/capabilities/code-grading.js +8 -1
- package/dist/capabilities/code-grading.js.map +1 -1
- package/dist/capabilities/code.d.ts.map +1 -1
- package/dist/capabilities/code.js +13 -7
- 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 +83 -0
- package/dist/capabilities/derive-shot.js.map +1 -0
- package/dist/capabilities/dispatcher.js +2 -2
- package/dist/capabilities/dispatcher.js.map +1 -1
- 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/files.d.ts.map +1 -1
- package/dist/capabilities/files.js +3 -7
- package/dist/capabilities/files.js.map +1 -1
- package/dist/capabilities/files.specs.d.ts.map +1 -1
- package/dist/capabilities/files.specs.js +2 -1
- package/dist/capabilities/files.specs.js.map +1 -1
- package/dist/capabilities/flow.d.ts +3 -0
- package/dist/capabilities/flow.d.ts.map +1 -1
- package/dist/capabilities/flow.js +7 -0
- package/dist/capabilities/flow.js.map +1 -1
- package/dist/capabilities/game-asset-browser.d.ts +3 -0
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -0
- package/dist/capabilities/game-asset-browser.js +153 -0
- package/dist/capabilities/game-asset-browser.js.map +1 -0
- package/dist/capabilities/game.d.ts +30 -0
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +266 -149
- 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 +21 -6
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts.map +1 -1
- package/dist/capabilities/game3d.js +24 -10
- package/dist/capabilities/game3d.js.map +1 -1
- package/dist/capabilities/generations.d.ts.map +1 -1
- package/dist/capabilities/generations.js +7 -21
- package/dist/capabilities/generations.js.map +1 -1
- package/dist/capabilities/invoke.d.ts.map +1 -1
- package/dist/capabilities/invoke.js +15 -2
- package/dist/capabilities/invoke.js.map +1 -1
- package/dist/capabilities/jobs.d.ts +7 -2
- package/dist/capabilities/jobs.d.ts.map +1 -1
- package/dist/capabilities/jobs.js +13 -3
- package/dist/capabilities/jobs.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 +2 -21
- package/dist/capabilities/lazy-tool.d.ts.map +1 -1
- package/dist/capabilities/lazy-tool.js +3 -55
- package/dist/capabilities/lazy-tool.js.map +1 -1
- package/dist/capabilities/legacy-tools.js +1 -1
- package/dist/capabilities/legacy-tools.js.map +1 -1
- package/dist/capabilities/media.d.ts.map +1 -1
- package/dist/capabilities/media.js +26 -1
- 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 +6 -1
- package/dist/capabilities/model3d.js.map +1 -1
- package/dist/capabilities/nodes.specs.js +1 -1
- package/dist/capabilities/nodes.specs.js.map +1 -1
- package/dist/capabilities/registry.d.ts +4 -19
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +48 -117
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/capabilities/run-agent.js +1 -1
- package/dist/capabilities/scripts.js +2 -2
- 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/storyboard-layout-check.d.ts.map +1 -1
- package/dist/capabilities/storyboard-layout-check.js +2 -1
- package/dist/capabilities/storyboard-layout-check.js.map +1 -1
- package/dist/capabilities/storyboard-measured-sources.d.ts +1 -1
- package/dist/capabilities/storyboard-measured-sources.d.ts.map +1 -1
- package/dist/capabilities/storyboard-measured-sources.js +2 -1
- package/dist/capabilities/storyboard-measured-sources.js.map +1 -1
- package/dist/capabilities/storyboards.d.ts +11 -1
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +467 -5
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts +6 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +139 -3
- 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-asset-ids.d.ts +27 -0
- package/dist/capabilities/timeline-asset-ids.d.ts.map +1 -0
- package/dist/capabilities/timeline-asset-ids.js +88 -0
- package/dist/capabilities/timeline-asset-ids.js.map +1 -0
- package/dist/capabilities/timeline-bake.d.ts.map +1 -1
- package/dist/capabilities/timeline-bake.js +2 -1
- package/dist/capabilities/timeline-bake.js.map +1 -1
- package/dist/capabilities/timeline-bridge.d.ts +9 -0
- package/dist/capabilities/timeline-bridge.d.ts.map +1 -1
- package/dist/capabilities/timeline-bridge.js +60 -68
- package/dist/capabilities/timeline-bridge.js.map +1 -1
- package/dist/capabilities/timeline-clip-frames.d.ts.map +1 -1
- package/dist/capabilities/timeline-clip-frames.js +2 -1
- package/dist/capabilities/timeline-clip-frames.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 +4 -39
- package/dist/capabilities/timelines.d.ts.map +1 -1
- package/dist/capabilities/timelines.js +45 -414
- package/dist/capabilities/timelines.js.map +1 -1
- package/dist/capabilities/timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/timelines.specs.js +8 -3
- package/dist/capabilities/timelines.specs.js.map +1 -1
- package/dist/capabilities/web.d.ts.map +1 -1
- package/dist/capabilities/web.js +5 -1
- package/dist/capabilities/web.js.map +1 -1
- package/dist/capabilities/web.specs.js +1 -1
- package/dist/capabilities/web.specs.js.map +1 -1
- package/dist/capabilities/workflows.d.ts +3 -23
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +197 -15
- 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 +65 -1
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/codeact/nodetool-api.d.ts +17 -1
- package/dist/codeact/nodetool-api.d.ts.map +1 -1
- package/dist/codeact/nodetool-api.js +168 -48
- 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.map +1 -1
- package/dist/codeact/tool-api.js +6 -2
- package/dist/codeact/tool-api.js.map +1 -1
- package/dist/evals/app-build-plan.d.ts.map +1 -1
- package/dist/evals/app-build-plan.js +3 -0
- package/dist/evals/app-build-plan.js.map +1 -1
- package/dist/evals/codeact-api-core.d.ts.map +1 -1
- package/dist/evals/codeact-api-core.js +6 -0
- package/dist/evals/codeact-api-core.js.map +1 -1
- package/dist/evals/surfaces/creative-pipeline.d.ts +1 -1
- package/dist/evals/surfaces/creative-pipeline.d.ts.map +1 -1
- package/dist/evals/surfaces/creative-pipeline.js +2 -2
- package/dist/evals/surfaces/creative-pipeline.js.map +1 -1
- package/dist/evals/surfaces/game.d.ts +9 -2
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +714 -2
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/evals/surfaces/sketch.d.ts +1 -1
- package/dist/evals/surfaces/sketch.d.ts.map +1 -1
- package/dist/evals/surfaces/sketch.js +6 -13
- package/dist/evals/surfaces/sketch.js.map +1 -1
- package/dist/evals/tool-loop-eval.d.ts +1 -1
- package/dist/evals/tool-loop-eval.d.ts.map +1 -1
- package/dist/evals/tool-loop-eval.js +1 -1
- package/dist/evals/tool-loop-eval.js.map +1 -1
- package/dist/game-authoring.d.ts +18868 -3824
- package/dist/game-authoring.d.ts.map +1 -1
- 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-examples.d.ts +5310 -1056
- package/dist/game-examples.d.ts.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/tokens.js +2 -2
- package/dist/host-modules/tokens.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 +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox.d.ts +8 -1
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +27 -4
- package/dist/js-sandbox.js.map +1 -1
- package/dist/jtbd/registry.d.ts.map +1 -1
- package/dist/jtbd/registry.js +16 -3
- package/dist/jtbd/registry.js.map +1 -1
- package/dist/jtbd/run.d.ts +2 -2
- package/dist/jtbd/run.d.ts.map +1 -1
- package/dist/jtbd/run.js +3 -3
- package/dist/jtbd/run.js.map +1 -1
- package/dist/jtbd/types.d.ts +1 -1
- package/dist/jtbd/types.d.ts.map +1 -1
- package/dist/model-prompting-skills.d.ts +8 -7
- package/dist/model-prompting-skills.d.ts.map +1 -1
- package/dist/model-prompting-skills.js +35 -16
- package/dist/model-prompting-skills.js.map +1 -1
- package/dist/sandbox-media.d.ts +1 -1
- package/dist/sandbox-media.d.ts.map +1 -1
- package/dist/sandbox-media.js +10 -9
- package/dist/sandbox-media.js.map +1 -1
- package/dist/skill-prompt.d.ts.map +1 -1
- package/dist/skill-prompt.js +1 -1
- package/dist/skill-prompt.js.map +1 -1
- package/dist/system-skills.d.ts +4 -3
- package/dist/system-skills.d.ts.map +1 -1
- package/dist/system-skills.js +4 -3
- package/dist/system-skills.js.map +1 -1
- package/dist/task-planner.js +1 -1
- package/dist/timeline-code-bake.js +2 -2
- package/dist/timeline-code-bake.js.map +1 -1
- package/dist/timeline-preview/compare.d.ts.map +1 -1
- package/dist/timeline-preview/compare.js +4 -1
- package/dist/timeline-preview/compare.js.map +1 -1
- package/dist/timeline-preview/frames.d.ts.map +1 -1
- package/dist/timeline-preview/frames.js +7 -4
- package/dist/timeline-preview/frames.js.map +1 -1
- package/dist/timeline-preview/model3d.d.ts +1 -1
- package/dist/timeline-preview/model3d.d.ts.map +1 -1
- package/dist/timeline-preview/model3d.js +4 -23
- package/dist/timeline-preview/model3d.js.map +1 -1
- package/dist/timeline-preview/rasterize.d.ts +4 -2
- package/dist/timeline-preview/rasterize.d.ts.map +1 -1
- package/dist/timeline-preview/rasterize.js +10 -4
- package/dist/timeline-preview/rasterize.js.map +1 -1
- package/dist/timeline-preview/sheet.d.ts.map +1 -1
- package/dist/timeline-preview/sheet.js +3 -2
- package/dist/timeline-preview/sheet.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 +11 -3
- package/dist/tools/builtin-tools.js.map +1 -1
- package/dist/tools/graph-validation-registry.d.ts.map +1 -1
- package/dist/tools/graph-validation-registry.js +3 -0
- package/dist/tools/graph-validation-registry.js.map +1 -1
- package/dist/tools/mcp-tool-support.js +1 -1
- package/dist/tools/mcp-tool-support.js.map +1 -1
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +4 -0
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/utils/json-parser.js +1 -1
- package/dist/utils/json-parser.js.map +1 -1
- package/package.json +131 -47
|
@@ -17,17 +17,14 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Design: docs/tool-class-retirement-design.md § "Migration".
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
import { STAGGER_UNITS, ANIMATED_PROPERTIES, DEFAULT_BEAT_TOLERANCE_MS } from "@nodetool-ai/timeline";
|
|
23
|
-
import { buildTimelineToolContracts } from "@nodetool-ai/protocol/api-schemas/timeline-tool-contracts.js";
|
|
24
|
-
import { resolveMoveTrackArgs, resolveDeleteTrackArgs } from "@nodetool-ai/protocol/api-schemas/timeline-tool-params.js";
|
|
25
|
-
import { parseWithTypeCoercion } from "@nodetool-ai/runtime";
|
|
20
|
+
import { parseOps, applyOps, TOOL_PREFIX, READ_ONLY_OPS, timelineModel3DBaker } from "./timeline-operations.js";
|
|
21
|
+
export { parseOps, applyOps } from "./timeline-operations.js";
|
|
26
22
|
import { listTimelinesSpec, createTimelineSpec, getTimelineSpec, listTimelineVersionsSpec, getTimelineVersionSpec, createTimelineVersionSpec, restoreTimelineVersionSpec, deleteTimelineVersionSpec, editTimelineSpec, validateTimelineSpec, setTimelineDocumentSpec, previewTimelineFrameSpec, compareTimelineFramesSpec, renderTimelineSpec, bakeAudioAnimationSpec, isolateSubjectSpec, ISOLATE_SUBJECT_MODELS, ISOLATE_SUBJECT_RESOLUTIONS, DEFAULT_ISOLATE_SUBJECT_MODEL, DEFAULT_ISOLATE_SUBJECT_RESOLUTION, DEFAULT_BAKE_ATTACK_MS, DEFAULT_BAKE_RELEASE_MS, DEFAULT_BAKE_TOLERANCE, DEFAULT_BAKE_MAX_POINTS, DEFAULT_BAKE_MAX_SECONDS, MAX_BAKE_POINTS, DEFAULT_RENDER_TIMEOUT_MS, MAX_RENDER_TIMEOUT_MS, DEFAULT_PREVIEW_COUNT, DEFAULT_COMPARE_WIDTH, MAX_PREVIEW_TIMES, MAX_PREVIEW_RANGE_COUNT, DEFAULT_VERSION_LIMIT, MAX_VERSION_LIMIT, trackObjectSpec, TRACK_OBJECT_DIRECTIONS, getTimelineCodeSpec, setTimelineCodeSpec, editTimelineCodeSpec, rebakeTimelineCodeSpec, detachTimelineCodeSpec, deleteTimelineSpec } from "./timelines.specs.js";
|
|
27
23
|
import { AUDIO_BAKED_ANIMATION_KIND, normalizeAuthoredDocument, hashSceneSubtree, mergeTimelineSource } from "@nodetool-ai/timeline";
|
|
28
24
|
import { bakeTimelineCode, bakedDocumentMatchesSaved } from "../timeline-code-bake.js";
|
|
29
25
|
import { applyJsScriptEditOps, parseJsScriptEditOps } from "./js-scripts.js";
|
|
30
26
|
import { clipSourceWindowMs } from "./timeline-audio-bake.js";
|
|
27
|
+
import { canonicalizeTimelineAssetIds, userAssetFinder } from "./timeline-asset-ids.js";
|
|
31
28
|
import { TIMELINE_NATIVE_MEDIA_CAPABILITIES } from "./timeline-native-media.js";
|
|
32
29
|
import { isFiniteNumber, isRecord, isString } from "../utils/type-guards.js";
|
|
33
30
|
import { resolveProjectId } from "./project-scope.js";
|
|
@@ -372,400 +369,6 @@ const deleteTimelineVersion = {
|
|
|
372
369
|
// ---------------------------------------------------------------------------
|
|
373
370
|
/** Attempts to land the document write before reporting a conflict. */
|
|
374
371
|
const CAS_ATTEMPTS = 2;
|
|
375
|
-
/** Operations one call may apply, so a runaway script cannot rewrite a cut. */
|
|
376
|
-
const MAX_OPS = 60;
|
|
377
|
-
const TOOL_PREFIX = "ui_timeline_";
|
|
378
|
-
/** Browser generation without a real provider adapter remains excluded. */
|
|
379
|
-
const EXCLUDED_OPS = new Set([`${TOOL_PREFIX}generate_clip`]);
|
|
380
|
-
const READ_ONLY_OPS = new Set([`${TOOL_PREFIX}get_clip_frames`]);
|
|
381
|
-
/** `add_track`, `ui_add_track`, and `ui_timeline_add_track` all name one tool. */
|
|
382
|
-
function normalizeOpName(name) {
|
|
383
|
-
const trimmed = name.trim();
|
|
384
|
-
if (trimmed.startsWith(TOOL_PREFIX))
|
|
385
|
-
return trimmed;
|
|
386
|
-
const bare = trimmed.startsWith("ui_")
|
|
387
|
-
? trimmed.slice("ui_".length)
|
|
388
|
-
: trimmed;
|
|
389
|
-
return `${TOOL_PREFIX}${bare}`;
|
|
390
|
-
}
|
|
391
|
-
/** An op is `{op, ...args}` — the arguments sit alongside the verb. */
|
|
392
|
-
export function parseOps(raw) {
|
|
393
|
-
if (!Array.isArray(raw) || raw.length === 0) {
|
|
394
|
-
return {
|
|
395
|
-
error: 'ops must be a non-empty array, e.g. [{"op": "add_track", "type": "audio"}].'
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
if (raw.length > MAX_OPS) {
|
|
399
|
-
return {
|
|
400
|
-
error: `ops holds ${raw.length} entries; at most ${MAX_OPS} per call.`
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
const parsed = [];
|
|
404
|
-
for (const [index, entry] of raw.entries()) {
|
|
405
|
-
if (!isRecord(entry)) {
|
|
406
|
-
return { error: `ops[${index}] must be an object.` };
|
|
407
|
-
}
|
|
408
|
-
const { op, ...input } = entry;
|
|
409
|
-
if (!isString(op) || op.trim() === "") {
|
|
410
|
-
return { error: `ops[${index}] has no \`op\` name.` };
|
|
411
|
-
}
|
|
412
|
-
parsed.push({ op: normalizeOpName(op), input });
|
|
413
|
-
}
|
|
414
|
-
return parsed;
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Read one of the caller's assets for `add_media_clip`.
|
|
418
|
-
*
|
|
419
|
-
* The ref a model has in hand is whatever `list_assets` printed — a bare id or
|
|
420
|
-
* an `asset://<id>.<ext>` URI — so both resolve here.
|
|
421
|
-
*/
|
|
422
|
-
async function resolveTimelineAsset(run, ref) {
|
|
423
|
-
const id = ref.startsWith("asset://")
|
|
424
|
-
? ref.slice("asset://".length).replace(/\.[A-Za-z0-9]{1,8}$/, "")
|
|
425
|
-
: ref;
|
|
426
|
-
if (!id)
|
|
427
|
-
return null;
|
|
428
|
-
const userId = run.context.userId;
|
|
429
|
-
if (!userId)
|
|
430
|
-
return null;
|
|
431
|
-
const { Asset } = await import("@nodetool-ai/models");
|
|
432
|
-
// `find` is already user-scoped: someone else's asset reads as missing.
|
|
433
|
-
const asset = await Asset.find(userId, id);
|
|
434
|
-
if (!asset)
|
|
435
|
-
return null;
|
|
436
|
-
const thumbnails = isRecord(asset.metadata)
|
|
437
|
-
? asset.metadata["thumbnails"]
|
|
438
|
-
: undefined;
|
|
439
|
-
const thumbnailAssetId = Array.isArray(thumbnails) && isString(thumbnails[0])
|
|
440
|
-
? thumbnails[0]
|
|
441
|
-
: undefined;
|
|
442
|
-
const resolved = {
|
|
443
|
-
id: asset.id,
|
|
444
|
-
name: asset.name,
|
|
445
|
-
contentType: asset.content_type
|
|
446
|
-
};
|
|
447
|
-
let duration = asset.duration;
|
|
448
|
-
if ((!isFiniteNumber(duration) || duration <= 0) &&
|
|
449
|
-
(asset.content_type.startsWith("audio/") || asset.content_type.startsWith("video/"))) {
|
|
450
|
-
try {
|
|
451
|
-
const { loadMediaRefBytes, probeVideoDurationSeconds } = await import("@nodetool-ai/runtime");
|
|
452
|
-
const bytes = await loadMediaRefBytes({ asset_id: asset.id }, run.context);
|
|
453
|
-
if (bytes) {
|
|
454
|
-
duration = await probeVideoDurationSeconds(bytes, new AbortController().signal);
|
|
455
|
-
if (isFiniteNumber(duration) && duration > 0) {
|
|
456
|
-
asset.duration = duration;
|
|
457
|
-
await asset.save();
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
// An explicit clip duration remains usable when the source cannot be probed.
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (isFiniteNumber(duration) && duration > 0) {
|
|
466
|
-
resolved.durationMs = Math.round(duration * 1000);
|
|
467
|
-
}
|
|
468
|
-
if (thumbnailAssetId)
|
|
469
|
-
resolved.thumbnailAssetId = thumbnailAssetId;
|
|
470
|
-
return resolved;
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Run a custom animation's body once and return its curves.
|
|
474
|
-
*
|
|
475
|
-
* The bake is the only place that code runs — the curves are stored and every
|
|
476
|
-
* render site samples them — so it goes through `bakeCustomAnimation`, the
|
|
477
|
-
* same hermetic path `POST /api/timelines/animations/bake` uses. Imported
|
|
478
|
-
* lazily: the sandbox is a heavy dependency for a capability most calls never
|
|
479
|
-
* reach.
|
|
480
|
-
*/
|
|
481
|
-
async function bakeTimelineAnimation(run, request) {
|
|
482
|
-
const { bakeCustomAnimation } = await import("../custom-animation-bake.js");
|
|
483
|
-
const params = {
|
|
484
|
-
code: request.code,
|
|
485
|
-
role: request.role,
|
|
486
|
-
durationMs: request.durationMs,
|
|
487
|
-
clipDurationMs: request.clipDurationMs,
|
|
488
|
-
canvas: request.canvas
|
|
489
|
-
};
|
|
490
|
-
if (request.params !== undefined)
|
|
491
|
-
params.params = request.params;
|
|
492
|
-
if (request.staggerCount !== undefined) {
|
|
493
|
-
params.staggerCount = request.staggerCount;
|
|
494
|
-
}
|
|
495
|
-
const baked = await bakeCustomAnimation(run.context, params);
|
|
496
|
-
const result = { ok: baked.ok };
|
|
497
|
-
if (baked.curves !== undefined)
|
|
498
|
-
result.curves = baked.curves;
|
|
499
|
-
if (baked.mask !== undefined)
|
|
500
|
-
result.mask = baked.mask;
|
|
501
|
-
if (baked.error !== undefined)
|
|
502
|
-
result.error = baked.error;
|
|
503
|
-
return result;
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* The bridge hooks that write outside the document: a Blender render of a 3D
|
|
507
|
-
* clip, and the new sequence `retarget_format` creates.
|
|
508
|
-
*/
|
|
509
|
-
function writingHooks(run, sequence) {
|
|
510
|
-
return {
|
|
511
|
-
bakeModel3DClip: async (request) => {
|
|
512
|
-
const { bakeModel3DClipOnServer } = await import("./timeline-bake.js");
|
|
513
|
-
return bakeModel3DClipOnServer(run.context, request);
|
|
514
|
-
},
|
|
515
|
-
retargetFormat: async (adapted) => {
|
|
516
|
-
const { TimelineSequence } = await import("@nodetool-ai/models");
|
|
517
|
-
const derivedDocument = {
|
|
518
|
-
tracks: adapted.tracks,
|
|
519
|
-
clips: adapted.clips,
|
|
520
|
-
markers: adapted.markers
|
|
521
|
-
};
|
|
522
|
-
if (adapted.transcript !== undefined) {
|
|
523
|
-
derivedDocument.transcript = adapted.transcript;
|
|
524
|
-
}
|
|
525
|
-
if (adapted.scriptEnabled !== undefined) {
|
|
526
|
-
derivedDocument.scriptEnabled = adapted.scriptEnabled;
|
|
527
|
-
}
|
|
528
|
-
if (adapted.tempo !== undefined)
|
|
529
|
-
derivedDocument.tempo = adapted.tempo;
|
|
530
|
-
if (adapted.camera2d !== undefined)
|
|
531
|
-
derivedDocument.camera2d = adapted.camera2d;
|
|
532
|
-
if (adapted.setup !== undefined)
|
|
533
|
-
derivedDocument.setup = adapted.setup;
|
|
534
|
-
if (adapted.templateId !== undefined) {
|
|
535
|
-
derivedDocument.templateId = adapted.templateId;
|
|
536
|
-
}
|
|
537
|
-
if (adapted.mediaTracks !== undefined) {
|
|
538
|
-
derivedDocument.mediaTracks = adapted.mediaTracks;
|
|
539
|
-
}
|
|
540
|
-
const name = `${sequence.name} (${adapted.width}×${adapted.height})`;
|
|
541
|
-
const created = await TimelineSequence.create({
|
|
542
|
-
user_id: run.context.userId,
|
|
543
|
-
project_id: sequence.project_id,
|
|
544
|
-
workflow_id: sequence.workflow_id,
|
|
545
|
-
name,
|
|
546
|
-
fps: adapted.fps,
|
|
547
|
-
width: adapted.width,
|
|
548
|
-
height: adapted.height,
|
|
549
|
-
duration_ms: adapted.durationMs,
|
|
550
|
-
document: JSON.stringify(derivedDocument)
|
|
551
|
-
});
|
|
552
|
-
return { sequenceId: created.id, name };
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
/** Track-only batches need no simulated editor or external hooks. */
|
|
557
|
-
function applyTrackOps(sequence, document, ops) {
|
|
558
|
-
const contracts = buildTimelineToolContracts({
|
|
559
|
-
staggerUnits: STAGGER_UNITS,
|
|
560
|
-
animatedProperties: ANIMATED_PROPERTIES,
|
|
561
|
-
beatToleranceMs: DEFAULT_BEAT_TOLERANCE_MS
|
|
562
|
-
});
|
|
563
|
-
let state = {
|
|
564
|
-
fps: sequence.fps,
|
|
565
|
-
width: sequence.width,
|
|
566
|
-
height: sequence.height,
|
|
567
|
-
tracks: document.tracks,
|
|
568
|
-
clips: document.clips,
|
|
569
|
-
markers: document.markers,
|
|
570
|
-
mediaTracks: document.mediaTracks,
|
|
571
|
-
tempo: document.tempo,
|
|
572
|
-
playheadMs: 0,
|
|
573
|
-
selectedClipIds: []
|
|
574
|
-
};
|
|
575
|
-
const usedIds = new Set([
|
|
576
|
-
...[
|
|
577
|
-
...document.tracks,
|
|
578
|
-
...document.clips,
|
|
579
|
-
...document.markers,
|
|
580
|
-
...(document.mediaTracks ?? [])
|
|
581
|
-
].map((unit) => unit.id),
|
|
582
|
-
...document.clips.flatMap((clip) => (clip.animations ?? []).map((animation) => animation.id))
|
|
583
|
-
]);
|
|
584
|
-
let nextId = 0;
|
|
585
|
-
const newId = () => {
|
|
586
|
-
let id;
|
|
587
|
-
do {
|
|
588
|
-
id = `track_${++nextId}`;
|
|
589
|
-
} while (usedIds.has(id));
|
|
590
|
-
usedIds.add(id);
|
|
591
|
-
return id;
|
|
592
|
-
};
|
|
593
|
-
const records = [];
|
|
594
|
-
for (const entry of ops) {
|
|
595
|
-
try {
|
|
596
|
-
let op;
|
|
597
|
-
if (entry.op === "ui_timeline_add_track") {
|
|
598
|
-
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_add_track.shape), entry.input);
|
|
599
|
-
op = { op: "add_track", ...args };
|
|
600
|
-
}
|
|
601
|
-
else if (entry.op === "ui_timeline_move_track") {
|
|
602
|
-
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_move_track.shape), entry.input);
|
|
603
|
-
op = { op: "move_track", ...resolveMoveTrackArgs(args) };
|
|
604
|
-
}
|
|
605
|
-
else {
|
|
606
|
-
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_delete_track.shape), entry.input);
|
|
607
|
-
op = { op: "delete_track", ...resolveDeleteTrackArgs(args) };
|
|
608
|
-
}
|
|
609
|
-
const outcome = applyTimelineTrackOp(state, op, { newId });
|
|
610
|
-
if (outcome.error)
|
|
611
|
-
throw new Error(outcome.error);
|
|
612
|
-
state = outcome.state;
|
|
613
|
-
records.push({ op: entry.op, ok: true, result: outcome.result });
|
|
614
|
-
}
|
|
615
|
-
catch (error) {
|
|
616
|
-
records.push({
|
|
617
|
-
op: entry.op,
|
|
618
|
-
ok: false,
|
|
619
|
-
error: error instanceof Error ? error.message : String(error)
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return {
|
|
624
|
-
records,
|
|
625
|
-
state: {
|
|
626
|
-
fps: state.fps,
|
|
627
|
-
width: state.width,
|
|
628
|
-
height: state.height,
|
|
629
|
-
durationMs: state.clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0),
|
|
630
|
-
playheadMs: state.playheadMs,
|
|
631
|
-
tracks: state.tracks.map(({ id, name, type, index }) => ({
|
|
632
|
-
id,
|
|
633
|
-
name,
|
|
634
|
-
type,
|
|
635
|
-
index
|
|
636
|
-
})),
|
|
637
|
-
clips: state.clips.map((clip) => ({
|
|
638
|
-
id: clip.id,
|
|
639
|
-
name: clip.name,
|
|
640
|
-
trackId: clip.trackId,
|
|
641
|
-
mediaType: clip.mediaType,
|
|
642
|
-
startMs: clip.startMs,
|
|
643
|
-
durationMs: clip.durationMs,
|
|
644
|
-
prompt: clip.prompt,
|
|
645
|
-
animations: (clip.animations ?? []).map(({ role, preset }) => ({
|
|
646
|
-
role,
|
|
647
|
-
preset
|
|
648
|
-
}))
|
|
649
|
-
})),
|
|
650
|
-
documentTracks: state.tracks,
|
|
651
|
-
documentClips: state.clips,
|
|
652
|
-
markers: state.markers,
|
|
653
|
-
mediaTracks: state.mediaTracks ?? [],
|
|
654
|
-
derivedSequences: [],
|
|
655
|
-
tempo: state.tempo,
|
|
656
|
-
camera2d: document.camera2d ?? null,
|
|
657
|
-
setup: document.setup ?? null,
|
|
658
|
-
transitionCandidates: [],
|
|
659
|
-
appliedTransitionCandidates: [],
|
|
660
|
-
toolLog: ops.map((entry) => entry.op),
|
|
661
|
-
previewTimesMs: [],
|
|
662
|
-
previewedLayerKinds: []
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* Run `ops` against a bridge seeded from `document`.
|
|
668
|
-
*
|
|
669
|
-
* A failing op is recorded and the script continues: stopping at the first
|
|
670
|
-
* error hides every problem behind it, and the caller wants the whole picture.
|
|
671
|
-
*
|
|
672
|
-
* `hermetic` leaves out the hooks that write outside the document: the 3D
|
|
673
|
-
* render and the new sequence a retarget creates. A code bake sets it. The
|
|
674
|
-
* bridge then uses its in-memory fallbacks for those ops.
|
|
675
|
-
*/
|
|
676
|
-
export async function applyOps(run, sequence, document, ops, options = {}) {
|
|
677
|
-
if (ops.every(({ op }) => op === "ui_timeline_add_track" || op === "ui_timeline_move_track" || op === "ui_timeline_delete_track")) {
|
|
678
|
-
return applyTrackOps(sequence, document, ops);
|
|
679
|
-
}
|
|
680
|
-
const { createTimelineToolBridge } = await import("./timeline-bridge.js");
|
|
681
|
-
let operationIndex = 0;
|
|
682
|
-
const init = {
|
|
683
|
-
sequenceId: sequence.id,
|
|
684
|
-
sequenceName: sequence.name,
|
|
685
|
-
projectId: sequence.project_id,
|
|
686
|
-
sequence: {
|
|
687
|
-
fps: sequence.fps,
|
|
688
|
-
width: sequence.width,
|
|
689
|
-
height: sequence.height,
|
|
690
|
-
tracks: document.tracks,
|
|
691
|
-
clips: document.clips,
|
|
692
|
-
markers: document.markers,
|
|
693
|
-
transcript: document.transcript,
|
|
694
|
-
scriptEnabled: document.scriptEnabled,
|
|
695
|
-
templateId: document.templateId,
|
|
696
|
-
tempo: document.tempo,
|
|
697
|
-
camera2d: document.camera2d,
|
|
698
|
-
setup: document.setup,
|
|
699
|
-
mediaTracks: document.mediaTracks
|
|
700
|
-
},
|
|
701
|
-
resolveAsset: (ref) => resolveTimelineAsset(run, ref),
|
|
702
|
-
bakeAnimation: (request) => bakeTimelineAnimation(run, request),
|
|
703
|
-
loadComposition: {
|
|
704
|
-
get: async (id) => {
|
|
705
|
-
const { loadComposition } = await import("./compositions.js");
|
|
706
|
-
const found = await loadComposition(run, id);
|
|
707
|
-
return found ? found.composition : null;
|
|
708
|
-
},
|
|
709
|
-
listIds: async () => {
|
|
710
|
-
const { loadShippedCompositions, loadUserCompositions } = await import("./compositions.js");
|
|
711
|
-
const mine = await loadUserCompositions(run);
|
|
712
|
-
return [
|
|
713
|
-
...loadShippedCompositions().map((comp) => comp.id),
|
|
714
|
-
...(Array.isArray(mine) ? mine.map((comp) => comp.id) : [])
|
|
715
|
-
];
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
};
|
|
719
|
-
if (!options.hermetic) {
|
|
720
|
-
Object.assign(init, writingHooks(run, sequence));
|
|
721
|
-
const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
|
|
722
|
-
const generate = options.generateMediaEdit ?? timelineMediaEditGenerator(run);
|
|
723
|
-
init.generateMediaEdit = (request) => generate(request, operationIndex);
|
|
724
|
-
init.getClipFrames = async (clip, frameOptions) => {
|
|
725
|
-
const current = bridge.finalState();
|
|
726
|
-
const { inspectTimelineClipFrames } = await import("./timeline-clip-frames.js");
|
|
727
|
-
return inspectTimelineClipFrames(run.context, { ...sequence.toDocument(),
|
|
728
|
-
tracks: current.documentTracks, clips: current.documentClips, markers: current.markers,
|
|
729
|
-
mediaTracks: current.mediaTracks, tempo: current.tempo, camera2d: current.camera2d,
|
|
730
|
-
id: sequence.id, projectId: sequence.project_id ?? "default", name: sequence.name,
|
|
731
|
-
fps: sequence.fps, width: sequence.width, height: sequence.height,
|
|
732
|
-
durationMs: sequence.duration_ms, createdAt: sequence.created_at, updatedAt: sequence.updated_at
|
|
733
|
-
}, clip, frameOptions);
|
|
734
|
-
};
|
|
735
|
-
}
|
|
736
|
-
const bridge = createTimelineToolBridge(init);
|
|
737
|
-
const byName = new Map(bridge.tools
|
|
738
|
-
.filter((tool) => !EXCLUDED_OPS.has(tool.name) && !(options.hermetic && (tool.name === `${TOOL_PREFIX}generatively_edit_clip` || READ_ONLY_OPS.has(tool.name))))
|
|
739
|
-
.map((tool) => [tool.name, tool]));
|
|
740
|
-
const records = [];
|
|
741
|
-
for (const [index, { op, input }] of ops.entries()) {
|
|
742
|
-
operationIndex = index;
|
|
743
|
-
const tool = byName.get(op);
|
|
744
|
-
if (!tool) {
|
|
745
|
-
const known = [...byName.keys()]
|
|
746
|
-
.map((name) => name.slice(TOOL_PREFIX.length))
|
|
747
|
-
.sort()
|
|
748
|
-
.join(", ");
|
|
749
|
-
records.push({
|
|
750
|
-
op,
|
|
751
|
-
ok: false,
|
|
752
|
-
error: `No timeline operation named "${op.slice(TOOL_PREFIX.length)}". Available: ${known}.`
|
|
753
|
-
});
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
try {
|
|
757
|
-
records.push({ op, ok: true, result: await tool.execute(input) });
|
|
758
|
-
}
|
|
759
|
-
catch (e) {
|
|
760
|
-
records.push({
|
|
761
|
-
op,
|
|
762
|
-
ok: false,
|
|
763
|
-
error: e instanceof Error ? e.message : String(e)
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return { records, state: bridge.finalState() };
|
|
768
|
-
}
|
|
769
372
|
/**
|
|
770
373
|
* The keys an op names its unit under, and which list each one means.
|
|
771
374
|
*
|
|
@@ -886,6 +489,9 @@ const editTimeline = {
|
|
|
886
489
|
const { TimelineSequence, TimelineSequenceVersion } = await import("@nodetool-ai/models");
|
|
887
490
|
const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
|
|
888
491
|
const generateMediaEdit = timelineMediaEditGenerator(run);
|
|
492
|
+
// Shared across retries, like the media edit generator: a retry re-applies
|
|
493
|
+
// every op, and a 3D bake already rendered must not render again.
|
|
494
|
+
const bakeModel3DClip = timelineModel3DBaker(run);
|
|
889
495
|
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
890
496
|
const sequence = await TimelineSequence.findById(timelineId);
|
|
891
497
|
// A sequence owned by someone else reads as missing — the rule the tRPC
|
|
@@ -894,7 +500,7 @@ const editTimeline = {
|
|
|
894
500
|
return { error: `Timeline ${timelineId} was not found.` };
|
|
895
501
|
}
|
|
896
502
|
const document = sequence.toDocument();
|
|
897
|
-
const { records, state } = await applyOps(run, sequence, document, ops, { generateMediaEdit });
|
|
503
|
+
const { records, state } = await applyOps(run, sequence, document, ops, { generateMediaEdit, bakeModel3DClip });
|
|
898
504
|
// The transcript rides along untouched — no timeline operation edits it,
|
|
899
505
|
// so the stored copy stays authoritative. Markers come back from the
|
|
900
506
|
// bridge, which seeded them and which the marker ops write to.
|
|
@@ -1134,7 +740,11 @@ const validateTimeline = {
|
|
|
1134
740
|
* whole-document write is that what was sent is what the sequence becomes.
|
|
1135
741
|
*/
|
|
1136
742
|
function documentToStore(raw) {
|
|
1137
|
-
|
|
743
|
+
const { name, id: _id, projectId: _projectId, workflowId: _workflowId, durationMs: _durationMs, createdAt: _createdAt, updatedAt: _updatedAt, ...document } = raw;
|
|
744
|
+
return {
|
|
745
|
+
...normalizeAuthoredDocument(document),
|
|
746
|
+
name: isString(name) && name.trim() ? name.trim() : undefined
|
|
747
|
+
};
|
|
1138
748
|
}
|
|
1139
749
|
/** The end of the last clip — what the sequence's stored duration means. */
|
|
1140
750
|
function documentDurationMs(document) {
|
|
@@ -1201,7 +811,19 @@ const setTimelineDocument = {
|
|
|
1201
811
|
};
|
|
1202
812
|
}
|
|
1203
813
|
}
|
|
1204
|
-
|
|
814
|
+
// `get_timeline` returns the sequence row's fields beside the document.
|
|
815
|
+
// A caller that sends that object back renames the sequence with `name`.
|
|
816
|
+
// The other row fields are owned by the row, so they are dropped rather
|
|
817
|
+
// than reported as stripped document fields.
|
|
818
|
+
const stored = documentToStore(raw);
|
|
819
|
+
const { settings, name } = stored;
|
|
820
|
+
// Sandbox code holds the `asset://<short id>.<ext>` form the asset tools
|
|
821
|
+
// print; the stored clip holds the asset row's full id.
|
|
822
|
+
const canonical = await canonicalizeTimelineAssetIds(stored.document, userAssetFinder(sequence.user_id));
|
|
823
|
+
if ("error" in canonical) {
|
|
824
|
+
return { error: canonical.error, written: false, timeline_id: sequence.id };
|
|
825
|
+
}
|
|
826
|
+
const { document } = canonical;
|
|
1205
827
|
// Document-level fps/width/height are sequence settings, so opts win and a
|
|
1206
828
|
// document that carries them configures the sequence instead of having
|
|
1207
829
|
// them stripped with a `field_stripped` warning.
|
|
@@ -1237,16 +859,19 @@ const setTimelineDocument = {
|
|
|
1237
859
|
saveType: "manual",
|
|
1238
860
|
name: snapshotName
|
|
1239
861
|
});
|
|
1240
|
-
const
|
|
862
|
+
const fields = {
|
|
863
|
+
document: JSON.stringify(document),
|
|
864
|
+
fps,
|
|
865
|
+
width,
|
|
866
|
+
height,
|
|
867
|
+
duration_ms: documentDurationMs(document)
|
|
868
|
+
};
|
|
869
|
+
if (name !== undefined) {
|
|
870
|
+
fields.name = name;
|
|
871
|
+
}
|
|
1241
872
|
let saved;
|
|
1242
873
|
try {
|
|
1243
|
-
saved = await TimelineSequence.updateFieldsIfUnchanged(sequence.id, sequence.updated_at,
|
|
1244
|
-
document: JSON.stringify(document),
|
|
1245
|
-
fps,
|
|
1246
|
-
width,
|
|
1247
|
-
height,
|
|
1248
|
-
duration_ms: durationMs
|
|
1249
|
-
});
|
|
874
|
+
saved = await TimelineSequence.updateFieldsIfUnchanged(sequence.id, sequence.updated_at, fields);
|
|
1250
875
|
}
|
|
1251
876
|
catch (error) {
|
|
1252
877
|
// The model rejects a document without tracks/clips/markers arrays. The
|
|
@@ -1273,6 +898,7 @@ const setTimelineDocument = {
|
|
|
1273
898
|
ok: true,
|
|
1274
899
|
written: true,
|
|
1275
900
|
timeline_id: saved.id,
|
|
901
|
+
name: saved.name,
|
|
1276
902
|
updated_at: saved.updated_at,
|
|
1277
903
|
undo_version: undo.version,
|
|
1278
904
|
fps: saved.fps,
|
|
@@ -2294,7 +1920,7 @@ const isolateSubject = {
|
|
|
2294
1920
|
let expectedUpdatedAt = sequence.updated_at;
|
|
2295
1921
|
const persist = async (next) => {
|
|
2296
1922
|
const clips = document.clips.map((c) => (c.id === next.id ? next : c));
|
|
2297
|
-
const saved = await TimelineSequence.updateDocumentIfUnchanged(
|
|
1923
|
+
const saved = await TimelineSequence.updateDocumentIfUnchanged(sequence.id, expectedUpdatedAt, { ...document, clips }, {
|
|
2298
1924
|
ops: [
|
|
2299
1925
|
{
|
|
2300
1926
|
tool: `${TOOL_PREFIX}set_generated_matte`,
|
|
@@ -2416,7 +2042,7 @@ const trackObject = {
|
|
|
2416
2042
|
let expectedUpdatedAt = sequence.updated_at;
|
|
2417
2043
|
let mediaTracks = document.mediaTracks ?? [];
|
|
2418
2044
|
const persist = async (next) => {
|
|
2419
|
-
const saved = await TimelineSequence.updateDocumentIfUnchanged(
|
|
2045
|
+
const saved = await TimelineSequence.updateDocumentIfUnchanged(sequence.id, expectedUpdatedAt, { ...document, mediaTracks: next }, { ops: [{ tool: "track_object", input: { clip_id: clip.id, track_id: trackId } }] });
|
|
2420
2046
|
if (!saved)
|
|
2421
2047
|
return false;
|
|
2422
2048
|
mediaTracks = next;
|
|
@@ -2495,7 +2121,12 @@ function normalizeForceOption(value) {
|
|
|
2495
2121
|
* state being replaced so the write is undoable, then a CAS write so a
|
|
2496
2122
|
* concurrent edit is reported rather than clobbered.
|
|
2497
2123
|
*/
|
|
2498
|
-
export async function writeTimelineCodeDocument(sequence,
|
|
2124
|
+
export async function writeTimelineCodeDocument(sequence, authored, snapshotName) {
|
|
2125
|
+
const canonical = await canonicalizeTimelineAssetIds(authored, userAssetFinder(sequence.user_id));
|
|
2126
|
+
if ("error" in canonical) {
|
|
2127
|
+
return { written: false, error: canonical.error, errors: [], warnings: [] };
|
|
2128
|
+
}
|
|
2129
|
+
const { document } = canonical;
|
|
2499
2130
|
const { validateTimelineSequence } = await import("@nodetool-ai/execution/timeline-debug");
|
|
2500
2131
|
const before = validateTimelineSequence(document, { fps: sequence.fps, width: sequence.width, height: sequence.height });
|
|
2501
2132
|
const errors = before.errors.map((issue) => issue.message);
|