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,848 @@
1
+ import { v7 as uuidv7 } from "uuid";
2
+ import { z } from "zod";
3
+ import { applyBackgroundAudioRoute, planBackgroundAudioFromQuery, applyBytedanceNamedModelRoute, applyStackedChainRoute, applyVideoJoinRoute, AUTO_APPLY_TOOL_NAMES, buildChatRoutingPrompt, buildChatRoutingUserMessage, buildRoutableTools, effectForTool, getRoutableTool, NESTED_TIMESTAMP_PARAMS, TIMESTAMP_PARAMS, } from "../chat-router.js";
4
+ import { ensureChainStepIds, planStackedChainFromQuery, preflightChainPlan, } from "../chain-plan.js";
5
+ import { withoutUnrequestedSettings } from "../chat-generation-settings.js";
6
+ import { resolveChatViewAction } from "../chat-view-actions.js";
7
+ import { rewriteChatGenerationPrompt } from "../chat-prompt-rewrite.js";
8
+ import { parseTime } from "../time-parse.js";
9
+ import { withLogging } from "./log-wrapper.js";
10
+ import { applyGenerationRoutingDefaults } from "../chat-generation-routing.js";
11
+ import { getApiKey } from "../api-key.js";
12
+ import { SCRIPT_FILENAME } from "../jobs/describe-image.js";
13
+ import { timelineSlotArtifactIds } from "../timeline-helpers.js";
14
+ import { expandSlotRefs, parseArtifactIdTags, resolveArtifactRefs, } from "../artifact-refs.js";
15
+ import { buildIntelligenceResponses, firstResponseText, intelligenceRequest, } from "../openai-intelligence.js";
16
+ import { chatIntelligenceRequestOptions, chatModelSchema, } from "../chat-models.js";
17
+ import { notebookGridAddress, } from "videobook-engine/notebook-grid";
18
+ import { NOTEBOOK_GRID_ADDRESS_SOURCE, replaceNotebookMentions, } from "videobook-engine/notebook-mentions";
19
+ import { notebookMediaAddresses, resolveNotebookPromptReferences, tagNotebookGridAddresses, } from "../notebook-reference-resolver.js";
20
+ const chatActionPlanSchema = z.object({
21
+ kind: z.literal("chat-action-plan"),
22
+ planId: z.string().uuid(),
23
+ bookId: z.string().uuid(),
24
+ prompt: z.string().min(1),
25
+ routedTool: z.string().min(1),
26
+ tool: z.string().min(1),
27
+ params: z.record(z.string(), z.unknown()),
28
+ message: z.string(),
29
+ summary: z.string().min(1),
30
+ effect: z.enum(["mutation", "generation", "view"]),
31
+ confirmation: z.object({
32
+ required: z.boolean(),
33
+ reason: z.string().min(1),
34
+ }),
35
+ });
36
+ const CHAT_ROUTING_MAX_OUTPUT_TOKENS = 2048;
37
+ const CHAT_ROUTING_TIMEOUT_MS = 45_000;
38
+ const NOTEBOOK_DESTINATION_SOURCE = NOTEBOOK_GRID_ADDRESS_SOURCE;
39
+ function duplicateDestinationReference(prompt) {
40
+ if (!/\b(?:copy|duplicate)\b/iu.test(prompt))
41
+ return null;
42
+ const explicit = new RegExp(`\\b(?:at|in|into|to)\\s+@(${NOTEBOOK_DESTINATION_SOURCE})(?![\\w-])`, "iu").exec(prompt);
43
+ if (explicit?.[1])
44
+ return explicit[1].toLowerCase();
45
+ const mentions = [...prompt.matchAll(new RegExp(`@(${NOTEBOOK_DESTINATION_SOURCE})(?![\\w-])`, "giu"))];
46
+ return mentions.length > 1
47
+ ? mentions.at(-1)?.[1]?.toLowerCase() ?? null
48
+ : null;
49
+ }
50
+ function buildChatNotebookContext(engine, notebook) {
51
+ if (!notebook)
52
+ return null;
53
+ return {
54
+ id: notebook.id,
55
+ name: notebook.name,
56
+ cells: notebook.cells.map((cell) => {
57
+ const artifact = cell.outputArtifactId
58
+ ? engine.artifacts.get(cell.outputArtifactId)
59
+ : null;
60
+ return {
61
+ address: `@${notebookGridAddress(cell.slot)}`,
62
+ cellId: cell.id,
63
+ ...(cell.label === undefined ? {} : { label: cell.label }),
64
+ type: cell.type,
65
+ outputAsset: artifact?.ok ? `@${artifact.value.artifactId}` : null,
66
+ };
67
+ }),
68
+ };
69
+ }
70
+ function attachNotebookReferences(assetContext, notebookContext) {
71
+ if (!notebookContext)
72
+ return assetContext;
73
+ return assetContext.map((asset) => {
74
+ const assetId = typeof asset.asset_id === "string" ? asset.asset_id : "";
75
+ const references = notebookContext.cells
76
+ .filter((cell) => cell.outputAsset === `@${assetId}`)
77
+ .map((cell) => cell.address);
78
+ return references.length > 0
79
+ ? { ...asset, notebook_references: references }
80
+ : asset;
81
+ });
82
+ }
83
+ function formatHistoryForPrompt(lines) {
84
+ if (lines.length === 0)
85
+ return "";
86
+ const formatted = lines.map((line) => line.role === "user"
87
+ ? `User: ${line.text ?? ""}`
88
+ : `Assistant: ${line.tool ? `[${line.tool}] ` : ""}${line.message ?? ""}`);
89
+ return `Recent conversation:\n${formatted.join("\n")}`;
90
+ }
91
+ function isMediaContextObject(value) {
92
+ return typeof value === "object" && value !== null && !Array.isArray(value);
93
+ }
94
+ function stripAssetToken(value) {
95
+ return value.replace(/^@/u, "").trim();
96
+ }
97
+ function stringAssetIds(value) {
98
+ if (!Array.isArray(value))
99
+ return [];
100
+ return value.flatMap((entry) => typeof entry === "string" && entry.trim()
101
+ ? [stripAssetToken(entry)]
102
+ : []);
103
+ }
104
+ function overlapNumber(value) {
105
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
106
+ return value;
107
+ if (typeof value === "string" && value.trim()) {
108
+ const parsed = Number(value);
109
+ if (Number.isFinite(parsed) && parsed >= 0)
110
+ return parsed;
111
+ }
112
+ return undefined;
113
+ }
114
+ export function normalizeChatRoutedParams(tool, source) {
115
+ const params = { ...source };
116
+ if (tool === "plan_chat_command") {
117
+ const context = params.media_context;
118
+ if (Array.isArray(context)) {
119
+ if (context.length === 1 && isMediaContextObject(context[0])) {
120
+ params.media_context = context[0];
121
+ }
122
+ else {
123
+ delete params.media_context;
124
+ }
125
+ }
126
+ return params;
127
+ }
128
+ if (tool === "chain") {
129
+ if ("steps" in params)
130
+ params.steps = ensureChainStepIds(params.steps);
131
+ return params;
132
+ }
133
+ if (tool !== "splice_video")
134
+ return params;
135
+ const assetIds = stringAssetIds(params.asset_ids);
136
+ if (typeof params.first_asset_id !== "string"
137
+ && assetIds.length >= 2) {
138
+ params.first_asset_id = assetIds[0];
139
+ params.second_asset_id = assetIds[1];
140
+ if (assetIds.length > 2)
141
+ params.additional_asset_ids = assetIds.slice(2);
142
+ }
143
+ if (typeof params.first_asset_id === "string") {
144
+ params.first_asset_id = stripAssetToken(params.first_asset_id);
145
+ }
146
+ if (typeof params.second_asset_id === "string") {
147
+ params.second_asset_id = stripAssetToken(params.second_asset_id);
148
+ }
149
+ const additional = stringAssetIds(params.additional_asset_ids);
150
+ if (additional.length > 0)
151
+ params.additional_asset_ids = additional;
152
+ const overlap = overlapNumber(params.overlap);
153
+ if (overlap !== undefined)
154
+ params.overlap = overlap;
155
+ else if (params.overlap === undefined)
156
+ params.overlap = 0;
157
+ delete params.asset_ids;
158
+ delete params.media_context;
159
+ return params;
160
+ }
161
+ export function normalizeChatTimestampParams(tool, source) {
162
+ const params = { ...source };
163
+ const timestampParams = TIMESTAMP_PARAMS[tool];
164
+ if (timestampParams) {
165
+ for (const paramName of timestampParams) {
166
+ if (!(paramName in params))
167
+ continue;
168
+ try {
169
+ params[paramName] = parseTime(params[paramName]);
170
+ }
171
+ catch {
172
+ continue;
173
+ }
174
+ }
175
+ }
176
+ const nestedTimestampParams = NESTED_TIMESTAMP_PARAMS[tool];
177
+ if (!nestedTimestampParams)
178
+ return params;
179
+ for (const [collectionName, fieldNames] of Object.entries(nestedTimestampParams)) {
180
+ const collection = params[collectionName];
181
+ if (!Array.isArray(collection))
182
+ continue;
183
+ params[collectionName] = collection.map((item) => {
184
+ if (typeof item !== "object" || item === null || Array.isArray(item))
185
+ return item;
186
+ const normalized = { ...item };
187
+ for (const fieldName of fieldNames) {
188
+ if (!(fieldName in normalized))
189
+ continue;
190
+ try {
191
+ normalized[fieldName] = parseTime(normalized[fieldName]);
192
+ }
193
+ catch {
194
+ continue;
195
+ }
196
+ }
197
+ return normalized;
198
+ });
199
+ }
200
+ return params;
201
+ }
202
+ /** Close open strings / braces / brackets so truncated Gemini JSON can parse. */
203
+ function closeTruncatedJson(text) {
204
+ let inString = false;
205
+ let escape = false;
206
+ const stack = [];
207
+ for (const ch of text) {
208
+ if (inString) {
209
+ if (escape) {
210
+ escape = false;
211
+ continue;
212
+ }
213
+ if (ch === "\\") {
214
+ escape = true;
215
+ continue;
216
+ }
217
+ if (ch === "\"")
218
+ inString = false;
219
+ continue;
220
+ }
221
+ if (ch === "\"") {
222
+ inString = true;
223
+ continue;
224
+ }
225
+ if (ch === "{")
226
+ stack.push("}");
227
+ else if (ch === "[")
228
+ stack.push("]");
229
+ else if (ch === "}" || ch === "]") {
230
+ if (stack.length > 0 && stack[stack.length - 1] === ch)
231
+ stack.pop();
232
+ }
233
+ }
234
+ let out = text;
235
+ if (inString)
236
+ out += "\"";
237
+ // Drop a trailing incomplete property fragment: , "key or , "key": value-less
238
+ out = out.replace(/,\s*"[^"]*"\s*:\s*$/u, "");
239
+ out = out.replace(/,\s*"[^"]*$/u, "");
240
+ out = out.replace(/,\s*$/u, "");
241
+ // Re-scan stack after stripping incomplete tails
242
+ inString = false;
243
+ escape = false;
244
+ stack.length = 0;
245
+ for (const ch of out) {
246
+ if (inString) {
247
+ if (escape) {
248
+ escape = false;
249
+ continue;
250
+ }
251
+ if (ch === "\\") {
252
+ escape = true;
253
+ continue;
254
+ }
255
+ if (ch === "\"")
256
+ inString = false;
257
+ continue;
258
+ }
259
+ if (ch === "\"") {
260
+ inString = true;
261
+ continue;
262
+ }
263
+ if (ch === "{")
264
+ stack.push("}");
265
+ else if (ch === "[")
266
+ stack.push("]");
267
+ else if (ch === "}" || ch === "]") {
268
+ if (stack.length > 0 && stack[stack.length - 1] === ch)
269
+ stack.pop();
270
+ }
271
+ }
272
+ if (inString)
273
+ out += "\"";
274
+ while (stack.length > 0)
275
+ out += stack.pop();
276
+ return out;
277
+ }
278
+ /** Best-effort repairs for common Gemini JSON glitches before JSON.parse. */
279
+ export function repairChatRoutingJson(text) {
280
+ let out = text.trim();
281
+ // Strip trailing commas before } or ]
282
+ out = out.replace(/,(\s*[}\]])/g, "$1");
283
+ // Quote bare http(s) URLs used as property values
284
+ out = out.replace(/(:\s*)(https?:\/\/[^\s,"}\]]+)/g, (_m, prefix, url) => `${prefix}${JSON.stringify(url)}`);
285
+ // Quote bare @slug tokens used as property values
286
+ out = out.replace(/(:\s*)(@[\w-]+)/g, (_m, prefix, slug) => `${prefix}${JSON.stringify(slug)}`);
287
+ // Close truncated objects (missing final } / message field) — the live
288
+ // failure mode for create_watermark_card routing under Kie load.
289
+ out = closeTruncatedJson(out);
290
+ return out;
291
+ }
292
+ export function parseChatDecision(outputText) {
293
+ const attempts = [outputText, repairChatRoutingJson(outputText)];
294
+ let lastError;
295
+ for (const candidate of attempts) {
296
+ try {
297
+ const parsed = JSON.parse(candidate);
298
+ if (parsed && typeof parsed === "object" && typeof parsed.tool === "string") {
299
+ return {
300
+ tool: parsed.tool,
301
+ params: (parsed.params && typeof parsed.params === "object"
302
+ ? parsed.params
303
+ : {}),
304
+ message: typeof parsed.message === "string" ? parsed.message : "",
305
+ };
306
+ }
307
+ lastError = new Error("Routing decision missing tool field");
308
+ }
309
+ catch (e) {
310
+ lastError = e;
311
+ }
312
+ }
313
+ const msg = lastError instanceof Error ? lastError.message : String(lastError);
314
+ const snippet = outputText.length > 200 ? `${outputText.slice(0, 200)}…` : outputText;
315
+ throw new Error(`${msg} (raw: ${JSON.stringify(snippet)})`);
316
+ }
317
+ export async function enrichAssetContext(engine, resolvedAssets) {
318
+ const rows = [];
319
+ for (const a of resolvedAssets) {
320
+ const row = {
321
+ tag: a.tag,
322
+ type: a.artifactType,
323
+ asset_id: a.artifactId,
324
+ has_file: a.filePath !== null,
325
+ };
326
+ const metaResult = await engine.metadata.artifacts.read(a.artifactId, "original");
327
+ if (metaResult.ok) {
328
+ const sourceUrl = typeof metaResult.value.source_url === "string"
329
+ ? metaResult.value.source_url
330
+ : typeof metaResult.value.webpage_url === "string"
331
+ ? metaResult.value.webpage_url
332
+ : null;
333
+ if (sourceUrl)
334
+ row.source_url = sourceUrl;
335
+ }
336
+ rows.push(row);
337
+ }
338
+ return rows;
339
+ }
340
+ export async function loadScriptContentsForChat(engine, resolvedAssets) {
341
+ const scriptContents = [];
342
+ for (const asset of resolvedAssets) {
343
+ if (asset.artifactType !== "script" || !asset.filePath)
344
+ continue;
345
+ const readResult = await engine.files.read(asset.artifactId, SCRIPT_FILENAME);
346
+ if (!readResult.ok)
347
+ continue;
348
+ const content = typeof readResult.value === "string"
349
+ ? readResult.value
350
+ : new TextDecoder().decode(readResult.value);
351
+ scriptContents.push({ tag: asset.tag, content });
352
+ }
353
+ return scriptContents;
354
+ }
355
+ function appendChatHistory(engine, input) {
356
+ void engine.logs
357
+ .append("chat", { role: "user", text: input.prompt, ts: Date.now() })
358
+ .catch(() => { });
359
+ void engine.logs
360
+ .append("chat", {
361
+ role: "assistant",
362
+ tool: input.tool,
363
+ params: input.params,
364
+ message: input.message,
365
+ error: input.failed,
366
+ ts: Date.now(),
367
+ })
368
+ .catch(() => { });
369
+ }
370
+ function autoApplyReason(tool, chainPausesPerStep = false) {
371
+ return tool === "duplicate_asset"
372
+ ? "Duplication is reversible and only adds a copied asset and cell"
373
+ : tool === "extract_audio"
374
+ ? "Audio extraction is free, local, and only adds derived media"
375
+ : tool === "chain"
376
+ ? chainPausesPerStep
377
+ ? "Every step that spends money stops for its own review before it runs"
378
+ : "This chain only reads or auto-applies existing local actions"
379
+ : "Frame extraction is free and only adds a new image cell";
380
+ }
381
+ async function applyChatActionPlan(engine, bookId, plan) {
382
+ if (plan.bookId !== bookId) {
383
+ return error("The chat action belongs to a different book");
384
+ }
385
+ if (plan.tool === "chat")
386
+ return error("Chat cannot route to itself");
387
+ if (plan.effect === "view")
388
+ return error("View actions open in the app and have nothing to apply");
389
+ const { getAllToolDefs } = await import("./index.js");
390
+ const targetDef = getAllToolDefs(engine).find((definition) => definition.name === plan.tool);
391
+ if (!targetDef)
392
+ return error(`Tool handler not found: ${plan.tool}`);
393
+ const params = { ...plan.params, book_id: bookId };
394
+ if (plan.tool === "chain" && "steps" in params) {
395
+ params.steps = ensureChainStepIds(params.steps);
396
+ }
397
+ const validated = z.object(targetDef.schema).safeParse(params);
398
+ if (!validated.success) {
399
+ return error(`Invalid parameters for ${plan.routedTool}: ${z.prettifyError(validated.error)}`);
400
+ }
401
+ const wrappedDef = withLogging(targetDef, engine);
402
+ let result;
403
+ try {
404
+ result = await wrappedDef.handler(validated.data);
405
+ }
406
+ catch (cause) {
407
+ const message = cause instanceof Error ? cause.message : String(cause);
408
+ result = error(`${plan.routedTool} failed: ${message}`);
409
+ }
410
+ appendChatHistory(engine, {
411
+ prompt: plan.prompt,
412
+ tool: plan.routedTool,
413
+ params: plan.params,
414
+ message: plan.message,
415
+ failed: Boolean(result.isError),
416
+ });
417
+ return result;
418
+ }
419
+ export { chatActionPlanSchema, duplicateDestinationReference };
420
+ export function chatDef(engine) {
421
+ return {
422
+ name: "chat",
423
+ description: "Route one composer request through the app's chat model: runs reads and returns reviewable plans for edits and generation. Agents should call the specific tools directly instead.",
424
+ schema: {
425
+ prompt: z.string().min(1).optional().describe("Natural language query with optional @asset tags"),
426
+ book_id: z.string().uuid().describe("Book UUID"),
427
+ notebook_id: z.string().uuid().optional().describe("Open notebook UUID for resolving grid addresses"),
428
+ model: chatModelSchema.optional().describe("Chat routing model"),
429
+ action_plan: chatActionPlanSchema.optional().describe("Previously previewed action to apply"),
430
+ },
431
+ handler: (args) => handleChatRequest(engine, args),
432
+ };
433
+ }
434
+ export function prepareVoiceChatAction(engine, input, decision) {
435
+ return handleChatRequest(engine, input, decision);
436
+ }
437
+ async function handleChatRequest(engine, args, proposedDecision) {
438
+ const { prompt, book_id: bookId, notebook_id: notebookId, model, action_plan: actionPlan } = args;
439
+ if (actionPlan)
440
+ return await applyChatActionPlan(engine, bookId, actionPlan);
441
+ if (!prompt)
442
+ return error("A chat prompt is required");
443
+ const chatRequest = chatIntelligenceRequestOptions(model);
444
+ const book = engine.book.get();
445
+ let notebookDocument = null;
446
+ try {
447
+ if (notebookId) {
448
+ const notebook = engine.notebooks.read(notebookId);
449
+ if (!notebook.ok)
450
+ throw new Error(notebook.error.message);
451
+ notebookDocument = notebook.value;
452
+ }
453
+ }
454
+ catch (cause) {
455
+ return error(cause instanceof Error ? cause.message : String(cause));
456
+ }
457
+ const notebookContext = buildChatNotebookContext(engine, notebookDocument);
458
+ const notebookAddresses = notebookDocument
459
+ ? notebookMediaAddresses(notebookDocument)
460
+ : new Set();
461
+ // 0. Tag bare grid addresses ("the image at A1") so the steps below see the
462
+ // reference the speaker meant instead of prose.
463
+ const taggedPrompt = tagNotebookGridAddresses(prompt, notebookAddresses);
464
+ // 1. Expand @s01 slot refs → the artifact ids they occupy
465
+ const expandedPrompt = expandSlotRefs(taggedPrompt, timelineSlotArtifactIds(engine));
466
+ const stackedPlan = planStackedChainFromQuery(expandedPrompt);
467
+ if (stackedPlan) {
468
+ const { getAllToolDefs } = await import("./index.js");
469
+ const allDefs = getAllToolDefs(engine);
470
+ const targetDef = allDefs.find((definition) => definition.name === "chain");
471
+ if (!targetDef)
472
+ return error("Tool handler not found: chain");
473
+ const preflight = await preflightChainPlan({
474
+ action: "run",
475
+ version: 1,
476
+ book_id: bookId,
477
+ ...(notebookId ? { notebook_id: notebookId } : {}),
478
+ steps: stackedPlan.steps,
479
+ }, {
480
+ engine,
481
+ bookId,
482
+ notebookId,
483
+ toolSchemas: new Map(allDefs.map((definition) => [definition.name, definition.schema])),
484
+ autoApplyTools: AUTO_APPLY_TOOL_NAMES,
485
+ effectForTool: (name) => effectForTool(name, /^(?:generate|extend|upscale)_/u.test(name) ? "generation" : "action"),
486
+ });
487
+ if (!preflight.ok)
488
+ return error(preflight.error);
489
+ const params = {
490
+ action: "run",
491
+ version: 1,
492
+ book_id: bookId,
493
+ ...(notebookId ? { notebook_id: notebookId } : {}),
494
+ steps: stackedPlan.steps,
495
+ };
496
+ const validated = z.object(targetDef.schema).safeParse(params);
497
+ if (!validated.success) {
498
+ return error(`Invalid parameters for chain: ${z.prettifyError(validated.error)}`);
499
+ }
500
+ const effect = preflight.plan.effect === "generation"
501
+ ? "generation"
502
+ : "mutation";
503
+ const autoApply = preflight.plan.autoApply;
504
+ return json({
505
+ kind: "chat-action-plan",
506
+ planId: uuidv7(),
507
+ bookId,
508
+ prompt: taggedPrompt,
509
+ routedTool: "chain",
510
+ tool: "chain",
511
+ params: validated.data,
512
+ message: stackedPlan.message,
513
+ summary: stackedPlan.message,
514
+ effect,
515
+ confirmation: {
516
+ required: !autoApply,
517
+ reason: autoApply
518
+ ? "This chain only reads or auto-applies existing local actions"
519
+ : "This request changes the current book",
520
+ },
521
+ });
522
+ }
523
+ const notebookResolution = notebookDocument
524
+ ? resolveNotebookPromptReferences({
525
+ engine: engine,
526
+ document: notebookDocument,
527
+ prompt: expandedPrompt,
528
+ requireMedia: false,
529
+ })
530
+ : null;
531
+ const duplicateDestination = duplicateDestinationReference(expandedPrompt);
532
+ const resolutionPrompt = notebookResolution && duplicateDestination
533
+ ? replaceNotebookMentions(expandedPrompt, notebookResolution.resolved
534
+ .filter((item) => item.reference !== duplicateDestination)
535
+ .map((item) => ({
536
+ reference: item.reference,
537
+ replacement: `@${item.artifactId}`,
538
+ })))
539
+ : notebookResolution?.prompt ?? expandedPrompt;
540
+ const unresolvedNotebookReferences = notebookResolution?.unresolved
541
+ .filter((issue) => issue.reference !== duplicateDestination)
542
+ ?? [];
543
+ if (unresolvedNotebookReferences.length > 0) {
544
+ return error(`Asset not found: ${unresolvedNotebookReferences
545
+ .map((issue) => `${issue.mention} (${issue.code})`)
546
+ .join(", ")}`);
547
+ }
548
+ // 2. Resolve @<artifact-id> tags
549
+ const { resolved: resolvedAssets, unresolved } = await resolveArtifactRefs(engine, parseArtifactIdTags(resolutionPrompt));
550
+ if (unresolved.length > 0) {
551
+ return error(`Asset not found: ${unresolved.join(", ")}`);
552
+ }
553
+ // 3. Build asset context for the LLM (include source_url when known)
554
+ const assetContext = attachNotebookReferences(await enrichAssetContext(engine, resolvedAssets), notebookContext);
555
+ const imageFiles = resolvedAssets
556
+ .filter((asset) => asset.artifactType === "image")
557
+ .map((asset) => asset.filePath)
558
+ .filter((path) => path !== null);
559
+ const videoFiles = resolvedAssets
560
+ .filter((asset) => asset.artifactType === "video")
561
+ .map((asset) => asset.filePath)
562
+ .filter((path) => path !== null);
563
+ const scriptContents = await loadScriptContentsForChat(engine, resolvedAssets);
564
+ // 4. Read recent history for context
565
+ const historyLines = engine.logs.read("chat", {
566
+ limit: 10,
567
+ });
568
+ const historyContext = formatHistoryForPrompt(historyLines);
569
+ const initialDecision = proposedDecision ?? planBackgroundAudioFromQuery({
570
+ query: expandedPrompt, assetContext, notebookContext,
571
+ });
572
+ const apiKey = getApiKey("OPENAI_API_KEY");
573
+ if (!apiKey && !initialDecision) {
574
+ return error("OPENAI_API_KEY environment variable is not set");
575
+ }
576
+ const { getAllToolDefs } = await import("./index.js");
577
+ const allDefs = getAllToolDefs(engine);
578
+ const routableTools = buildRoutableTools(allDefs);
579
+ const systemPrompt = buildChatRoutingPrompt(book.name, routableTools, historyContext || undefined);
580
+ const userMessage = buildChatRoutingUserMessage({
581
+ query: expandedPrompt,
582
+ assetContext,
583
+ imageFiles,
584
+ videoFiles,
585
+ scriptContents,
586
+ notebookContext,
587
+ });
588
+ let decision;
589
+ if (initialDecision) {
590
+ decision = initialDecision;
591
+ }
592
+ else
593
+ try {
594
+ const responses = buildIntelligenceResponses(apiKey ?? undefined, {
595
+ timeout: CHAT_ROUTING_TIMEOUT_MS,
596
+ model: chatRequest.model,
597
+ reasoningEffort: chatRequest.reasoningEffort,
598
+ });
599
+ let lastError;
600
+ decision = { tool: "", params: {}, message: "" };
601
+ for (let attempt = 0; attempt < 2; attempt++) {
602
+ try {
603
+ const response = await responses(intelligenceRequest({
604
+ instructions: systemPrompt,
605
+ input: userMessage,
606
+ text: { format: { type: "json_object" } },
607
+ max_output_tokens: CHAT_ROUTING_MAX_OUTPUT_TOKENS,
608
+ }, chatRequest), AbortSignal.timeout(CHAT_ROUTING_TIMEOUT_MS));
609
+ const outputText = firstResponseText(response);
610
+ decision = parseChatDecision(outputText);
611
+ lastError = null;
612
+ break;
613
+ }
614
+ catch (e) {
615
+ lastError = e;
616
+ }
617
+ }
618
+ if (lastError) {
619
+ const msg = lastError instanceof Error ? lastError.message : String(lastError);
620
+ return error(`Routing failed: ${msg}`);
621
+ }
622
+ }
623
+ catch (e) {
624
+ const msg = e instanceof Error ? e.message : String(e);
625
+ return error(`Routing failed: ${msg}`);
626
+ }
627
+ if (!decision.tool) {
628
+ return error("Could not determine which tool to use. Please rephrase.");
629
+ }
630
+ const bytedanceRoute = applyBytedanceNamedModelRoute({
631
+ query: expandedPrompt,
632
+ tool: decision.tool,
633
+ params: decision.params ?? {},
634
+ message: decision.message,
635
+ assetContext,
636
+ });
637
+ const joinRoute = bytedanceRoute.substituted
638
+ ? bytedanceRoute
639
+ : applyVideoJoinRoute({
640
+ query: expandedPrompt,
641
+ tool: bytedanceRoute.tool,
642
+ params: bytedanceRoute.params,
643
+ message: bytedanceRoute.message,
644
+ assetContext,
645
+ notebookContext,
646
+ });
647
+ const stackedRoute = joinRoute.substituted
648
+ ? joinRoute
649
+ : applyStackedChainRoute({
650
+ query: expandedPrompt,
651
+ tool: joinRoute.tool,
652
+ params: joinRoute.params,
653
+ message: joinRoute.message,
654
+ assetContext,
655
+ notebookContext,
656
+ });
657
+ const audioRoute = stackedRoute.substituted
658
+ ? stackedRoute
659
+ : applyBackgroundAudioRoute({
660
+ query: expandedPrompt,
661
+ tool: stackedRoute.tool,
662
+ params: stackedRoute.params,
663
+ message: stackedRoute.message,
664
+ assetContext,
665
+ notebookContext,
666
+ });
667
+ decision = {
668
+ tool: audioRoute.tool,
669
+ params: audioRoute.params,
670
+ // The voice path routes the spoken sentence here, so the sheet's title
671
+ // reads in the same vocabulary as the prompt beneath it.
672
+ message: tagNotebookGridAddresses(audioRoute.message, notebookAddresses),
673
+ };
674
+ // 6. Validate tool exists in routable registry
675
+ const routableTool = getRoutableTool(routableTools, decision.tool);
676
+ if (!routableTool) {
677
+ return error(`Unknown tool: ${decision.tool}. Please rephrase.`);
678
+ }
679
+ // A view opens in the app, so the plan carries the resolved target back
680
+ // instead of running a handler here.
681
+ if (routableTool.effect === "view") {
682
+ const view = resolveChatViewAction(engine, notebookDocument, routableTool.actualTool, decision.params ?? {});
683
+ if (!view.ok)
684
+ return error(view.message);
685
+ if (!proposedDecision)
686
+ appendChatHistory(engine, {
687
+ prompt: taggedPrompt,
688
+ tool: decision.tool,
689
+ params: view.params,
690
+ message: decision.message,
691
+ failed: false,
692
+ });
693
+ return json({
694
+ kind: "chat-action-plan",
695
+ planId: uuidv7(),
696
+ bookId,
697
+ prompt: taggedPrompt,
698
+ routedTool: decision.tool,
699
+ tool: routableTool.actualTool,
700
+ params: view.params,
701
+ message: decision.message,
702
+ summary: decision.message.trim() || "Show media fullscreen",
703
+ effect: "view",
704
+ confirmation: {
705
+ required: false,
706
+ reason: "Opening a view changes nothing in the book",
707
+ },
708
+ });
709
+ }
710
+ // 7. Apply fixups (spread to avoid mutating decision.params for logging)
711
+ const routedParams = normalizeChatRoutedParams(decision.tool, normalizeChatTimestampParams(decision.tool, decision.params ?? {}));
712
+ // The routing prompt asks the model to omit optional parameters so each tool
713
+ // can apply its defaults; enforce that for generation rather than trusting
714
+ // it, since an invented duration or a muted soundtrack costs a real
715
+ // generation. Reads the query, not the routed prompt, so only what the
716
+ // person actually said counts as asking.
717
+ const params = routableTool.category === "generation"
718
+ ? withoutUnrequestedSettings({
719
+ query: expandedPrompt,
720
+ params: routedParams,
721
+ requiredParams: routableTool.requiredParams,
722
+ }).params
723
+ : routedParams;
724
+ // Inject book_id
725
+ params.book_id = bookId;
726
+ if (notebookId
727
+ && routableTool.params.includes("notebook_id")
728
+ && params.notebook_id === undefined) {
729
+ params.notebook_id = notebookId;
730
+ }
731
+ // create_watermark_card needs a URL; if the model omitted it, take source_url
732
+ // from a referenced asset (e.g. "QR from the url in @vid-og").
733
+ if (decision.tool === "create_watermark_card" && (params.url === undefined || params.url === "")) {
734
+ const fromAsset = assetContext.find((a) => typeof a.source_url === "string");
735
+ if (fromAsset)
736
+ params.url = fromAsset.source_url;
737
+ }
738
+ // Inject script content as prompt for generation tools
739
+ const scriptIsPrompt = scriptContents.length > 0
740
+ && routableTool.category === "generation";
741
+ if (scriptIsPrompt) {
742
+ const combinedScript = scriptContents.map((s) => s.content).join("\n\n");
743
+ params.prompt = combinedScript;
744
+ }
745
+ // Generation routes: default the model, fold flat ref fields into refs, and
746
+ // default the ElevenLabs voice. Runs after the script injection above so
747
+ // script content reaches the prompt.
748
+ Object.assign(params, await applyGenerationRoutingDefaults(decision.tool, params));
749
+ // The voice model writes its own params, so a bare address can survive there
750
+ // even when the query carried none.
751
+ if (typeof params.prompt === "string") {
752
+ params.prompt = tagNotebookGridAddresses(params.prompt, notebookAddresses);
753
+ }
754
+ // Rewrite the routed prompt against the target model's guide, so the sheet
755
+ // reviews the prompt the provider will see rather than the sentence that
756
+ // picked the tool. A script the speaker attached is their own wording and
757
+ // stands as written. Runs last: the rewrite has to see resolved mentions to
758
+ // hand them back intact.
759
+ if (routableTool.category === "generation"
760
+ && !scriptIsPrompt
761
+ && typeof params.prompt === "string") {
762
+ params.prompt = await rewriteChatGenerationPrompt(engine, {
763
+ bookId,
764
+ model: typeof params.model === "string" ? params.model : "",
765
+ prompt: params.prompt,
766
+ });
767
+ }
768
+ // 8. Look up handler from tool defs
769
+ const targetDef = allDefs.find((d) => d.name === routableTool.actualTool);
770
+ if (!targetDef) {
771
+ return error(`Tool handler not found: ${routableTool.actualTool}`);
772
+ }
773
+ const validated = z.object(targetDef.schema).safeParse(params);
774
+ if (!validated.success) {
775
+ return error(`Invalid parameters for ${decision.tool}: ${z.prettifyError(validated.error)}`);
776
+ }
777
+ if (routableTool.effect !== "read") {
778
+ const effect = routableTool.effect;
779
+ const summary = decision.message.trim()
780
+ || `${effect === "generation" ? "Generate" : "Apply"} with ${decision.tool}`;
781
+ let autoApply = routableTool.autoApply;
782
+ let chainPausesPerStep = false;
783
+ if (targetDef.name === "chain") {
784
+ const preflight = await preflightChainPlan({
785
+ action: "run",
786
+ version: 1,
787
+ ...params,
788
+ book_id: bookId,
789
+ }, {
790
+ engine,
791
+ bookId,
792
+ notebookId,
793
+ toolSchemas: new Map(allDefs.map((definition) => [definition.name, definition.schema])),
794
+ autoApplyTools: AUTO_APPLY_TOOL_NAMES,
795
+ effectForTool: (name) => effectForTool(name, /^(?:generate|extend|upscale)_/u.test(name) ? "generation" : "action"),
796
+ });
797
+ if (!preflight.ok)
798
+ return error(preflight.error);
799
+ autoApply = preflight.plan.autoApply;
800
+ chainPausesPerStep = preflight.plan.steps.some((step) => step.requiresApproval);
801
+ }
802
+ return json({
803
+ kind: "chat-action-plan",
804
+ planId: uuidv7(),
805
+ bookId,
806
+ prompt: taggedPrompt,
807
+ routedTool: decision.tool,
808
+ tool: targetDef.name,
809
+ params: validated.data,
810
+ message: decision.message,
811
+ summary,
812
+ effect,
813
+ confirmation: {
814
+ required: !autoApply,
815
+ reason: autoApply
816
+ ? autoApplyReason(targetDef.name, chainPausesPerStep)
817
+ : effect === "generation"
818
+ ? "This request creates generated media and may incur provider spend"
819
+ : "This request changes the current book",
820
+ },
821
+ });
822
+ }
823
+ // 9. Execute (wrapped so the routed tool writes its own action log entry)
824
+ const wrappedDef = withLogging(targetDef, engine);
825
+ let handlerResult;
826
+ try {
827
+ handlerResult = await wrappedDef.handler(validated.data);
828
+ }
829
+ catch (cause) {
830
+ const message = cause instanceof Error ? cause.message : String(cause);
831
+ return error(`${decision.tool} failed: ${message}`);
832
+ }
833
+ if (!proposedDecision)
834
+ appendChatHistory(engine, {
835
+ prompt: taggedPrompt,
836
+ tool: decision.tool,
837
+ params: decision.params,
838
+ message: decision.message,
839
+ failed: Boolean(handlerResult.isError),
840
+ });
841
+ return handlerResult;
842
+ }
843
+ function error(text) {
844
+ return { isError: true, content: [{ type: "text", text }] };
845
+ }
846
+ function json(value) {
847
+ return { content: [{ type: "text", text: JSON.stringify(value) }] };
848
+ }