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,711 @@
1
+ import { notebookOccupiedSlots } from "videobook-engine/notebook-grid";
2
+ import { createHash } from "node:crypto";
3
+ import { z } from "zod";
4
+ import { nextHorizontalSlotFrom } from "videobook-engine";
5
+ import { notebookGridTag } from "videobook-engine/notebook-grid";
6
+ import { IMAGE_SUBJECT_MAP_VERSION, imageSubjectMapResponse, readImageSubjectMapRecord, resolveImageSubjectSource, subjectAnalysisProviderFailureMessage, writeCurrentImageSubjectAnalysisRecord, writeImageSubjectMapRecord, } from "../image-subject-map.js";
7
+ import { seedreamProImageUrlCapacityError, } from "../jobs/image-subjects.js";
8
+ import { DEFAULT_IMAGE_EDIT_MODEL_ID, findImageEditModel, IMAGE_EDIT_MODEL_IDS, IMAGE_EDIT_MODELS, } from "../image-edit-models.js";
9
+ import { readLastImageEditModel, writeLastImageEditModel } from "../image-edit-settings.js";
10
+ import { mutateNotebook } from "../notebook-mutations.js";
11
+ import { isPublishedPostCell } from "../notebook-published-post.js";
12
+ import { slotRight } from "../notebook-placement.js";
13
+ import { queue } from "../queue.js";
14
+ import { resolveImageRef } from "./_media-refs.js";
15
+ function failure(message) {
16
+ return {
17
+ isError: true,
18
+ content: [{ type: "text", text: message }],
19
+ };
20
+ }
21
+ function editModelCapacityError(entry, replacementCount) {
22
+ if (entry.id === "seedream/5-pro-image-to-image") {
23
+ return seedreamProImageUrlCapacityError(replacementCount);
24
+ }
25
+ const max = entry.maxInputImages ?? 1;
26
+ return `${entry.label} accepts at most ${max} image inputs (1 base + ${max - 1} replacements). This request needs 1 base + ${replacementCount} replacements.`;
27
+ }
28
+ function editCapability(operation) {
29
+ if (operation === "replace")
30
+ return "regionReplace";
31
+ if (operation === "whole_image")
32
+ return "wholeImage";
33
+ return "semantic";
34
+ }
35
+ function editOperationLabel(operation) {
36
+ if (operation === "replace")
37
+ return "region replacement";
38
+ if (operation === "whole_image")
39
+ return "whole-image edits";
40
+ return "semantic subject edits";
41
+ }
42
+ function json(value) {
43
+ return {
44
+ content: [{ type: "text", text: JSON.stringify(value) }],
45
+ };
46
+ }
47
+ function sourceCellAcceptsArtifact(cell, artifactId) {
48
+ if (isPublishedPostCell(cell))
49
+ return false;
50
+ if (cell.outputArtifactId === artifactId)
51
+ return true;
52
+ return Array.isArray(cell.inputs?.outputVersionArtifactIds)
53
+ && cell.inputs.outputVersionArtifactIds.includes(artifactId);
54
+ }
55
+ function uniqueSubjectIds(value) {
56
+ return [...new Set(value)];
57
+ }
58
+ const replacementBoxSchema = z.object({
59
+ x: z.number().min(0).max(1),
60
+ y: z.number().min(0).max(1),
61
+ width: z.number().positive().max(1),
62
+ height: z.number().positive().max(1),
63
+ }).strict();
64
+ const editBoxSchema = replacementBoxSchema.refine((box) => box.x < 1 && box.y < 1
65
+ && box.x + box.width <= 1.000001 && box.y + box.height <= 1.000001, { message: "Edit regions must fit inside the image" });
66
+ const replacementRegionSchema = z.object({
67
+ box: replacementBoxSchema.optional(),
68
+ subject_id: z.string().min(1).optional(),
69
+ replacement_artifact_id: z.string().min(1),
70
+ note: z.string().max(400).optional(),
71
+ }).strict().refine((region) => Boolean(region.box || region.subject_id), { message: "Each replacement region needs a box or subject_id" });
72
+ function parsedReplacementRegions(value) {
73
+ if (!Array.isArray(value))
74
+ return [];
75
+ return value.flatMap((entry) => {
76
+ const parsed = replacementRegionSchema.safeParse(entry);
77
+ if (!parsed.success)
78
+ return [];
79
+ const region = parsed.data;
80
+ return [{
81
+ replacementArtifactId: region.replacement_artifact_id,
82
+ ...(region.box ? { box: region.box } : {}),
83
+ ...(region.subject_id ? { subjectId: region.subject_id } : {}),
84
+ ...(region.note ? { note: region.note } : {}),
85
+ }];
86
+ });
87
+ }
88
+ export function reuseImageSubjectAnalysis(record, objectHash, force, active) {
89
+ return record?.objectHash === objectHash
90
+ && ((record.status === "analyzing" && active)
91
+ || (record.status === "ready" && !force));
92
+ }
93
+ const ACTIVE_IMAGE_SUBJECT_ANALYSIS_STATES = new Set([
94
+ "queued",
95
+ "running",
96
+ "completing",
97
+ ]);
98
+ function matchingImageSubjectAnalysisJobs(engine, record) {
99
+ return engine.jobs.queue.list({
100
+ states: ["queued", "running", "completing", "done", "failed", "aborted"],
101
+ type: "analyze_image_subjects",
102
+ }).filter((job) => job.payload.jobId === record.jobId
103
+ && job.payload.sourceArtifactId === record.artifactId
104
+ && job.payload.sourceObjectHash === record.objectHash);
105
+ }
106
+ function terminalJobError(job) {
107
+ if (job.error?.message)
108
+ return job.error.message;
109
+ if (!job.result || typeof job.result !== "object" || Array.isArray(job.result)) {
110
+ return null;
111
+ }
112
+ const result = job.result;
113
+ if (typeof result.error === "string" && result.error.trim()) {
114
+ return result.error.trim();
115
+ }
116
+ if (result.error
117
+ && typeof result.error === "object"
118
+ && !Array.isArray(result.error)
119
+ && typeof result.error.message === "string") {
120
+ return result.error.message.trim() || null;
121
+ }
122
+ return result.success === false && typeof result.message === "string"
123
+ ? result.message.trim() || null
124
+ : null;
125
+ }
126
+ function projectedSubjectAnalysisError(detail, state) {
127
+ const normalized = detail.trim();
128
+ if (/^internal error[.!]?$/iu.test(normalized)) {
129
+ return subjectAnalysisProviderFailureMessage(normalized);
130
+ }
131
+ if (normalized.startsWith("Subject analysis provider failed:")) {
132
+ return normalized;
133
+ }
134
+ const withoutPeriod = normalized.replace(/[.\s]+$/u, "");
135
+ const lower = withoutPeriod.toLowerCase();
136
+ if (lower.includes("did not finish within")
137
+ || lower.includes("deadline")
138
+ || lower.includes("lease expired")) {
139
+ if (lower.includes("retry"))
140
+ return normalized;
141
+ return `${withoutPeriod}. Retry the analysis.`;
142
+ }
143
+ return `Subject analysis job ${state}: ${withoutPeriod}. Retry the analysis.`;
144
+ }
145
+ function projectImageSubjectAnalysisRecord(engine, record) {
146
+ if (record?.status === "failed" && /^internal error[.!]?$/iu.test(record.error.trim())) {
147
+ return {
148
+ ...record,
149
+ error: subjectAnalysisProviderFailureMessage(record.error),
150
+ };
151
+ }
152
+ if (record?.status !== "analyzing")
153
+ return record;
154
+ const jobs = matchingImageSubjectAnalysisJobs(engine, record);
155
+ if (jobs.some((job) => ACTIVE_IMAGE_SUBJECT_ANALYSIS_STATES.has(job.state))) {
156
+ return record;
157
+ }
158
+ const terminal = jobs.find((job) => job.state === "failed" || job.state === "aborted" || job.state === "done");
159
+ const detail = terminal ? terminalJobError(terminal) : null;
160
+ const error = detail
161
+ ? projectedSubjectAnalysisError(detail, terminal.state)
162
+ : terminal?.state === "done"
163
+ ? "Subject analysis completed without publishing subjects. Retry the analysis."
164
+ : terminal
165
+ ? `Subject analysis job ${terminal.state}. Retry the analysis.`
166
+ : "Subject analysis is no longer active. Retry the analysis.";
167
+ return {
168
+ version: IMAGE_SUBJECT_MAP_VERSION,
169
+ status: "failed",
170
+ artifactId: record.artifactId,
171
+ objectHash: record.objectHash,
172
+ width: record.width,
173
+ height: record.height,
174
+ jobId: record.jobId,
175
+ error,
176
+ failedAt: terminal?.finishedAt
177
+ ? new Date(terminal.finishedAt).toISOString()
178
+ : new Date().toISOString(),
179
+ ...(record.taskId ? { taskId: record.taskId } : {}),
180
+ };
181
+ }
182
+ export function imageSubjectAnalysisResumeTaskId(record, objectHash, active) {
183
+ return record?.status === "analyzing"
184
+ && record.objectHash === objectHash
185
+ && active
186
+ ? record.taskId
187
+ : undefined;
188
+ }
189
+ export function imageSubjectAnalysisJobIsActive(engine, jobId) {
190
+ return engine.jobs.queue.list({
191
+ states: ["queued", "running", "completing"],
192
+ type: "analyze_image_subjects",
193
+ }).some((job) => job.payload.jobId === jobId);
194
+ }
195
+ export function imageSubjectAnalysisJobId(artifactId, objectHash, priorJobId, force) {
196
+ const key = `${artifactId}:${objectHash}`;
197
+ const seed = force || priorJobId
198
+ ? `${key}:retry:${priorJobId ?? "none"}`
199
+ : `${key}:v${IMAGE_SUBJECT_MAP_VERSION}`;
200
+ return `image-subject-analysis-${createHash("sha256")
201
+ .update(seed)
202
+ .digest("hex")
203
+ .slice(0, 24)}`;
204
+ }
205
+ async function rollbackEditPlacement(engine, notebookId, producerCellId, outputCellId) {
206
+ await mutateNotebook(engine, notebookId, "rollback_image_subject_edit", (notebook) => {
207
+ const removed = new Set([producerCellId, outputCellId]);
208
+ const execution = { ...(notebook.execution ?? {}) };
209
+ delete execution[producerCellId];
210
+ delete execution[outputCellId];
211
+ return {
212
+ ...notebook,
213
+ cells: notebook.cells.filter((cell) => !removed.has(cell.id)),
214
+ edges: notebook.edges.filter((edge) => !removed.has(edge.source) && !removed.has(edge.target)),
215
+ execution,
216
+ };
217
+ }, { producerCellId, outputCellId });
218
+ }
219
+ export function imageSubjectDefs(engine) {
220
+ return [
221
+ {
222
+ name: "list_image_edit_models",
223
+ description: "List the image-edit models available to edit_image_subjects, with per-model capabilities, input caps, key availability and the book's last-used model",
224
+ annotations: {
225
+ readOnlyHint: true,
226
+ destructiveHint: false,
227
+ idempotentHint: true,
228
+ openWorldHint: false,
229
+ },
230
+ schema: {
231
+ book_id: z.string().min(1),
232
+ },
233
+ handler: async () => json({
234
+ models: IMAGE_EDIT_MODELS.map((entry) => ({
235
+ id: entry.id,
236
+ label: entry.label,
237
+ provider: entry.provider,
238
+ capabilities: entry.capabilities,
239
+ max_input_images: entry.maxInputImages,
240
+ available: Boolean(process.env[entry.envKey]),
241
+ })),
242
+ default_model: DEFAULT_IMAGE_EDIT_MODEL_ID,
243
+ last_used_model: readLastImageEditModel(engine),
244
+ }),
245
+ },
246
+ {
247
+ name: "get_image_subject_map",
248
+ description: "Read cached automatic image subjects without starting provider work",
249
+ annotations: {
250
+ readOnlyHint: true,
251
+ destructiveHint: false,
252
+ idempotentHint: true,
253
+ openWorldHint: false,
254
+ },
255
+ schema: {
256
+ book_id: z.string().min(1),
257
+ artifact_id: z.string().min(1),
258
+ object_hash: z.string().min(1),
259
+ },
260
+ handler: async (args) => {
261
+ const { book_id, artifact_id, object_hash } = args;
262
+ try {
263
+ const source = await resolveImageSubjectSource(engine, artifact_id);
264
+ if (source.objectHash !== object_hash) {
265
+ return failure("The requested image version is stale; refresh the asset before editing");
266
+ }
267
+ const record = await readImageSubjectMapRecord(engine, artifact_id);
268
+ const projected = projectImageSubjectAnalysisRecord(engine, record);
269
+ return json(imageSubjectMapResponse(book_id, source, projected));
270
+ }
271
+ catch (cause) {
272
+ return failure(cause instanceof Error ? cause.message : String(cause));
273
+ }
274
+ },
275
+ },
276
+ {
277
+ name: "analyze_image_subjects",
278
+ description: "Automatically decompose an image into cached subjects using Seedream 5 Pro",
279
+ annotations: {
280
+ readOnlyHint: false,
281
+ destructiveHint: false,
282
+ idempotentHint: true,
283
+ openWorldHint: true,
284
+ },
285
+ schema: {
286
+ book_id: z.string().min(1),
287
+ artifact_id: z.string().min(1),
288
+ object_hash: z.string().min(1),
289
+ force: z.boolean().optional(),
290
+ },
291
+ handler: async (args) => {
292
+ const { book_id, artifact_id, object_hash, force } = args;
293
+ try {
294
+ const source = await resolveImageSubjectSource(engine, artifact_id);
295
+ if (source.objectHash !== object_hash) {
296
+ return failure("The requested image version is stale; refresh the asset before analyzing");
297
+ }
298
+ const existing = await readImageSubjectMapRecord(engine, artifact_id);
299
+ const active = existing?.status === "analyzing"
300
+ && imageSubjectAnalysisJobIsActive(engine, existing.jobId);
301
+ if (reuseImageSubjectAnalysis(existing, object_hash, force === true, active)) {
302
+ return json(imageSubjectMapResponse(book_id, source, existing));
303
+ }
304
+ if (!process.env.KIE_API_KEY) {
305
+ return failure("KIE_API_KEY environment variable is not set");
306
+ }
307
+ const jobId = imageSubjectAnalysisJobId(artifact_id, object_hash, existing?.jobId ?? null, force === true);
308
+ const resumeTaskId = imageSubjectAnalysisResumeTaskId(existing, object_hash, active);
309
+ const analyzing = {
310
+ version: 1,
311
+ status: "analyzing",
312
+ artifactId: artifact_id,
313
+ objectHash: object_hash,
314
+ width: source.width,
315
+ height: source.height,
316
+ jobId,
317
+ startedAt: new Date().toISOString(),
318
+ ...(resumeTaskId
319
+ ? { taskId: resumeTaskId }
320
+ : {}),
321
+ };
322
+ await writeImageSubjectMapRecord(engine, analyzing);
323
+ try {
324
+ await queue.enqueue(jobId, {
325
+ type: "analyze_image_subjects",
326
+ sourceArtifactId: artifact_id,
327
+ sourceObjectHash: object_hash,
328
+ bookId: book_id,
329
+ jobId,
330
+ ...(analyzing.taskId ? { resumeTaskId: analyzing.taskId } : {}),
331
+ });
332
+ }
333
+ catch (cause) {
334
+ const message = cause instanceof Error ? cause.message : String(cause);
335
+ await writeCurrentImageSubjectAnalysisRecord(engine, {
336
+ version: 1,
337
+ status: "failed",
338
+ artifactId: artifact_id,
339
+ objectHash: object_hash,
340
+ width: source.width,
341
+ height: source.height,
342
+ jobId,
343
+ error: message.slice(0, 800) || "Failed to enqueue subject analysis",
344
+ failedAt: new Date().toISOString(),
345
+ }).catch(() => undefined);
346
+ throw cause;
347
+ }
348
+ const afterEnqueue = await readImageSubjectMapRecord(engine, artifact_id);
349
+ const projected = projectImageSubjectAnalysisRecord(engine, afterEnqueue);
350
+ return json(imageSubjectMapResponse(book_id, source, projected));
351
+ }
352
+ catch (cause) {
353
+ return failure(cause instanceof Error ? cause.message : String(cause));
354
+ }
355
+ },
356
+ },
357
+ {
358
+ name: "edit_image_subjects",
359
+ description: "Remove, semantically edit, or replace selected cached image subjects and user-drawn regions into a new connected notebook image",
360
+ annotations: {
361
+ readOnlyHint: false,
362
+ destructiveHint: false,
363
+ idempotentHint: false,
364
+ openWorldHint: true,
365
+ },
366
+ schema: {
367
+ book_id: z.string().min(1),
368
+ notebook_id: z.string().uuid(),
369
+ source_cell_id: z.string().uuid(),
370
+ artifact_id: z.string().min(1),
371
+ object_hash: z.string().min(1),
372
+ subject_ids: z.array(z.string().min(1)).optional(),
373
+ operation: z.enum(["remove", "edit", "replace", "whole_image"]),
374
+ prompt: z.string().max(2400).optional(),
375
+ regions: z.array(replacementRegionSchema).optional(),
376
+ edit_regions: z.array(editBoxSchema).optional().describe("Normalized boxes for prompt edits or removal without replacement images; coordinates start at the top-left corner"),
377
+ model: z.enum(IMAGE_EDIT_MODEL_IDS).optional(),
378
+ },
379
+ handler: async (args) => {
380
+ const { book_id, notebook_id, source_cell_id, artifact_id, object_hash, subject_ids, operation, prompt, regions, edit_regions, model, } = args;
381
+ if ((operation === "edit" || operation === "whole_image") && !prompt?.trim()) {
382
+ return failure("A prompt is required to edit selected subjects");
383
+ }
384
+ const regional = (edit_regions?.length ?? 0) > 0;
385
+ if (regional && operation !== "edit" && operation !== "remove") {
386
+ return failure("edit_regions applies only to edit and remove operations");
387
+ }
388
+ const localRemoval = operation === "remove" && !regional;
389
+ if (localRemoval && model !== undefined) {
390
+ return failure("Model selection applies only to edit and replace operations");
391
+ }
392
+ const editModel = findImageEditModel(model ?? DEFAULT_IMAGE_EDIT_MODEL_ID);
393
+ if (!localRemoval && !process.env[editModel.envKey]) {
394
+ return failure(`${editModel.envKey} environment variable is not set`);
395
+ }
396
+ if (!localRemoval && !editModel.capabilities[editCapability(operation)]) {
397
+ return failure(`${editModel.label} cannot perform ${editOperationLabel(operation)}`);
398
+ }
399
+ try {
400
+ const source = await resolveImageSubjectSource(engine, artifact_id);
401
+ if (source.objectHash !== object_hash) {
402
+ return failure("The requested image version is stale; refresh the asset before editing");
403
+ }
404
+ const map = await readImageSubjectMapRecord(engine, artifact_id);
405
+ const readyMap = map?.status === "ready" && map.objectHash === object_hash
406
+ ? map
407
+ : null;
408
+ const replacementRegions = operation === "replace"
409
+ ? parsedReplacementRegions(regions)
410
+ : [];
411
+ if (operation === "replace") {
412
+ if (!regions || regions.length === 0) {
413
+ return failure("Replacement edits require at least one region");
414
+ }
415
+ if (replacementRegions.length !== regions.length) {
416
+ return failure("Each replacement region needs a box or subject_id");
417
+ }
418
+ const uniqueReplacements = uniqueSubjectIds(replacementRegions.map((region) => region.replacementArtifactId));
419
+ if (editModel.maxInputImages !== null
420
+ && 1 + uniqueReplacements.length > editModel.maxInputImages) {
421
+ return failure(editModelCapacityError(editModel, uniqueReplacements.length));
422
+ }
423
+ }
424
+ else if (operation !== "whole_image" && !readyMap
425
+ && (!regional || (subject_ids?.length ?? 0) > 0)) {
426
+ return failure("Analyze image subjects before editing them");
427
+ }
428
+ const selectedIds = uniqueSubjectIds([
429
+ ...(subject_ids ?? []),
430
+ ...replacementRegions.flatMap((region) => region.subjectId ? [region.subjectId] : []),
431
+ ]);
432
+ if (operation !== "replace" && operation !== "whole_image" && !regional && selectedIds.length === 0) {
433
+ return failure("Select at least one subject to edit");
434
+ }
435
+ const subjects = new Map((readyMap?.subjects ?? []).map((subject) => [subject.id, subject]));
436
+ const unknown = selectedIds.filter((id) => !subjects.has(id));
437
+ if (unknown.length > 0) {
438
+ return failure(`Unknown subject ids: ${unknown.join(", ")}`);
439
+ }
440
+ const editRegions = regional
441
+ ? [...edit_regions, ...selectedIds.map((id) => subjects.get(id).normalizedBox)]
442
+ : [];
443
+ const resolvedRegions = replacementRegions.map((region) => {
444
+ const subject = region.subjectId ? subjects.get(region.subjectId) : undefined;
445
+ return {
446
+ ...region,
447
+ box: region.box ?? subject?.normalizedBox,
448
+ };
449
+ });
450
+ if (operation === "replace" && resolvedRegions.some((region) => !region.box)) {
451
+ return failure("Analyze image subjects before replacing by subject id");
452
+ }
453
+ const notebook = engine.notebooks.read(notebook_id);
454
+ if (!notebook.ok)
455
+ return failure(notebook.error.message);
456
+ const sourceCell = notebook.value.cells.find((cell) => cell.id === source_cell_id);
457
+ if (!sourceCell || !sourceCellAcceptsArtifact(sourceCell, artifact_id)) {
458
+ return failure("The pinned source cell does not contain the requested image version");
459
+ }
460
+ const replacementRefs = [];
461
+ const seenReplacements = new Set();
462
+ if (operation === "replace") {
463
+ for (const region of resolvedRegions) {
464
+ if (seenReplacements.has(region.replacementArtifactId))
465
+ continue;
466
+ const resolved = await resolveImageRef(engine, region.replacementArtifactId);
467
+ if (!resolved.ok)
468
+ return failure(resolved.error);
469
+ seenReplacements.add(region.replacementArtifactId);
470
+ replacementRefs.push(resolved.value);
471
+ }
472
+ }
473
+ const labels = selectedIds.map((id) => subjects.get(id).label);
474
+ if (regional)
475
+ labels.push(`${edit_regions.length} ${edit_regions.length === 1 ? "region" : "regions"}`);
476
+ const generatedPrompt = prompt?.trim()
477
+ || (operation === "replace"
478
+ ? `Replace ${resolvedRegions.length} selected ${resolvedRegions.length === 1 ? "region" : "regions"}`
479
+ : `Remove ${labels.join(", ")}`);
480
+ const actionLabel = operation === "remove"
481
+ ? `Remove ${labels.join(", ")}`
482
+ : operation === "replace"
483
+ ? `Replace ${resolvedRegions.length} ${resolvedRegions.length === 1 ? "region" : "regions"}`
484
+ : operation === "whole_image"
485
+ ? "Edit whole image"
486
+ : `Edit ${labels.join(", ")}`;
487
+ const outputLabel = operation === "remove"
488
+ ? `Image without ${labels.join(", ")}`
489
+ : operation === "replace"
490
+ ? `Replaced ${resolvedRegions.length} ${resolvedRegions.length === 1 ? "region" : "regions"}`
491
+ : operation === "whole_image"
492
+ ? "Edited image"
493
+ : `Edited ${labels.join(", ")}`;
494
+ const provider = localRemoval ? "local" : editModel.provider;
495
+ const cellModel = localRemoval
496
+ ? "sharp/layer-composite"
497
+ : editModel.id;
498
+ const sourceArtifact = engine.artifacts.get(artifact_id);
499
+ const output = await engine.artifacts.create({
500
+ kind: "image",
501
+ label: sourceArtifact.ok && sourceArtifact.value.label
502
+ ? operation === "whole_image"
503
+ ? `${sourceArtifact.value.label} edited`
504
+ : `${sourceArtifact.value.label} ${operation === "remove" ? "without" : "edited"} ${labels.join(", ")}`
505
+ : outputLabel,
506
+ });
507
+ if (!output.ok)
508
+ return failure(output.error.message);
509
+ const imageAssetId = output.value.artifactId;
510
+ const jobId = `image-subject-${operation}-${imageAssetId}`;
511
+ const placed = {
512
+ producer: null,
513
+ output: null,
514
+ };
515
+ let generationId = null;
516
+ try {
517
+ const written = await mutateNotebook(engine, notebook_id, "enqueue_image_subject_edit", (current) => {
518
+ const currentSource = current.cells.find((cell) => cell.id === source_cell_id);
519
+ if (!currentSource || !sourceCellAcceptsArtifact(currentSource, artifact_id)) {
520
+ throw new Error("The pinned source cell changed before placement");
521
+ }
522
+ const occupied = notebookOccupiedSlots(current.cells);
523
+ const producer = engine.notebooks.createCell({
524
+ type: "generate_image",
525
+ label: actionLabel,
526
+ slot: nextHorizontalSlotFrom(slotRight(currentSource.slot), occupied),
527
+ prompt: generatedPrompt,
528
+ provider,
529
+ model: cellModel,
530
+ operation: "image_to_image",
531
+ tool: "edit_image_subjects",
532
+ inputs: {
533
+ generationMode: regional ? `region-${operation}` : operation === "remove"
534
+ ? "subject-remove"
535
+ : operation === "replace"
536
+ ? "region-replace"
537
+ : operation === "whole_image"
538
+ ? "whole-image"
539
+ : "subject-edit",
540
+ sourceArtifactId: artifact_id,
541
+ sourceObjectHash: object_hash,
542
+ subjectIds: selectedIds,
543
+ subjectMapVersion: IMAGE_SUBJECT_MAP_VERSION,
544
+ ...(regional ? { editRegions } : {}),
545
+ ...(operation === "replace"
546
+ ? {
547
+ regions: resolvedRegions,
548
+ replacementArtifactIds: replacementRefs.map((ref) => ref.assetId),
549
+ }
550
+ : {}),
551
+ state: "queued",
552
+ },
553
+ });
554
+ const outputCell = engine.notebooks.createCell({
555
+ type: "image",
556
+ label: outputLabel,
557
+ slot: nextHorizontalSlotFrom(slotRight(producer.slot), [...occupied, producer.slot]),
558
+ outputArtifactId: imageAssetId,
559
+ inputs: {
560
+ mediaRole: "generate-image-output",
561
+ producerCellId: producer.id,
562
+ state: "queued",
563
+ },
564
+ });
565
+ placed.producer = producer;
566
+ placed.output = outputCell;
567
+ return {
568
+ ...current,
569
+ cells: [...current.cells, producer, outputCell],
570
+ edges: [
571
+ ...current.edges,
572
+ engine.notebooks.createEdge({
573
+ source: currentSource.id,
574
+ target: producer.id,
575
+ targetInput: "image",
576
+ }),
577
+ engine.notebooks.createEdge({
578
+ source: producer.id,
579
+ target: outputCell.id,
580
+ targetInput: "media",
581
+ }),
582
+ ],
583
+ execution: {
584
+ ...(current.execution ?? {}),
585
+ [producer.id]: {
586
+ status: "queued",
587
+ outputArtifactId: imageAssetId,
588
+ providerArtifactId: imageAssetId,
589
+ runId: jobId,
590
+ updatedAt: new Date().toISOString(),
591
+ tool: "edit_image_subjects",
592
+ },
593
+ [outputCell.id]: {
594
+ status: "queued",
595
+ outputArtifactId: imageAssetId,
596
+ runId: jobId,
597
+ updatedAt: new Date().toISOString(),
598
+ tool: "edit_image_subjects",
599
+ },
600
+ },
601
+ };
602
+ }, {
603
+ sourceCellId: source_cell_id,
604
+ sourceArtifactId: artifact_id,
605
+ outputArtifactId: imageAssetId,
606
+ subjectIds: selectedIds,
607
+ }, { artifactId: imageAssetId });
608
+ if (!written.ok)
609
+ throw new Error(written.error.message);
610
+ if (!placed.producer || !placed.output) {
611
+ throw new Error("Subject removal cells were not placed");
612
+ }
613
+ const producer = placed.producer;
614
+ const outputCell = placed.output;
615
+ const generation = await engine.generations.record({
616
+ notebookId: notebook_id,
617
+ cellId: producer.id,
618
+ outputCellId: outputCell.id,
619
+ runId: jobId,
620
+ status: "dispatched",
621
+ tool: "edit_image_subjects",
622
+ provider,
623
+ model: cellModel,
624
+ prompt: generatedPrompt,
625
+ providerArtifactId: imageAssetId,
626
+ });
627
+ if (!generation.ok)
628
+ throw new Error(generation.error.message);
629
+ generationId = generation.value.generationId;
630
+ if (operation === "replace") {
631
+ const provenance = await engine.metadata.artifacts.write(imageAssetId, "original", {
632
+ origin: "image_subject_replace",
633
+ source_asset: artifact_id,
634
+ source_object_hash: object_hash,
635
+ replacement_artifact_ids: replacementRefs.map((ref) => ref.assetId),
636
+ model: editModel.id,
637
+ width: source.width,
638
+ height: source.height,
639
+ status: "queued",
640
+ });
641
+ if (!provenance.ok)
642
+ throw new Error(provenance.error.message);
643
+ }
644
+ await queue.enqueue(jobId, {
645
+ type: "generate_image_subject_edit",
646
+ imageAssetId,
647
+ sourceArtifactId: artifact_id,
648
+ sourceObjectHash: object_hash,
649
+ subjectIds: selectedIds,
650
+ operation,
651
+ prompt: generatedPrompt,
652
+ ...(regional ? { editRegions } : {}),
653
+ ...(operation === "replace"
654
+ ? {
655
+ regions: resolvedRegions,
656
+ replacementRefs,
657
+ orderedImageRefs: [
658
+ { role: "base", artifactId: artifact_id },
659
+ ...replacementRefs.map((ref) => ({
660
+ role: "replacement",
661
+ artifactId: ref.assetId,
662
+ })),
663
+ ],
664
+ }
665
+ : {}),
666
+ bookId: book_id,
667
+ notebookId: notebook_id,
668
+ producerCellId: producer.id,
669
+ outputCellId: outputCell.id,
670
+ generationId,
671
+ jobId,
672
+ ...(localRemoval ? {} : { modelId: editModel.id }),
673
+ });
674
+ if (!localRemoval) {
675
+ writeLastImageEditModel(engine, editModel.id);
676
+ }
677
+ return json({
678
+ status: "enqueued",
679
+ operation,
680
+ job_id: jobId,
681
+ source_artifact_id: artifact_id,
682
+ output_artifact_id: imageAssetId,
683
+ producer_cell_id: producer.id,
684
+ output_cell_id: outputCell.id,
685
+ output_address: notebookGridTag(outputCell.slot),
686
+ });
687
+ }
688
+ catch (cause) {
689
+ const message = cause instanceof Error ? cause.message : String(cause);
690
+ if (generationId) {
691
+ await engine.generations.update(generationId, {
692
+ status: "failed",
693
+ error: message,
694
+ }).catch(() => undefined);
695
+ }
696
+ const producer = placed.producer;
697
+ const outputCell = placed.output;
698
+ if (producer && outputCell) {
699
+ await rollbackEditPlacement(engine, notebook_id, producer.id, outputCell.id).catch(() => undefined);
700
+ }
701
+ await engine.artifacts.delete(imageAssetId).catch(() => undefined);
702
+ throw cause;
703
+ }
704
+ }
705
+ catch (cause) {
706
+ return failure(cause instanceof Error ? cause.message : String(cause));
707
+ }
708
+ },
709
+ },
710
+ ];
711
+ }