videobook 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (565) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +195 -2
  4. package/bin/videobook.js +7 -0
  5. package/dist/acestep/client.js +203 -0
  6. package/dist/acestep/config.js +62 -0
  7. package/dist/acestep/errors.js +8 -0
  8. package/dist/acestep/process.js +175 -0
  9. package/dist/alive-captions/analyze.js +117 -0
  10. package/dist/alive-captions/audio.js +179 -0
  11. package/dist/alive-captions/frames.js +59 -0
  12. package/dist/alive-captions/model.js +37 -0
  13. package/dist/alive-captions/schema.js +49 -0
  14. package/dist/alive-captions/track.js +9 -0
  15. package/dist/api-key.js +33 -0
  16. package/dist/app-state.js +56 -0
  17. package/dist/app.js +257 -0
  18. package/dist/archive-b2.js +332 -0
  19. package/dist/archive-store.js +169 -0
  20. package/dist/artifact-label.js +36 -0
  21. package/dist/artifact-refs.js +49 -0
  22. package/dist/asset-tag-extraction.js +169 -0
  23. package/dist/asset-tags.js +228 -0
  24. package/dist/asset-usage.js +23 -0
  25. package/dist/audio-cue.js +71 -0
  26. package/dist/audio-encoding.js +32 -0
  27. package/dist/audio-sync-request.js +1 -0
  28. package/dist/audio-sync.js +223 -0
  29. package/dist/audio-trim-range.js +31 -0
  30. package/dist/book-context.js +31 -0
  31. package/dist/book-manager.js +374 -0
  32. package/dist/catalog-gc.js +26 -0
  33. package/dist/chain-executor.js +815 -0
  34. package/dist/chain-generation.js +115 -0
  35. package/dist/chain-plan.js +746 -0
  36. package/dist/chain-step-review.js +60 -0
  37. package/dist/character-name.js +25 -0
  38. package/dist/chat-generation-routing.js +73 -0
  39. package/dist/chat-generation-settings.js +78 -0
  40. package/dist/chat-models.js +44 -0
  41. package/dist/chat-prompt-rewrite.js +67 -0
  42. package/dist/chat-router.js +628 -0
  43. package/dist/chat-view-actions.js +49 -0
  44. package/dist/cli/args.js +242 -0
  45. package/dist/cli/config-command.js +103 -0
  46. package/dist/cli/doctor-command.js +289 -0
  47. package/dist/cli/entry.js +2 -0
  48. package/dist/cli/key-report.js +16 -0
  49. package/dist/cli/main.js +104 -0
  50. package/dist/cli/mcp-command.js +88 -0
  51. package/dist/cli/mcp-relay.js +51 -0
  52. package/dist/cli/open-browser.js +14 -0
  53. package/dist/cli/run-command.js +231 -0
  54. package/dist/cli/serve-command.js +62 -0
  55. package/dist/cli/setup-command.js +60 -0
  56. package/dist/cli/stdio-guard.js +8 -0
  57. package/dist/cold-open-model.js +7 -0
  58. package/dist/cold-open.js +79 -0
  59. package/dist/command-reference.js +95 -0
  60. package/dist/content-store.js +74 -0
  61. package/dist/cost.js +8 -0
  62. package/dist/crop-geometry.js +23 -0
  63. package/dist/dancing-workflow.js +447 -0
  64. package/dist/data-home.js +44 -0
  65. package/dist/diagnostics.js +109 -0
  66. package/dist/dialog-quality.js +34 -0
  67. package/dist/downloads-dest.js +56 -0
  68. package/dist/editing-moments.js +194 -0
  69. package/dist/elevenlabs-audio-extension.js +66 -0
  70. package/dist/engine.js +54 -0
  71. package/dist/env-file.js +91 -0
  72. package/dist/env-secrets.js +148 -0
  73. package/dist/ffmpeg-binary.js +12 -0
  74. package/dist/ffmpeg.js +425 -0
  75. package/dist/ffprobe-binary.js +77 -0
  76. package/dist/forget-asset.js +216 -0
  77. package/dist/frame-utils.js +27 -0
  78. package/dist/generation-credit-error.js +11 -0
  79. package/dist/generation-input-mentions.js +117 -0
  80. package/dist/generation-input-policy.js +68 -0
  81. package/dist/generation-reference-mentions.js +97 -0
  82. package/dist/goal-characters.js +49 -0
  83. package/dist/goal-plan-v3.js +273 -0
  84. package/dist/goal-plan.js +220 -0
  85. package/dist/goal-planner-prompt.js +28 -0
  86. package/dist/goal-prompt-budget.js +4 -0
  87. package/dist/goal-reference-images.js +25 -0
  88. package/dist/goal-state.js +261 -0
  89. package/dist/goal-stills.js +153 -0
  90. package/dist/goal-store.js +144 -0
  91. package/dist/goal-styles.js +144 -0
  92. package/dist/goal-v3-runtime.js +157 -0
  93. package/dist/guides/character_schema_guide.md +70 -0
  94. package/dist/guides/gpt_image_2_5_prompting_guide.md +21 -0
  95. package/dist/guides/grok_imagine_prompting_guide.md +72 -0
  96. package/dist/guides/image_reproduction_guide.md +84 -0
  97. package/dist/guides/kling_3_0_prompting_guide.md +136 -0
  98. package/dist/guides/nano_banana_pro_prompting_guide.md +28 -0
  99. package/dist/guides/seedance_2_prompting_guide.md +172 -0
  100. package/dist/http-access.js +49 -0
  101. package/dist/http-byte-range.js +31 -0
  102. package/dist/hydration.js +56 -0
  103. package/dist/image-compress.js +77 -0
  104. package/dist/image-edit-default.js +1 -0
  105. package/dist/image-edit-models.js +41 -0
  106. package/dist/image-edit-settings.js +11 -0
  107. package/dist/image-region-edit.js +32 -0
  108. package/dist/image-subject-map.js +523 -0
  109. package/dist/image.js +46 -0
  110. package/dist/index.js +12 -0
  111. package/dist/job-deadline.js +60 -0
  112. package/dist/jobs/_alibaba-upload.js +30 -0
  113. package/dist/jobs/_audio-chunks.js +31 -0
  114. package/dist/jobs/_download-into-asset.js +15 -0
  115. package/dist/jobs/_fal-shared.js +22 -0
  116. package/dist/jobs/_fal-upload.js +35 -0
  117. package/dist/jobs/_image-edit-adapter.js +213 -0
  118. package/dist/jobs/_kie-poll.js +119 -0
  119. package/dist/jobs/_kie-upload.js +32 -0
  120. package/dist/jobs/_lease.js +163 -0
  121. package/dist/jobs/_persist-output.js +19 -0
  122. package/dist/jobs/_storage-retry.js +21 -0
  123. package/dist/jobs/_transcribe.js +245 -0
  124. package/dist/jobs/_translate-transcript.js +270 -0
  125. package/dist/jobs/analyze-alive-captions.js +10 -0
  126. package/dist/jobs/analyze-editing-moments.js +104 -0
  127. package/dist/jobs/analyze-jokes.js +143 -0
  128. package/dist/jobs/analyze-keyword-placement.js +155 -0
  129. package/dist/jobs/analyze-music.js +14 -0
  130. package/dist/jobs/analyze-video.js +282 -0
  131. package/dist/jobs/apply-cuts.js +83 -0
  132. package/dist/jobs/apply-sfx.js +107 -0
  133. package/dist/jobs/archive-clip.js +458 -0
  134. package/dist/jobs/assemble-goal.js +98 -0
  135. package/dist/jobs/audio-boost.js +71 -0
  136. package/dist/jobs/audio-mix.js +161 -0
  137. package/dist/jobs/backfill-asset-tags.js +144 -0
  138. package/dist/jobs/burn-watermark.js +316 -0
  139. package/dist/jobs/change-video-speed.js +176 -0
  140. package/dist/jobs/clean-video-audio.js +207 -0
  141. package/dist/jobs/cold-open.js +166 -0
  142. package/dist/jobs/combine-goal.js +108 -0
  143. package/dist/jobs/complete-provider-task.js +185 -0
  144. package/dist/jobs/copy-library-asset.js +33 -0
  145. package/dist/jobs/cost-estimate-assessment.js +113 -0
  146. package/dist/jobs/crop-image.js +47 -0
  147. package/dist/jobs/crop-video.js +71 -0
  148. package/dist/jobs/describe-image.js +181 -0
  149. package/dist/jobs/detect-scenes.js +33 -0
  150. package/dist/jobs/doctor-fix.js +100 -0
  151. package/dist/jobs/download-video.js +427 -0
  152. package/dist/jobs/enqueue-search-index.js +24 -0
  153. package/dist/jobs/even-split.js +122 -0
  154. package/dist/jobs/export-sequence.js +365 -0
  155. package/dist/jobs/extend-audio.js +229 -0
  156. package/dist/jobs/extract-audio.js +72 -0
  157. package/dist/jobs/extract-frame.js +58 -0
  158. package/dist/jobs/extract-notebook-audio.js +82 -0
  159. package/dist/jobs/extract-notebook-frame.js +93 -0
  160. package/dist/jobs/extract-video-clip.js +8 -0
  161. package/dist/jobs/extract-video-poster.js +55 -0
  162. package/dist/jobs/extract-waveform.js +25 -0
  163. package/dist/jobs/finish-goal.js +255 -0
  164. package/dist/jobs/forget-asset.js +11 -0
  165. package/dist/jobs/generate-acestep-cover.js +219 -0
  166. package/dist/jobs/generate-media.js +70 -0
  167. package/dist/jobs/image-subjects.js +1059 -0
  168. package/dist/jobs/index.js +18 -0
  169. package/dist/jobs/inject-first-frame.js +175 -0
  170. package/dist/jobs/isolate-vocals.js +69 -0
  171. package/dist/jobs/kie-paygate.js +177 -0
  172. package/dist/jobs/label-music-sections.js +5 -0
  173. package/dist/jobs/osg-placement.js +135 -0
  174. package/dist/jobs/pad-video.js +72 -0
  175. package/dist/jobs/plan-goal.js +97 -0
  176. package/dist/jobs/post-record.js +21 -0
  177. package/dist/jobs/post-to-x.js +21 -0
  178. package/dist/jobs/prepare-search-reference.js +190 -0
  179. package/dist/jobs/prepare-temporal-search.js +16 -0
  180. package/dist/jobs/process-audio.js +67 -0
  181. package/dist/jobs/process-image.js +58 -0
  182. package/dist/jobs/process-upload.js +166 -0
  183. package/dist/jobs/process-video.js +438 -0
  184. package/dist/jobs/register-all.js +161 -0
  185. package/dist/jobs/relayout-notebook.js +78 -0
  186. package/dist/jobs/remove-sound.js +299 -0
  187. package/dist/jobs/render-project.js +380 -0
  188. package/dist/jobs/render-qr-outro.js +138 -0
  189. package/dist/jobs/render-timeline.js +283 -0
  190. package/dist/jobs/repair-search-indexes.js +42 -0
  191. package/dist/jobs/replace-video-audio.js +64 -0
  192. package/dist/jobs/restore-archive-clip.js +53 -0
  193. package/dist/jobs/reverse-video.js +53 -0
  194. package/dist/jobs/rewrite-script.js +173 -0
  195. package/dist/jobs/run-notebook.js +3335 -0
  196. package/dist/jobs/semantic-index.js +797 -0
  197. package/dist/jobs/smooth-scene-concat.js +257 -0
  198. package/dist/jobs/splice-video.js +280 -0
  199. package/dist/jobs/split-video.js +127 -0
  200. package/dist/jobs/suggest-cuts.js +149 -0
  201. package/dist/jobs/suggest-sfx.js +135 -0
  202. package/dist/jobs/sync-video-to-audio.js +329 -0
  203. package/dist/jobs/tag-asset.js +266 -0
  204. package/dist/jobs/task-failure.js +16 -0
  205. package/dist/jobs/timewarp-video.js +152 -0
  206. package/dist/jobs/transcribe-video-audio.js +117 -0
  207. package/dist/jobs/transcribe.js +35 -0
  208. package/dist/jobs/translate-transcript.js +55 -0
  209. package/dist/jobs/trim-audio.js +133 -0
  210. package/dist/jobs/trim-video.js +291 -0
  211. package/dist/jobs/xai-paygate.js +26 -0
  212. package/dist/keyword-placement-cache.js +74 -0
  213. package/dist/lazy-render.js +142 -0
  214. package/dist/library-browse.js +78 -0
  215. package/dist/lightning-defaults.js +206 -0
  216. package/dist/load-env.js +12 -0
  217. package/dist/local-search-models.js +206 -0
  218. package/dist/local-upload.js +318 -0
  219. package/dist/logger.js +69 -0
  220. package/dist/media/adapters/alibaba.js +20 -0
  221. package/dist/media/adapters/elevenlabs.js +58 -0
  222. package/dist/media/adapters/fal.js +45 -0
  223. package/dist/media/adapters/index.js +26 -0
  224. package/dist/media/adapters/kie.js +120 -0
  225. package/dist/media/adapters/pending.js +21 -0
  226. package/dist/media/adapters/types.js +5 -0
  227. package/dist/media/adapters/xai.js +18 -0
  228. package/dist/media/catalog.js +236 -0
  229. package/dist/media/complete.js +191 -0
  230. package/dist/media/cost.js +45 -0
  231. package/dist/media/descriptor.js +157 -0
  232. package/dist/media/endpoints.js +11 -0
  233. package/dist/media/frame-options.js +18 -0
  234. package/dist/media/insert-video-timing.js +17 -0
  235. package/dist/media/json-schema.js +75 -0
  236. package/dist/media/legacy-task-types.js +129 -0
  237. package/dist/media/listing.js +24 -0
  238. package/dist/media/model-id.js +14 -0
  239. package/dist/media/preview.js +86 -0
  240. package/dist/media/prompt-dialects.js +65 -0
  241. package/dist/media/ref-fields.js +106 -0
  242. package/dist/media/refs.js +131 -0
  243. package/dist/media/request.js +188 -0
  244. package/dist/media/types.js +6 -0
  245. package/dist/media/wire.js +161 -0
  246. package/dist/media-library.js +535 -0
  247. package/dist/media-output-path.js +33 -0
  248. package/dist/multyculty-workflow.js +757 -0
  249. package/dist/notebook-action-catalog.js +608 -0
  250. package/dist/notebook-cell-outputs.js +726 -0
  251. package/dist/notebook-credit-retry.js +45 -0
  252. package/dist/notebook-fixtures.js +8 -0
  253. package/dist/notebook-generation-contract.js +77 -0
  254. package/dist/notebook-generation-controls.js +145 -0
  255. package/dist/notebook-goal-layout.js +40 -0
  256. package/dist/notebook-graph.js +37 -0
  257. package/dist/notebook-image-edit.js +84 -0
  258. package/dist/notebook-initial-generation-prompt.js +222 -0
  259. package/dist/notebook-live-analysis.js +941 -0
  260. package/dist/notebook-media-ops.js +579 -0
  261. package/dist/notebook-mutations.js +20 -0
  262. package/dist/notebook-placement.js +41 -0
  263. package/dist/notebook-provider-failure.js +87 -0
  264. package/dist/notebook-published-post.js +26 -0
  265. package/dist/notebook-reference-resolver.js +227 -0
  266. package/dist/notebook-relayout.js +270 -0
  267. package/dist/notebook-run-plan.js +519 -0
  268. package/dist/notebook-shots.js +162 -0
  269. package/dist/notebook-workflow.js +856 -0
  270. package/dist/one-password.js +200 -0
  271. package/dist/openai-intelligence.js +99 -0
  272. package/dist/original-media-file.js +12 -0
  273. package/dist/osg-assets.js +36 -0
  274. package/dist/osg-placement-cache.js +64 -0
  275. package/dist/osg-settings.js +27 -0
  276. package/dist/paid-tools.js +22 -0
  277. package/dist/platform-guidance.js +26 -0
  278. package/dist/posts/dropbox-auth.js +91 -0
  279. package/dist/posts/dropbox-upload.js +132 -0
  280. package/dist/posts/icloud-drive.js +134 -0
  281. package/dist/posts/icloud-status.js +64 -0
  282. package/dist/posts/post-contract.js +2 -0
  283. package/dist/posts/post-copy-format.js +43 -0
  284. package/dist/posts/post-copy.js +193 -0
  285. package/dist/posts/post-folder.js +18 -0
  286. package/dist/posts/post-metrics.js +119 -0
  287. package/dist/posts/post-store.js +340 -0
  288. package/dist/posts/x-post-target.js +43 -0
  289. package/dist/preview-payload.js +90 -0
  290. package/dist/prompt-surfaces.js +22 -0
  291. package/dist/provider-prompt-transform.js +77 -0
  292. package/dist/provider-status-link.js +21 -0
  293. package/dist/provider-task-record.js +78 -0
  294. package/dist/provider-task-status.js +47 -0
  295. package/dist/provider-usage.js +294 -0
  296. package/dist/queue.js +810 -0
  297. package/dist/reference-video-duration.js +17 -0
  298. package/dist/remotion/BlurFillVideo.js +51 -0
  299. package/dist/remotion/KeywordGraphicsOverlay.js +68 -0
  300. package/dist/remotion/OsgOverlay.js +30 -0
  301. package/dist/remotion/QrOutroComposition.js +47 -0
  302. package/dist/remotion/RenderComposition.js +11 -0
  303. package/dist/remotion/browser.js +59 -0
  304. package/dist/remotion/constants.js +35 -0
  305. package/dist/remotion/index.js +66 -0
  306. package/dist/remotion/keyword-graphics.js +151 -0
  307. package/dist/remotion/keyword-placement.js +182 -0
  308. package/dist/remotion/osg-spec.js +198 -0
  309. package/dist/remotion/public/qr-outro-impact.wav +0 -0
  310. package/dist/remotion/qr-outro-audio.js +1 -0
  311. package/dist/remotion/qr-outro-spec.js +211 -0
  312. package/dist/remotion/shared-bundle.js +37 -0
  313. package/dist/remotion/subtitles/AliveCaptions.js +83 -0
  314. package/dist/remotion/subtitles/Highlight.js +58 -0
  315. package/dist/remotion/subtitles/Impact.js +67 -0
  316. package/dist/remotion/subtitles/SubtitleOverlay.js +9 -0
  317. package/dist/remotion/subtitles/alive-layout.js +321 -0
  318. package/dist/remotion/subtitles/alive-types.js +17 -0
  319. package/dist/remotion/subtitles/chunker.js +68 -0
  320. package/dist/remotion/subtitles/segments.js +42 -0
  321. package/dist/remotion/subtitles/styles.js +4 -0
  322. package/dist/remotion/subtitles/types.js +1 -0
  323. package/dist/remotion/util.js +18 -0
  324. package/dist/remotion-cancellation.js +15 -0
  325. package/dist/rename-helpers.js +204 -0
  326. package/dist/rhythm/analyze.js +128 -0
  327. package/dist/rhythm/contract.js +14 -0
  328. package/dist/rhythm/draft-model.js +19 -0
  329. package/dist/rhythm/energy.js +133 -0
  330. package/dist/rhythm/fft.js +44 -0
  331. package/dist/rhythm/model.js +71 -0
  332. package/dist/rhythm/music-analysis.js +124 -0
  333. package/dist/rhythm/music-draft.js +261 -0
  334. package/dist/rhythm/onset.js +101 -0
  335. package/dist/rhythm/run-worker.js +51 -0
  336. package/dist/rhythm/score.js +47 -0
  337. package/dist/rhythm/section-labels.js +168 -0
  338. package/dist/rhythm/sequence-rhythm.js +67 -0
  339. package/dist/rhythm/synthesize.js +103 -0
  340. package/dist/rhythm/timing.js +98 -0
  341. package/dist/rhythm/track.js +320 -0
  342. package/dist/rhythm/visual-fit.js +4 -0
  343. package/dist/rhythm/worker.js +25 -0
  344. package/dist/scene-detect.js +200 -0
  345. package/dist/scene-transition.js +12 -0
  346. package/dist/scheduled-posts.js +200 -0
  347. package/dist/search-index-coverage.js +13 -0
  348. package/dist/search-index-media.js +71 -0
  349. package/dist/search-reference-store.js +60 -0
  350. package/dist/search-scoring.js +17 -0
  351. package/dist/serve.js +125 -0
  352. package/dist/server-lock.js +143 -0
  353. package/dist/server.js +42 -0
  354. package/dist/srt.js +16 -0
  355. package/dist/tag-backfill-state.js +121 -0
  356. package/dist/tag-settings.js +30 -0
  357. package/dist/tag-vocabulary.js +237 -0
  358. package/dist/test-queue-cleanup.js +1 -0
  359. package/dist/time-parse.js +50 -0
  360. package/dist/timeline-helpers.js +554 -0
  361. package/dist/timewarp.js +87 -0
  362. package/dist/tools/_image-inputs.js +9 -0
  363. package/dist/tools/_media-refs.js +84 -0
  364. package/dist/tools/_shared.js +59 -0
  365. package/dist/tools/_suggest-shared.js +279 -0
  366. package/dist/tools/add-timeline-audio.js +73 -0
  367. package/dist/tools/alive-captions.js +157 -0
  368. package/dist/tools/analyze-jokes.js +56 -0
  369. package/dist/tools/analyze-video.js +66 -0
  370. package/dist/tools/animate-keywords.js +217 -0
  371. package/dist/tools/api-status.js +42 -0
  372. package/dist/tools/application-commands.js +34 -0
  373. package/dist/tools/apply-cuts.js +112 -0
  374. package/dist/tools/apply-sfx.js +152 -0
  375. package/dist/tools/apply-watermark.js +670 -0
  376. package/dist/tools/archive-clip.js +74 -0
  377. package/dist/tools/asset-history.js +61 -0
  378. package/dist/tools/asset-manifest.js +24 -0
  379. package/dist/tools/asset-tags.js +151 -0
  380. package/dist/tools/assign-slot.js +97 -0
  381. package/dist/tools/audio-boost.js +107 -0
  382. package/dist/tools/audio-mix.js +121 -0
  383. package/dist/tools/book-usage.js +16 -0
  384. package/dist/tools/chain.js +136 -0
  385. package/dist/tools/change-video-speed.js +105 -0
  386. package/dist/tools/chat-history.js +5 -0
  387. package/dist/tools/chat.js +848 -0
  388. package/dist/tools/clean-video-audio.js +97 -0
  389. package/dist/tools/clear-failed-tasks.js +66 -0
  390. package/dist/tools/cold-open.js +114 -0
  391. package/dist/tools/command-plan.js +1680 -0
  392. package/dist/tools/commands.js +1666 -0
  393. package/dist/tools/concat-images.js +236 -0
  394. package/dist/tools/connect-service.js +55 -0
  395. package/dist/tools/copy-file-to-clipboard.js +58 -0
  396. package/dist/tools/create-character.js +114 -0
  397. package/dist/tools/create-qart.js +253 -0
  398. package/dist/tools/create-qr-outro.js +149 -0
  399. package/dist/tools/create-qrbtf.js +215 -0
  400. package/dist/tools/create-watermark.js +97 -0
  401. package/dist/tools/crop-asset.js +18 -0
  402. package/dist/tools/crop-image.js +120 -0
  403. package/dist/tools/crop-video.js +97 -0
  404. package/dist/tools/delete-asset.js +104 -0
  405. package/dist/tools/describe-image.js +69 -0
  406. package/dist/tools/detect-scenes.js +44 -0
  407. package/dist/tools/doctor.js +126 -0
  408. package/dist/tools/download-video.js +114 -0
  409. package/dist/tools/duplicate-asset.js +184 -0
  410. package/dist/tools/edit-timeline.js +59 -0
  411. package/dist/tools/editing-moments.js +105 -0
  412. package/dist/tools/estimate-cost.js +171 -0
  413. package/dist/tools/even-split.js +295 -0
  414. package/dist/tools/export-asset-file.js +22 -0
  415. package/dist/tools/export-asset.js +140 -0
  416. package/dist/tools/export-sequence.js +250 -0
  417. package/dist/tools/export-to-downloads.js +54 -0
  418. package/dist/tools/export-transcript-srt.js +79 -0
  419. package/dist/tools/extend-audio.js +100 -0
  420. package/dist/tools/extract-audio.js +174 -0
  421. package/dist/tools/extract-frame.js +108 -0
  422. package/dist/tools/extract-notebook-audio.js +173 -0
  423. package/dist/tools/extract-notebook-frame.js +160 -0
  424. package/dist/tools/extract-video-clip.js +101 -0
  425. package/dist/tools/generate-media.js +157 -0
  426. package/dist/tools/generation-payload.js +48 -0
  427. package/dist/tools/get-timeline.js +12 -0
  428. package/dist/tools/get-watermark-config.js +38 -0
  429. package/dist/tools/goal.js +644 -0
  430. package/dist/tools/image-subjects.js +711 -0
  431. package/dist/tools/index.js +323 -0
  432. package/dist/tools/inject-first-frame.js +154 -0
  433. package/dist/tools/invert-image.js +70 -0
  434. package/dist/tools/isolate-vocals.js +66 -0
  435. package/dist/tools/list-assets.js +98 -0
  436. package/dist/tools/list-cell-generations.js +225 -0
  437. package/dist/tools/list-elevenlabs-voices.js +163 -0
  438. package/dist/tools/log-wrapper.js +29 -0
  439. package/dist/tools/make-cover-song.js +95 -0
  440. package/dist/tools/mark-asset-seen.js +23 -0
  441. package/dist/tools/media-library.js +245 -0
  442. package/dist/tools/move-timeline-audio.js +35 -0
  443. package/dist/tools/music-analysis.js +60 -0
  444. package/dist/tools/music-draft.js +29 -0
  445. package/dist/tools/pad-video.js +87 -0
  446. package/dist/tools/post-record.js +83 -0
  447. package/dist/tools/post.js +168 -0
  448. package/dist/tools/prompt-history.js +164 -0
  449. package/dist/tools/provider-key-status.js +248 -0
  450. package/dist/tools/qrbtf-render.js +849 -0
  451. package/dist/tools/read-asset-metadata.js +26 -0
  452. package/dist/tools/read-file-at-commit.js +29 -0
  453. package/dist/tools/read-transcript.js +41 -0
  454. package/dist/tools/reconcile-project-work.js +1452 -0
  455. package/dist/tools/recover-asset.js +28 -0
  456. package/dist/tools/remove-sound.js +72 -0
  457. package/dist/tools/remove-timeline-audio.js +34 -0
  458. package/dist/tools/remove-timeline-slot.js +40 -0
  459. package/dist/tools/rename-all.js +434 -0
  460. package/dist/tools/rename-asset.js +34 -0
  461. package/dist/tools/render-settings.js +47 -0
  462. package/dist/tools/render-timeline.js +120 -0
  463. package/dist/tools/render.js +384 -0
  464. package/dist/tools/reorder-character-images.js +45 -0
  465. package/dist/tools/replace-video-audio.js +133 -0
  466. package/dist/tools/restore-archive-clips.js +209 -0
  467. package/dist/tools/restore-asset-version.js +37 -0
  468. package/dist/tools/retry-generation.js +54 -0
  469. package/dist/tools/reverse-video.js +74 -0
  470. package/dist/tools/rewrite-prompt.js +388 -0
  471. package/dist/tools/rewrite-script.js +67 -0
  472. package/dist/tools/schema-from-apicity.js +66 -0
  473. package/dist/tools/search-archive.js +150 -0
  474. package/dist/tools/semantic-search.js +1214 -0
  475. package/dist/tools/sequence-rhythm.js +43 -0
  476. package/dist/tools/set-character-metadata.js +84 -0
  477. package/dist/tools/set-env-secret.js +40 -0
  478. package/dist/tools/set-osg.js +143 -0
  479. package/dist/tools/show-folder.js +37 -0
  480. package/dist/tools/smooth-scene-concat.js +122 -0
  481. package/dist/tools/splice-video.js +149 -0
  482. package/dist/tools/split-video.js +202 -0
  483. package/dist/tools/suggest-cuts.js +45 -0
  484. package/dist/tools/suggest-sfx.js +43 -0
  485. package/dist/tools/sync-video-to-audio.js +115 -0
  486. package/dist/tools/tag-backfill.js +177 -0
  487. package/dist/tools/timewarp-video.js +85 -0
  488. package/dist/tools/tool-def.js +1 -0
  489. package/dist/tools/tool-json-schema.js +116 -0
  490. package/dist/tools/transcribe.js +90 -0
  491. package/dist/tools/translate-subtitles.js +59 -0
  492. package/dist/tools/trim-audio.js +139 -0
  493. package/dist/tools/trim-video.js +168 -0
  494. package/dist/tools/update-script.js +25 -0
  495. package/dist/tools/update-timeline-audio.js +40 -0
  496. package/dist/tools/upload-file.js +41 -0
  497. package/dist/tools/verify-x-mentions.js +141 -0
  498. package/dist/tools/video-editing-command-contract.js +291 -0
  499. package/dist/tools/video-source-reference.js +30 -0
  500. package/dist/tools/video-time-input.js +18 -0
  501. package/dist/tools/videobook-domain.js +2021 -0
  502. package/dist/tools/wait-for-asset-ready.js +143 -0
  503. package/dist/tools/workspace-actions.js +54 -0
  504. package/dist/transcript-projection.js +202 -0
  505. package/dist/transcript-srt.js +33 -0
  506. package/dist/upload.js +76 -0
  507. package/dist/usage-providers.js +25 -0
  508. package/dist/video-analysis-projection.js +46 -0
  509. package/dist/video-poster.js +33 -0
  510. package/dist/video-proxy.js +99 -0
  511. package/dist/vision-regions.js +111 -0
  512. package/dist/voice-actions.js +90 -0
  513. package/dist/voice-audio.js +55 -0
  514. package/dist/voice-config.js +122 -0
  515. package/dist/voice-continuation.js +54 -0
  516. package/dist/voice-contract.js +1 -0
  517. package/dist/voice-http.js +145 -0
  518. package/dist/voice-reply-progress.js +55 -0
  519. package/dist/voice-session.js +992 -0
  520. package/dist/watermark-badge.js +259 -0
  521. package/dist/watermark-config.js +111 -0
  522. package/dist/watermark-handle-position.js +55 -0
  523. package/dist/watermark-platform.js +15 -0
  524. package/dist/watermark-timing.js +16 -0
  525. package/dist/workspace-action-catalog.js +315 -0
  526. package/dist/workspace-action-contract.js +21 -0
  527. package/dist/workspace-action-groups.js +98 -0
  528. package/dist/workspace-action-surfaces.js +7 -0
  529. package/dist/x/account.js +88 -0
  530. package/dist/x/authenticated-handle.js +30 -0
  531. package/dist/x/oauth-flow.js +191 -0
  532. package/dist/x/post-contract.js +1 -0
  533. package/dist/x/post-metadata.js +44 -0
  534. package/dist/x/post-to-x.js +456 -0
  535. package/dist/x/published-post-cell.js +90 -0
  536. package/dist/x/x-tokens.js +69 -0
  537. package/dist/ytdlp-binary.js +65 -0
  538. package/dist/zod-slots.js +334 -0
  539. package/package.json +84 -4
  540. package/ui/assets/alive-captions-BWb5nLKj.css +1 -0
  541. package/ui/assets/alive-captions-DPEr1nKM.js +1 -0
  542. package/ui/assets/confirm-bodies-D8ouHXKy.js +1 -0
  543. package/ui/assets/confirm-bodies-DrMhaXC6.css +1 -0
  544. package/ui/assets/fullscreen-B7nw4YbE.js +1 -0
  545. package/ui/assets/goal-ui-DK-xTCK0.js +2 -0
  546. package/ui/assets/goal-ui-DXdUM2KF.css +1 -0
  547. package/ui/assets/index-DzPAnCUj.css +1 -0
  548. package/ui/assets/index-kS1905s5.js +37 -0
  549. package/ui/assets/inspector-MlzQbTVo.js +1 -0
  550. package/ui/assets/jobs-ui-CoinOWx0.js +1 -0
  551. package/ui/assets/music-video-Cbe80kmQ.js +1 -0
  552. package/ui/assets/music-video-DPaiKKEq.css +1 -0
  553. package/ui/assets/ui-Oirr9VOf.css +1 -0
  554. package/ui/assets/ui-xo_Y1yQg.js +2 -0
  555. package/ui/assets/vendor-D2rfiZ5j.css +1 -0
  556. package/ui/assets/vendor-DfTCWgKm.js +19 -0
  557. package/ui/assets/vendor-flow-BXbeWxW7.js +7 -0
  558. package/ui/assets/vendor-flow-BnuhLJ6X.css +1 -0
  559. package/ui/assets/vendor-icons-CGqhCIXV.js +1 -0
  560. package/ui/assets/vendor-mcp-BTlEJcHV.js +67 -0
  561. package/ui/assets/vendor-react-Dj7D1fD4.js +9 -0
  562. package/ui/assets/vendor-remotion-CH1HXrlc.js +95 -0
  563. package/ui/assets/videobook-Qlp9HQFu.js +20 -0
  564. package/ui/favicon.svg +8 -0
  565. package/ui/index.html +126 -0
@@ -0,0 +1,644 @@
1
+ import { v7 as uuidv7 } from "uuid";
2
+ import { z } from "zod";
3
+ import { resolveNotebookCellArtifactId } from "videobook-engine";
4
+ import { firstEmptyNotebookGridRow, notebookGridTag, notebookOccupiedSlots, NOTEBOOK_GRID_CAPACITY } from "videobook-engine/notebook-grid";
5
+ import { queue } from "../queue.js";
6
+ import { cancelChainRun, getChainRun, preflightAndAccept, retryChainRun } from "../chain-executor.js";
7
+ import { compileFrameSteps, compileGoalV3Approval, compileStoryboardSteps, compileVideoSteps, compileReviseStillStep, goalBriefConstraints, goalPlanSchema, goalPlanV3Schema, goalStillPrompt, isGoalPlanV3, refreshGoalPlanV3 } from "../goal-plan.js";
8
+ import { buildGoalPlanV3 } from "../goal-plan-v3.js";
9
+ import { estimateGoalV3, goalMediaCharacters, resolveGoalMediaReferences, captureGoalSceneFrame } from "../goal-v3-runtime.js";
10
+ import { currentGoalCharacters, resolveGoalCharacters } from "../goal-characters.js";
11
+ import { goalStylesEqual } from "../goal-styles.js";
12
+ import { activeGoalStage, completedGoalVideos, LEGACY_GOAL_MESSAGE, publicGoal, reconcileFinish, reconcileGoal } from "../goal-state.js";
13
+ import { goalAttemptFingerprints, goalStageAttempts, goalStageFingerprint, goalStillAttempts, goalStillStaleReason, goalV3Fingerprint } from "../goal-stills.js";
14
+ import { createGoalCell, findGoalCell, goalContent, listGoals, readGoal, readGoalCell, requireGoalContent, saveGoal, writeGoalContent, } from "../goal-store.js";
15
+ import { estimateGoal } from "../jobs/plan-goal.js";
16
+ import { queueGoalCombination } from "../jobs/combine-goal.js";
17
+ import { queueGoalFinish } from "../jobs/finish-goal.js";
18
+ import { mutateNotebook, withNotebookMutation } from "../notebook-mutations.js";
19
+ import { arrangeGoalNotebook } from "../notebook-goal-layout.js";
20
+ import { advertisedAs } from "./tool-json-schema.js";
21
+ const json = (value) => ({ content: [{ type: "text", text: JSON.stringify(value) }] });
22
+ const errorMessage = (error) => error instanceof Error ? error.message : String(error);
23
+ const failure = (error) => ({ isError: true, content: [{ type: "text", text: errorMessage(error) }] });
24
+ const ACTIVE_STATUSES = new Set(["planning", "review", "running"]);
25
+ const V3_ASSEMBLY_MESSAGE = "Scene and video goals do not use storyboard editing or multi-clip assembly.";
26
+ const V3_FINISH_REVIEW_MESSAGE = "The video is in finishing review. Revise the scene or video before generating again.";
27
+ const V3_SETTINGS_MESSAGE = "Scene and video settings stay with this goal. Start a new goal to change them.";
28
+ const STALE_RETRY_MESSAGE = "The references or selected versions changed after this attempt. Regenerate using the current selection instead of retrying the older request.";
29
+ const stageSchema = z.enum(["frame", "storyboards", "videos"]);
30
+ const feedbackSchema = z.string().trim().min(1).max(300).regex(/^[^@]*$/u, "Use plain text without @ references in revision feedback");
31
+ function readNotebook(engine, notebookId) {
32
+ const notebook = engine.notebooks.read(notebookId);
33
+ if (!notebook.ok)
34
+ throw new Error(notebook.error.message);
35
+ return notebook.value;
36
+ }
37
+ async function goalSnapshot(engine, goal) {
38
+ const finished = reconcileFinish(engine, goal);
39
+ const current = await reconcileGoal(engine, finished, readNotebook(engine, finished.notebookId));
40
+ return publicGoal(engine, current, readNotebook(engine, finished.notebookId));
41
+ }
42
+ function requireModernGoal(goal) {
43
+ if (goal.planVersion === 3)
44
+ throw new Error(V3_ASSEMBLY_MESSAGE);
45
+ if (goal.planVersion !== 2)
46
+ throw new Error(LEGACY_GOAL_MESSAGE);
47
+ }
48
+ function assertRevision(engine, goal, revision) {
49
+ const content = requireGoalContent(engine, goal);
50
+ if (content.revision !== revision)
51
+ throw new Error("The goal changed. Review the current version before approving it.");
52
+ return content;
53
+ }
54
+ function requireRevision(engine, goal, revision) {
55
+ return goalPlanSchema.parse(assertRevision(engine, goal, revision).plan);
56
+ }
57
+ async function goalToolLookup(engine) {
58
+ const { getAllToolDefs } = await import("./index.js");
59
+ const defs = getAllToolDefs(engine);
60
+ return { defs, lookup: (name) => defs.find(def => def.name === name) };
61
+ }
62
+ function outputReference(document, output) {
63
+ const cell = output?.cellId ? document.cells.find(candidate => candidate.id === output.cellId) : null;
64
+ if (!cell || resolveNotebookCellArtifactId(document, cell) !== output?.artifactId) {
65
+ throw new Error("An approved frame changed or was removed. Regenerate it before continuing.");
66
+ }
67
+ return notebookGridTag(cell.slot);
68
+ }
69
+ async function ensureStageSpace(engine, goal, stage, count, plan) {
70
+ const placed = await mutateNotebook(engine, goal.notebookId, "goal_stage_space", document => {
71
+ const occupied = notebookOccupiedSlots(document.cells);
72
+ const needsTimeline = stage === "videos" && plan.shots.length > 1 && !document.cells.some(cell => cell.type === "timeline");
73
+ if (NOTEBOOK_GRID_CAPACITY - occupied.length < count * 2 + (needsTimeline ? 8 : 0)) {
74
+ throw new Error("The notebook needs two empty cells per stage output and a row for the timeline when combining clips");
75
+ }
76
+ arrangeGoalNotebook(document, { generators: count, images: stage === "videos" ? 0 : count, videos: stage === "videos" ? count : 0, timelines: needsTimeline ? 1 : 0 });
77
+ const arranged = arrangeGoalNotebook(document);
78
+ if (!needsTimeline)
79
+ return arranged;
80
+ const timeline = engine.notebooks.createCell({ type: "timeline", label: plan.title, slot: firstEmptyNotebookGridRow(notebookOccupiedSlots(arranged.cells)) });
81
+ return arrangeGoalNotebook({ ...arranged, cells: [...arranged.cells, timeline] });
82
+ });
83
+ if (!placed.ok)
84
+ throw new Error(placed.error.message);
85
+ }
86
+ async function ensureV3Space(engine, goal, stage) {
87
+ const placed = await mutateNotebook(engine, goal.notebookId, "goal_stage_space", document => {
88
+ const occupied = notebookOccupiedSlots(document.cells);
89
+ if (NOTEBOOK_GRID_CAPACITY - occupied.length < 2) {
90
+ throw new Error("The notebook needs two empty cells per stage output and a row for the timeline when combining clips");
91
+ }
92
+ return arrangeGoalNotebook(document, { generators: 1, images: stage === "videos" ? 0 : 1, videos: stage === "videos" ? 1 : 0 });
93
+ });
94
+ if (!placed.ok)
95
+ throw new Error(placed.error.message);
96
+ }
97
+ function v3Stage(goal, args, regenerate) {
98
+ if (!regenerate)
99
+ return goal.stage === "videos" ? "videos" : "frame";
100
+ const requested = args.stage !== undefined || args.action === "revise" ? stageSchema.parse(args.stage) : goal.stage === "videos" ? "frame" : null;
101
+ if (requested === "storyboards")
102
+ throw new Error(V3_ASSEMBLY_MESSAGE);
103
+ if (requested !== "frame" && requested !== "videos")
104
+ throw new Error("There are no stage outputs to regenerate yet");
105
+ return requested;
106
+ }
107
+ async function startV3Stage(engine, goal, args, regenerate) {
108
+ if (!regenerate && goal.status === "running" && goal.stages[activeGoalStage(goal)]?.revision === args.revision)
109
+ return goal;
110
+ if (!(regenerate ? ["review", "completed", "failed", "interrupted"] : ["review"]).includes(goal.status))
111
+ throw new Error("Review the goal before approving, regenerating or revising a stage");
112
+ const content = assertRevision(engine, goal, args.revision);
113
+ const stage = v3Stage(goal, args, regenerate);
114
+ const feedback = args.action === "revise" ? feedbackSchema.parse(args.feedback) : null;
115
+ if (regenerate && args.index !== undefined && z.number().int().nonnegative().parse(args.index) !== 0)
116
+ throw new Error("There is no current output to regenerate or revise");
117
+ if (regenerate && !goal.stages[stage]?.outputs.length)
118
+ throw new Error("There is no current output to regenerate or revise");
119
+ if (!regenerate && stage === "videos" && goal.finish)
120
+ throw new Error(V3_FINISH_REVIEW_MESSAGE);
121
+ if (!process.env.KIE_API_KEY)
122
+ throw new Error("Set KIE_API_KEY in Settings before generating this stage");
123
+ if (stage === "videos")
124
+ outputReference(readNotebook(engine, goal.notebookId), goal.stages.frame?.outputs[0]);
125
+ await ensureV3Space(engine, goal, stage);
126
+ const document = readNotebook(engine, goal.notebookId);
127
+ const plan = refreshGoalPlanV3(goalPlanV3Schema.parse(content.plan), document);
128
+ const sceneOutput = goal.stages.frame?.outputs[0];
129
+ const sceneAddress = stage === "videos" ? outputReference(document, sceneOutput) : undefined;
130
+ if (stage === "videos" && sceneOutput) {
131
+ const sceneAttempt = goal.stages.frame?.attempts?.find(attempt => attempt.artifactId === sceneOutput.artifactId && attempt.fingerprint);
132
+ if (sceneAttempt?.fingerprint && sceneAttempt.fingerprint !== await goalV3Fingerprint(engine, plan, "frame", null)) {
133
+ throw new Error("The scene has changed. Refresh it before continuing.");
134
+ }
135
+ }
136
+ const compiled = compileGoalV3Approval(plan, stage, sceneAddress);
137
+ const step = feedback ? { ...compiled, params: { ...compiled.params, prompt: `${String(compiled.params.prompt)}\n\n${feedback}` } } : compiled;
138
+ const fingerprint = await goalV3Fingerprint(engine, plan, stage, stage === "videos" ? sceneOutput?.artifactId ?? null : null);
139
+ const { defs, lookup } = await goalToolLookup(engine);
140
+ const chain = await preflightAndAccept({
141
+ action: "run", version: 1, book_id: goal.bookId, notebook_id: goal.notebookId,
142
+ request_id: `goal:${goal.goalId}:${stage}:${content.revision}${feedback ? `:revise:0:${goal.stages[stage]?.attempts?.length ?? 0}` : regenerate ? `:regenerate:0:${goal.stages[stage]?.attempts?.length ?? 0}` : ""}`,
143
+ steps: [step],
144
+ }, {
145
+ engine, bookId: goal.bookId, notebookId: goal.notebookId,
146
+ toolSchemas: new Map(defs.map(def => [def.name, def.schema])), autoApplyTools: new Set(),
147
+ effectForTool: name => name === "assemble_goal" ? "mutation" : "generation",
148
+ }, lookup, { actor: "chat" });
149
+ const previous = goal.stages[stage];
150
+ const approvedAt = new Date().toISOString();
151
+ const { finish: _finish, ...stored } = goal;
152
+ return saveGoal(engine, {
153
+ ...(regenerate ? stored : goal),
154
+ stage, chainId: chain.chainId, status: "running", error: null,
155
+ ...(regenerate ? { finish: null, resumeFinish: goal.finish ?? null } : {}),
156
+ resumeStage: regenerate ? goal.resumeStage ?? goal.stage : null,
157
+ resumeStatus: regenerate ? goal.status === "completed" || goal.resumeStatus === "completed" ? "completed" : "review" : null,
158
+ resumeOutputs: regenerate ? previous?.outputs ?? [] : null,
159
+ stages: { ...goal.stages, [stage]: {
160
+ chainId: chain.chainId, revision: content.revision, approvedAt, shotIndexes: [0],
161
+ outputs: previous?.outputs ?? [], previousChainIds: previous ? [...previous.previousChainIds, previous.chainId] : [],
162
+ attempts: [...(previous?.attempts ?? []), {
163
+ stage, index: 0, chainId: chain.chainId, stepId: step.id, artifactId: null, cellId: null,
164
+ basePrompt: stage === "videos" ? plan.video.prompt : plan.scene.prompt, feedback, fingerprint,
165
+ parentArtifactId: feedback ? previous?.outputs[0]?.artifactId ?? null : null,
166
+ frameArtifactId: stage === "videos" ? sceneOutput?.artifactId ?? null : null, createdAt: approvedAt,
167
+ }],
168
+ } },
169
+ });
170
+ }
171
+ async function startStage(engine, goal, args, regenerate) {
172
+ if (goal.planVersion === 1)
173
+ throw new Error(LEGACY_GOAL_MESSAGE);
174
+ if (goal.planVersion === 3)
175
+ return startV3Stage(engine, goal, args, regenerate);
176
+ requireModernGoal(goal);
177
+ const content = requireGoalContent(engine, goal);
178
+ if (!regenerate && goal.status === "running" && goal.stages[activeGoalStage(goal)]?.revision === args.revision)
179
+ return goal;
180
+ if (!(regenerate ? ["review", "completed", "failed", "interrupted"] : ["review"]).includes(goal.status))
181
+ throw new Error("Review the goal before approving, regenerating or revising a stage");
182
+ const plan = requireRevision(engine, goal, args.revision);
183
+ const feedback = args.action === "revise" ? feedbackSchema.parse(args.feedback) : null;
184
+ if (!process.env.KIE_API_KEY)
185
+ throw new Error("Set KIE_API_KEY in Settings before generating this stage");
186
+ const stage = regenerate
187
+ ? args.stage !== undefined || feedback ? stageSchema.parse(args.stage)
188
+ : goal.stage === "storyboards" ? "frame" : goal.stage === "videos" ? "storyboards" : (() => { throw new Error("There are no stage outputs to regenerate yet"); })()
189
+ : activeGoalStage(goal);
190
+ const index = regenerate && (args.index !== undefined || feedback) ? z.number().int().nonnegative().parse(args.index) : null;
191
+ if (regenerate) {
192
+ if (index !== null && stage !== "videos")
193
+ goalStillPrompt(plan, stage, index);
194
+ if (stage === "videos" && index !== null && !plan.shots[index])
195
+ throw new Error("There is no video at this index");
196
+ if (!goal.stages[stage]?.outputs.some(output => index === null || output.index === index))
197
+ throw new Error("There is no current output to regenerate or revise");
198
+ }
199
+ const indexes = index !== null ? [index] : stage === "frame" ? [0]
200
+ : regenerate && Array.isArray(args.shot_indexes) ? [...new Set(z.array(z.number().int().min(0).max(plan.shots.length - 1)).min(1).parse(args.shot_indexes))].sort((a, b) => a - b)
201
+ : plan.shots.map((_, index) => index);
202
+ let document = readNotebook(engine, goal.notebookId);
203
+ let characters = currentGoalCharacters(document, content.characters);
204
+ if (characters.length === 0)
205
+ throw new Error("This goal has no reference images. Start a new /goal.");
206
+ if (stage !== "frame") {
207
+ const frame = goal.stages.frame?.outputs[0];
208
+ outputReference(document, frame);
209
+ const stale = await goalStillStaleReason(engine, goal, document, plan, characters, "frame", frame);
210
+ if (stale)
211
+ throw new Error(stale);
212
+ }
213
+ if (stage === "videos") {
214
+ for (const output of goal.stages.storyboards?.outputs ?? []) {
215
+ if (!indexes.includes(output.index % plan.shots.length))
216
+ continue;
217
+ const stale = await goalStillStaleReason(engine, goal, document, plan, characters, "storyboards", output);
218
+ if (stale)
219
+ throw new Error(stale);
220
+ }
221
+ }
222
+ const track = stage === "videos" && plan.shots.length > 1
223
+ ? engine.sequences.getPrimary().tracks.find(candidate => candidate.kind === "video" && candidate.enabled && !candidate.locked) : null;
224
+ if (stage === "videos" && plan.shots.length > 1 && !track)
225
+ throw new Error("The sequence needs an enabled, unlocked video track");
226
+ const count = stage === "frame" || feedback || index !== null ? 1 : indexes.length * (stage === "storyboards" && plan.frameContinuity ? 2 : 1);
227
+ await ensureStageSpace(engine, goal, stage, count, plan);
228
+ document = readNotebook(engine, goal.notebookId);
229
+ characters = currentGoalCharacters(document, content.characters);
230
+ const steps = feedback && stage !== "videos" ? [compileReviseStillStep(plan, characters, stage, index, outputReference(document, goal.stages[stage]?.outputs.find(output => output.index === index)), feedback, stage === "storyboards" ? outputReference(document, goal.stages.frame?.outputs[0]) : undefined)]
231
+ : stage === "frame" ? compileFrameSteps(plan, characters)
232
+ : stage === "storyboards" ? compileStoryboardSteps(plan, characters, outputReference(document, goal.stages.frame?.outputs[0]), indexes.map(position => position % plan.shots.length))
233
+ .filter(step => index === null || step.id === `storyboard${index + 1}`)
234
+ : compileVideoSteps(goal.goalId, plan, plan.shots.map((_, index) => outputReference(document, goal.stages.storyboards?.outputs.find(output => output.index === index))), plan.frameContinuity ? plan.shots.map((_, index) => outputReference(document, goal.stages.storyboards?.outputs.find(output => output.index === index + plan.shots.length))) : [], { frame: outputReference(document, goal.stages.frame?.outputs[0]), characters, indexes, feedback, assemble: !regenerate });
235
+ const fingerprints = await goalAttemptFingerprints(engine, goal, plan, characters, stage, steps);
236
+ const { defs, lookup } = await goalToolLookup(engine);
237
+ const chain = await preflightAndAccept({
238
+ action: "run", version: 1, book_id: goal.bookId, notebook_id: goal.notebookId,
239
+ request_id: `goal:${goal.goalId}:${stage}:${content.revision}${feedback ? `:revise:${index}:${goal.stages[stage]?.attempts?.length ?? 0}` : regenerate ? `:regenerate:${indexes.join(",")}:${goal.stages[stage]?.attempts?.length ?? 0}` : ""}`, steps,
240
+ }, {
241
+ engine, bookId: goal.bookId, notebookId: goal.notebookId,
242
+ toolSchemas: new Map(defs.map(def => [def.name, def.schema])), autoApplyTools: new Set(),
243
+ effectForTool: name => name === "assemble_goal" ? "mutation" : "generation",
244
+ }, lookup, { actor: "chat" });
245
+ const previous = goal.stages[stage];
246
+ const approvedAt = new Date().toISOString();
247
+ return saveGoal(engine, {
248
+ ...goal, stage, chainId: chain.chainId, status: "running", error: null, resumeStage: regenerate ? goal.resumeStage ?? goal.stage : null,
249
+ resumeStatus: regenerate ? goal.status === "completed" || goal.resumeStatus === "completed" ? "completed" : "review" : null, resumeOutputs: regenerate ? previous?.outputs ?? [] : null,
250
+ ...(stage === "videos" ? { clipIds: goal.clipIds.length === plan.shots.length ? goal.clipIds : plan.shots.map(() => uuidv7()), trackId: goal.trackId ?? track?.trackId ?? null } : {}),
251
+ stages: { ...goal.stages, [stage]: {
252
+ chainId: chain.chainId, revision: content.revision, approvedAt, shotIndexes: indexes,
253
+ outputs: previous?.outputs ?? [], previousChainIds: previous ? [...previous.previousChainIds, previous.chainId] : [],
254
+ attempts: goalStillAttempts(goal, plan, stage, chain.chainId, steps, approvedAt, feedback, fingerprints, goalStageAttempts(engine, goal, stage)),
255
+ } },
256
+ });
257
+ }
258
+ function selectV3Version(engine, goal, args) {
259
+ if (!["review", "completed", "failed", "interrupted"].includes(goal.status))
260
+ throw new Error("Select a version while reviewing the goal");
261
+ if (args.revision !== undefined)
262
+ assertRevision(engine, goal, args.revision);
263
+ const stage = stageSchema.parse(args.stage);
264
+ if (stage === "storyboards")
265
+ throw new Error(V3_ASSEMBLY_MESSAGE);
266
+ const index = z.number().int().nonnegative().parse(args.index);
267
+ const artifactId = z.string().uuid().parse(args.artifact_id);
268
+ const run = goal.stages[stage];
269
+ const attempts = goalStageAttempts(engine, goal, stage);
270
+ const attempt = attempts.find(item => item.stage === stage && item.index === index && item.artifactId === artifactId);
271
+ const step = attempt ? getChainRun(engine, attempt.chainId)?.steps.find(item => item.id === attempt.stepId) : null;
272
+ if (!run || !attempt || step?.status !== "completed")
273
+ throw new Error("Choose a completed version of this goal's output");
274
+ const document = readNotebook(engine, goal.notebookId);
275
+ const producer = document.cells.find(cell => cell.inputs?.chainRunId === `${attempt.chainId}:${attempt.stepId}`);
276
+ const output = { index, artifactId, cellId: attempt.cellId, producerCellId: producer?.id ?? null, address: null };
277
+ const address = outputReference(document, output);
278
+ const videoChanged = stage === "videos" && run.outputs.find(item => item.index === index)?.artifactId !== artifactId;
279
+ const sceneChanged = stage === "frame" && run.outputs[0]?.artifactId !== artifactId;
280
+ const invalidated = videoChanged || sceneChanged;
281
+ const finish = videoChanged
282
+ ? { status: "review", sourceArtifactId: artifactId, finalArtifactId: null, error: null }
283
+ : sceneChanged ? null : goal.finish ?? null;
284
+ return saveGoal(engine, {
285
+ ...goal,
286
+ ...(invalidated ? { status: "review", error: null } : {}),
287
+ finish,
288
+ stages: { ...goal.stages, [stage]: {
289
+ ...run, attempts, outputs: [...run.outputs.filter(item => item.index !== index), { ...output, address }].sort((a, b) => a.index - b.index),
290
+ } },
291
+ });
292
+ }
293
+ function selectStill(engine, goal, args) {
294
+ if (goal.planVersion === 1)
295
+ throw new Error(LEGACY_GOAL_MESSAGE);
296
+ if (goal.planVersion === 3)
297
+ return selectV3Version(engine, goal, args);
298
+ requireModernGoal(goal);
299
+ if (!["review", "completed", "failed", "interrupted"].includes(goal.status))
300
+ throw new Error("Select a version while reviewing the goal");
301
+ if (args.revision !== undefined)
302
+ requireRevision(engine, goal, args.revision);
303
+ const stage = stageSchema.parse(args.stage);
304
+ const index = z.number().int().nonnegative().parse(args.index);
305
+ const artifactId = z.string().uuid().parse(args.artifact_id);
306
+ const run = goal.stages[stage];
307
+ const attempts = goalStageAttempts(engine, goal, stage);
308
+ const attempt = attempts.find(item => item.stage === stage && item.index === index && item.artifactId === artifactId);
309
+ const step = attempt ? getChainRun(engine, attempt.chainId)?.steps.find(item => item.id === attempt.stepId) : null;
310
+ if (!run || !attempt || step?.status !== "completed")
311
+ throw new Error("Choose a completed version of this goal's output");
312
+ const document = readNotebook(engine, goal.notebookId);
313
+ const producer = document.cells.find(cell => cell.inputs?.chainRunId === `${attempt.chainId}:${attempt.stepId}`);
314
+ const output = { index, artifactId, cellId: attempt.cellId, producerCellId: producer?.id ?? null, address: null };
315
+ const address = outputReference(document, output);
316
+ return saveGoal(engine, { ...goal, ...(stage === "videos" && goal.combined && run.outputs.find(item => item.index === index)?.artifactId !== artifactId ? { combined: { ...goal.combined, status: "failed", error: "Video selection changed. Assemble the selected clips before combining again." } } : {}), stages: { ...goal.stages, [stage]: {
317
+ ...run, attempts, outputs: [...run.outputs.filter(item => item.index !== index), { ...output, address }].sort((a, b) => a.index - b.index),
318
+ } } });
319
+ }
320
+ async function queuePlanning(engine, goal, operationId) {
321
+ const planning = saveGoal(engine, { ...goal, status: "planning", error: null });
322
+ try {
323
+ const job = await queue.enqueue(operationId, { type: "plan_goal", bookId: goal.bookId, goalId: goal.goalId });
324
+ return saveGoal(engine, { ...readGoal(engine, goal.goalId), planningJobId: job.id });
325
+ }
326
+ catch (error) {
327
+ saveGoal(engine, { ...planning, status: "failed", error: errorMessage(error) });
328
+ throw error;
329
+ }
330
+ }
331
+ async function createGoal(engine, bookId, notebookId, args) {
332
+ return withNotebookMutation(`goal-create:${bookId}`, async () => {
333
+ const brief = z.string().trim().min(1).max(6000).parse(args.brief);
334
+ const requestId = typeof args.request_id === "string" ? args.request_id : uuidv7();
335
+ const existing = listGoals(engine, notebookId).find(goal => goal.requestId === requestId);
336
+ if (existing) {
337
+ const cell = readGoalCell(engine, existing);
338
+ if (cell && goalContent(cell).brief !== brief)
339
+ throw new Error("This request_id already belongs to a different goal");
340
+ return goalSnapshot(engine, existing);
341
+ }
342
+ const document = readNotebook(engine, notebookId);
343
+ const supplied = args.plan === undefined ? undefined : z.union([goalPlanV3Schema, goalPlanSchema]).parse(args.plan);
344
+ const goalId = uuidv7();
345
+ const timestamp = new Date().toISOString();
346
+ if (supplied === undefined || isGoalPlanV3(supplied)) {
347
+ const sourced = supplied === undefined
348
+ ? await resolveGoalMediaReferences(engine, document, brief)
349
+ : supplied.references;
350
+ const plan = supplied ?? buildGoalPlanV3({ brief, references: sourced, frame: await captureGoalSceneFrame(engine, notebookId, sourced) });
351
+ const media = plan.references;
352
+ const characters = goalMediaCharacters(media);
353
+ const estimate = await estimateGoalV3(engine, plan);
354
+ const cell = await createGoalCell(engine, notebookId, goalId, { brief, characters, media, revision: 1, plan, estimate });
355
+ const goal = saveGoal(engine, {
356
+ goalId, bookId, notebookId, cellId: cell.id, requestId, status: "review", planVersion: 3, stage: "frame", stages: {},
357
+ chainId: null, planningJobId: null, clipIds: [], trackId: null, assembly: null, error: null,
358
+ dismissedAt: null, createdAt: timestamp, updatedAt: timestamp,
359
+ });
360
+ return goalSnapshot(engine, goal);
361
+ }
362
+ goalBriefConstraints(brief);
363
+ const characters = resolveGoalCharacters(engine, document, brief);
364
+ const plan = supplied;
365
+ const estimate = await estimateGoal(engine, plan, characters);
366
+ const cell = await createGoalCell(engine, notebookId, goalId, { brief, characters, revision: 1, plan, estimate });
367
+ const goal = saveGoal(engine, {
368
+ goalId, bookId, notebookId, cellId: cell.id, requestId, status: "review", planVersion: 2, stage: "frame", stages: {},
369
+ chainId: null, planningJobId: null, clipIds: [], trackId: null, assembly: null, error: null,
370
+ dismissedAt: null, createdAt: timestamp, updatedAt: timestamp,
371
+ });
372
+ return goalSnapshot(engine, goal);
373
+ });
374
+ }
375
+ function validateReviewEdit(goal, previous, plan) {
376
+ if (!Object.values(goal.stages).some(run => run.outputs.length > 0))
377
+ return;
378
+ if (previous.storyboardLayout === "six-panel") {
379
+ if (previous.shots.length !== plan.shots.length || previous.frameContinuity !== plan.frameContinuity || previous.storyboardLayout !== plan.storyboardLayout)
380
+ throw new Error("Keep the clip count and storyboard layout after generation. Start a new goal to change them.");
381
+ return;
382
+ }
383
+ if (goal.stage === "frame")
384
+ return;
385
+ if (previous.aspectRatio !== plan.aspectRatio || previous.imageModel !== plan.imageModel || previous.imageResolution !== plan.imageResolution
386
+ || previous.continuity !== plan.continuity || previous.shots.length !== plan.shots.length || !goalStylesEqual(previous.style, plan.style)) {
387
+ throw new Error("The characters, visual style, format and shot count are fixed after generating the full frame. Start a new goal to change them.");
388
+ }
389
+ if (previous.frameContinuity !== plan.frameContinuity)
390
+ throw new Error("Choose ending frames before generating the full frame. Start a new goal to change this setting.");
391
+ }
392
+ function v3LockedShape(plan) {
393
+ const ref = (item) => ({
394
+ kind: item.kind, artifactId: item.artifactId, cellId: item.cellId, slot: item.slot, objectHash: item.objectHash,
395
+ });
396
+ return JSON.stringify({
397
+ aspectRatio: plan.aspectRatio,
398
+ scene: { ...plan.scene, prompt: "", imageRefs: plan.scene.imageRefs.map(ref) },
399
+ video: {
400
+ ...plan.video, prompt: "",
401
+ imageRefs: plan.video.imageRefs.map(ref), videoRefs: plan.video.videoRefs.map(ref),
402
+ },
403
+ references: plan.references.map(ref),
404
+ sampledFrames: plan.sampledFrames,
405
+ finish: plan.finish,
406
+ });
407
+ }
408
+ async function updateV3Goal(engine, goal, args) {
409
+ if (!["review", "completed", "failed", "interrupted"].includes(goal.status))
410
+ throw new Error("Wait until generation stops before editing the goal");
411
+ const content = assertRevision(engine, goal, args.revision);
412
+ const previous = goalPlanV3Schema.parse(content.plan);
413
+ const supplied = args.plan;
414
+ if (!supplied || typeof supplied !== "object" || !("version" in supplied) || supplied.version !== 3)
415
+ throw new Error(V3_ASSEMBLY_MESSAGE);
416
+ const plan = goalPlanV3Schema.parse(supplied);
417
+ if (v3LockedShape(previous) !== v3LockedShape(plan))
418
+ throw new Error(V3_SETTINGS_MESSAGE);
419
+ if (JSON.stringify(previous) === JSON.stringify(plan))
420
+ return goal;
421
+ const promptChanged = previous.scene.prompt !== plan.scene.prompt || previous.video.prompt !== plan.video.prompt;
422
+ await writeGoalContent(engine, goal, { ...content, plan, revision: content.revision + 1 });
423
+ return saveGoal(engine, {
424
+ ...goal,
425
+ ...(promptChanged ? { status: "review", error: null, finish: null } : {}),
426
+ });
427
+ }
428
+ async function updateGoal(engine, goal, args) {
429
+ if (goal.planVersion === 3)
430
+ return updateV3Goal(engine, goal, args);
431
+ requireModernGoal(goal);
432
+ if (!["review", "completed", "failed", "interrupted"].includes(goal.status))
433
+ throw new Error("Wait until generation stops before editing the goal");
434
+ const previous = requireRevision(engine, goal, args.revision);
435
+ const content = requireGoalContent(engine, goal);
436
+ const plan = goalPlanSchema.parse(args.plan);
437
+ validateReviewEdit(goal, previous, plan);
438
+ if (JSON.stringify(previous) === JSON.stringify(plan))
439
+ return goal;
440
+ const estimate = await estimateGoal(engine, plan, content.characters);
441
+ await writeGoalContent(engine, goal, { ...content, plan, estimate, revision: content.revision + 1 });
442
+ return saveGoal(engine, goal);
443
+ }
444
+ function stopStillRun(engine, goal) {
445
+ const stage = activeGoalStage(goal);
446
+ const run = goal.stages[stage];
447
+ const previousChainId = run?.previousChainIds.at(-1);
448
+ if (goal.status !== "running" || !goal.resumeStage || !run || !previousChainId || !goal.chainId)
449
+ return null;
450
+ cancelChainRun(engine, goal.chainId);
451
+ const finish = goal.resumeFinish === undefined ? goal.finish : goal.resumeFinish;
452
+ return saveGoal(engine, {
453
+ ...goal, status: goal.resumeStatus ?? "review", stage: goal.resumeStage, chainId: previousChainId, error: null, resumeStage: null, resumeStatus: null, resumeOutputs: null,
454
+ ...(goal.resumeFinish !== undefined ? { resumeFinish: null, finish } : {}),
455
+ stages: { ...goal.stages, [stage]: {
456
+ ...run, outputs: goal.resumeOutputs ?? run.outputs, chainId: previousChainId, previousChainIds: [...run.previousChainIds.slice(0, -1), run.chainId],
457
+ } },
458
+ });
459
+ }
460
+ async function cancelGoal(engine, goal, options = {}) {
461
+ if (goal.planVersion === 3 && goal.finish?.status === "running") {
462
+ if (goal.finish.jobId)
463
+ await engine.jobs.queue.abort(goal.finish.jobId, "Finishing cancelled");
464
+ return saveGoal(engine, { ...goal, status: "review", error: null, finish: { ...goal.finish, status: "cancelled", error: null } });
465
+ }
466
+ if (!ACTIVE_STATUSES.has(goal.status))
467
+ return goal;
468
+ const resumed = options.discard ? null : stopStillRun(engine, goal);
469
+ if (resumed)
470
+ return resumed;
471
+ const cancelled = saveGoal(engine, { ...goal, status: "cancelled", error: null });
472
+ if (cancelled.chainId)
473
+ cancelChainRun(engine, cancelled.chainId);
474
+ if (cancelled.planningJobId)
475
+ await engine.jobs.queue.abort(cancelled.planningJobId, "Goal cancelled");
476
+ return cancelled;
477
+ }
478
+ async function retryV3Goal(engine, goal) {
479
+ if (goal.status !== "failed" && goal.status !== "interrupted")
480
+ throw new Error("Only a goal that failed or was interrupted can be retried");
481
+ const content = requireGoalContent(engine, goal);
482
+ if (!goal.chainId)
483
+ throw new Error("There is no generation to retry. Approve the current stage.");
484
+ const stage = activeGoalStage(goal);
485
+ if (stage === "storyboards")
486
+ throw new Error(V3_ASSEMBLY_MESSAGE);
487
+ const run = goal.stages[stage];
488
+ if (run && run.revision !== content.revision)
489
+ throw new Error("The plan changed after this attempt. Regenerate using the current plan instead of retrying the older request.");
490
+ const plan = refreshGoalPlanV3(goalPlanV3Schema.parse(content.plan), readNotebook(engine, goal.notebookId));
491
+ const sceneArtifactId = stage === "videos" ? goal.stages.frame?.outputs[0]?.artifactId ?? null : null;
492
+ for (const attempt of run?.attempts ?? []) {
493
+ if (attempt.chainId !== goal.chainId || !attempt.fingerprint)
494
+ continue;
495
+ if (attempt.fingerprint !== await goalV3Fingerprint(engine, plan, stage, sceneArtifactId))
496
+ throw new Error(STALE_RETRY_MESSAGE);
497
+ }
498
+ if (!process.env.KIE_API_KEY)
499
+ throw new Error("Set KIE_API_KEY in Settings before retrying this stage");
500
+ const { lookup } = await goalToolLookup(engine);
501
+ retryChainRun(engine, goal.chainId, lookup);
502
+ return saveGoal(engine, { ...goal, status: "running", error: null });
503
+ }
504
+ async function retryGoal(engine, goal) {
505
+ if (goal.planVersion === 1)
506
+ throw new Error(LEGACY_GOAL_MESSAGE);
507
+ if (goal.planVersion === 3)
508
+ return retryV3Goal(engine, goal);
509
+ requireModernGoal(goal);
510
+ if (goal.status !== "failed" && goal.status !== "interrupted")
511
+ throw new Error("Only a goal that failed or was interrupted can be retried");
512
+ const content = requireGoalContent(engine, goal);
513
+ if (!goal.chainId)
514
+ return queuePlanning(engine, goal, `goal-plan-${goal.goalId}-${uuidv7()}`);
515
+ const stage = activeGoalStage(goal);
516
+ const run = goal.stages[stage];
517
+ if (run && run.revision !== content.revision)
518
+ throw new Error("The plan changed after this attempt. Regenerate using the current plan instead of retrying the older request.");
519
+ const plan = goalPlanSchema.parse(content.plan);
520
+ for (const attempt of run?.attempts ?? []) {
521
+ if (attempt.chainId !== goal.chainId || !attempt.fingerprint)
522
+ continue;
523
+ if (attempt.fingerprint !== await goalStageFingerprint(engine, goal, plan, content.characters, stage, attempt.index)) {
524
+ throw new Error(STALE_RETRY_MESSAGE);
525
+ }
526
+ }
527
+ if (!process.env.KIE_API_KEY)
528
+ throw new Error("Set KIE_API_KEY in Settings before retrying this stage");
529
+ const { lookup } = await goalToolLookup(engine);
530
+ retryChainRun(engine, goal.chainId, lookup);
531
+ return saveGoal(engine, { ...goal, status: "running", error: null });
532
+ }
533
+ async function applyGoalAction(engine, notebookId, goalId, args) {
534
+ const stored = readGoal(engine, goalId);
535
+ if (stored.notebookId !== notebookId)
536
+ throw new Error("Goal does not belong to this notebook");
537
+ const goal = await reconcileGoal(engine, stored, readNotebook(engine, notebookId));
538
+ if (args.action === "update")
539
+ return updateGoal(engine, goal, args);
540
+ if (args.action === "select")
541
+ return selectStill(engine, goal, args);
542
+ if (args.action === "approve" || args.action === "regenerate" || args.action === "revise")
543
+ return startStage(engine, goal, args, args.action !== "approve");
544
+ if (args.action === "cancel")
545
+ return cancelGoal(engine, goal);
546
+ if (args.action === "finish")
547
+ return queueGoalFinish(engine, goal, args);
548
+ if (args.action === "retry")
549
+ return retryGoal(engine, goal);
550
+ if (args.action === "combine") {
551
+ requireModernGoal(goal);
552
+ return queueGoalCombination(engine, goal);
553
+ }
554
+ if (args.action === "dismiss") {
555
+ if (ACTIVE_STATUSES.has(goal.status))
556
+ throw new Error("Stop the goal before dismissing it");
557
+ return saveGoal(engine, { ...goal, dismissedAt: new Date().toISOString() });
558
+ }
559
+ return goal;
560
+ }
561
+ async function listGoalSnapshots(engine, notebook) {
562
+ const snapshots = [];
563
+ for (const goal of listGoals(engine, notebook.id)) {
564
+ await withNotebookMutation(`goal:${goal.goalId}`, async () => {
565
+ if (findGoalCell(notebook, goal))
566
+ snapshots.push(await goalSnapshot(engine, readGoal(engine, goal.goalId)));
567
+ else
568
+ await cancelGoal(engine, readGoal(engine, goal.goalId), { discard: true });
569
+ });
570
+ }
571
+ return snapshots;
572
+ }
573
+ export function goalDefs(engine) {
574
+ return [{
575
+ name: "video_goal", scope: "book",
576
+ description: "Run Goal mode on a notebook: create a goal from image and video references (by default one scene image, then one Seedance video with audio), then list, status, update, approve (required before paid generation), regenerate, select, revise, cancel, retry, assemble, combine, finish (cold open plus watermark) or dismiss it by goal_id.",
577
+ schema: {
578
+ book_id: z.string().uuid(), notebook_id: z.string().uuid(),
579
+ action: z.enum(["create", "list", "status", "update", "approve", "regenerate", "select", "revise", "cancel", "assemble", "retry", "dismiss", "combine", "finish"]),
580
+ start_seconds: z.number().min(0).optional(), end_seconds: z.number().min(0).optional(),
581
+ goal_id: z.string().uuid().optional(), request_id: z.string().min(1).max(200).optional(),
582
+ brief: z.string().trim().min(1).max(6000).optional(), plan: advertisedAs(z.union([goalPlanV3Schema, goalPlanSchema]), {
583
+ type: "object",
584
+ description: "A goal plan, usually the plan from a video_goal status result with fields edited in place",
585
+ }).optional(),
586
+ revision: z.number().int().nonnegative().optional(), shot_indexes: z.array(z.number().int().min(0).max(7)).min(1).max(8).optional(),
587
+ stage: stageSchema.optional(), index: z.number().int().min(0).max(15).optional(), artifact_id: z.string().uuid().optional(), feedback: feedbackSchema.optional(),
588
+ },
589
+ handler: async (args) => {
590
+ try {
591
+ const bookId = engine.book.get().bookId;
592
+ if (args.book_id !== bookId)
593
+ throw new Error("Goal book does not match the active book");
594
+ const notebookId = String(args.notebook_id);
595
+ const notebook = readNotebook(engine, notebookId);
596
+ if (args.action === "list")
597
+ return json({ goals: await listGoalSnapshots(engine, notebook) });
598
+ if (args.action === "create")
599
+ return json(await createGoal(engine, bookId, notebookId, args));
600
+ const goalId = z.string().uuid().parse(args.goal_id);
601
+ if (args.action === "assemble") {
602
+ const goal = readGoal(engine, goalId);
603
+ requireModernGoal(goal);
604
+ if (goal.notebookId !== notebookId)
605
+ throw new Error("Goal does not belong to this notebook");
606
+ const result = await assembleGoalDef(engine).handler({ book_id: bookId, goal_id: goalId, artifact_ids: completedGoalVideos(engine, goal) });
607
+ if (result.isError)
608
+ return result;
609
+ return json(await goalSnapshot(engine, readGoal(engine, goalId)));
610
+ }
611
+ return await withNotebookMutation(`goal:${goalId}`, async () => json(await goalSnapshot(engine, await applyGoalAction(engine, notebookId, goalId, args))));
612
+ }
613
+ catch (error) {
614
+ return failure(error);
615
+ }
616
+ },
617
+ }, assembleGoalDef(engine)];
618
+ }
619
+ function assembleGoalDef(engine) {
620
+ return {
621
+ name: "assemble_goal", scope: "book",
622
+ description: "Append a reviewed goal's completed video-stage clips in order to the sequence. Used by Goal mode for multi-clip plans; single-clip goals need no assembly.",
623
+ schema: { book_id: z.string().uuid(), goal_id: z.string().uuid(), artifact_ids: z.array(z.string().uuid()).min(2).max(8) },
624
+ handler: async (args) => {
625
+ try {
626
+ const goal = readGoal(engine, String(args.goal_id));
627
+ requireModernGoal(goal);
628
+ if (args.book_id !== goal.bookId || goal.status === "cancelled")
629
+ throw new Error("Goal is unavailable or cancelled");
630
+ const expected = completedGoalVideos(engine, goal);
631
+ if (expected.length < 2 || expected.length !== goal.clipIds.length || JSON.stringify(expected) !== JSON.stringify(args.artifact_ids)) {
632
+ throw new Error("Only this goal's completed shots can be assembled");
633
+ }
634
+ const result = await queue.enqueueAndWait(`goal-assemble-${goal.goalId}-${uuidv7()}`, {
635
+ type: "assemble_goal", bookId: goal.bookId, goalId: goal.goalId, artifactIds: expected,
636
+ });
637
+ return json(result);
638
+ }
639
+ catch (error) {
640
+ return failure(error);
641
+ }
642
+ },
643
+ };
644
+ }