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,1059 @@
1
+ import { submitMediaJob, } from "@apicity/kie";
2
+ import { GrokImagineImage2ImageEditRequestSchema, GrokImagineImage2SegmentMapRequestSchema, SeedreamProLayerDecompositionRequestSchema, } from "@apicity/kie/zod";
3
+ import sharp from "sharp";
4
+ import { KIE_RESULT_TIMEOUT_MS, kieProvider, parseResultUrls, waitForKieResult, } from "./_kie-poll.js";
5
+ import { GROK_EXACT_MASK_ADAPTER_ID, imageEditAdapter, IMAGE_EDIT_ADAPTERS, } from "./_image-edit-adapter.js";
6
+ import { DEFAULT_IMAGE_EDIT_MODEL_ID } from "../image-edit-models.js";
7
+ import { compositeImageRegionEdit, imageRegionEditPrompt } from "../image-region-edit.js";
8
+ import { IMAGE_SUBJECT_MAP_VERSION, applyGrokSegmentsToSubjects, composeImageSubjectRemoval, exactEditMaskIndexes, grokMaskIndex, parseGrokSegmentMapResult, persistSeedreamSubjectLayers, readImageSubjectMapRecord, resolveImageSubjectSource, writeCurrentImageSubjectAnalysisRecord, } from "../image-subject-map.js";
9
+ import { enqueueSearchIndex } from "./enqueue-search-index.js";
10
+ import { cropToFill } from "../image.js";
11
+ import { mutateNotebook } from "../notebook-mutations.js";
12
+ import { registerJobHandler } from "../queue.js";
13
+ import { mintKieOtp } from "./kie-paygate.js";
14
+ import { uploadAssetToKie } from "./_kie-upload.js";
15
+ export const SEEDREAM_PRO_I2I_MAX_IMAGE_URLS = 10;
16
+ function record(value) {
17
+ return value !== null && typeof value === "object" && !Array.isArray(value)
18
+ ? value
19
+ : null;
20
+ }
21
+ function stringValue(value) {
22
+ return typeof value === "string" && value.trim() ? value.trim() : null;
23
+ }
24
+ function numberValue(value) {
25
+ const number = Number(value);
26
+ return Number.isFinite(number) ? number : null;
27
+ }
28
+ function normalizedBox(value) {
29
+ const values = Array.isArray(value) ? value.map(numberValue) : null;
30
+ if (values?.length === 4 && values.every((entry) => entry !== null)) {
31
+ const [x1, y1, x2, y2] = values;
32
+ const scale = values.some((entry) => entry > 1) ? 1000 : 1;
33
+ if (values.some((entry) => entry < 0 || entry > scale))
34
+ return undefined;
35
+ if (scale === 1000 && values.some((entry) => !Number.isInteger(entry))) {
36
+ return undefined;
37
+ }
38
+ const x = x1 / scale;
39
+ const y = y1 / scale;
40
+ const right = x2 / scale;
41
+ const bottom = y2 / scale;
42
+ if (right > x && bottom > y) {
43
+ return { x, y, width: right - x, height: bottom - y };
44
+ }
45
+ }
46
+ const object = record(value);
47
+ if (!object)
48
+ return undefined;
49
+ const xValue = numberValue(object.x ?? object.left);
50
+ const yValue = numberValue(object.y ?? object.top);
51
+ const widthValue = numberValue(object.width ?? object.w);
52
+ const heightValue = numberValue(object.height ?? object.h);
53
+ if (xValue === null || yValue === null || widthValue === null || heightValue === null) {
54
+ return undefined;
55
+ }
56
+ const raw = [xValue, yValue, widthValue, heightValue];
57
+ const scale = raw.some((entry) => entry > 1) ? 1000 : 1;
58
+ if (raw.some((entry) => entry < 0 || entry > scale))
59
+ return undefined;
60
+ if (scale === 1000 && raw.some((entry) => !Number.isInteger(entry))) {
61
+ return undefined;
62
+ }
63
+ const x = xValue / scale;
64
+ const y = yValue / scale;
65
+ const width = widthValue / scale;
66
+ const height = heightValue / scale;
67
+ if (width <= 0 || height <= 0 || x + width > 1.001 || y + height > 1.001) {
68
+ return undefined;
69
+ }
70
+ return { x, y, width, height };
71
+ }
72
+ function layerBox(value) {
73
+ const boundingBox = record(value.bounding_box ?? value.boundingBox);
74
+ return normalizedBox(boundingBox?.normalized
75
+ ?? boundingBox?.normalized_box
76
+ ?? value.normalized_box
77
+ ?? value.normalizedBox);
78
+ }
79
+ function layerArray(value) {
80
+ for (const candidate of [
81
+ value.layers_data,
82
+ value.layersData,
83
+ record(value.resultObject)?.layers_data,
84
+ record(value.resultObject)?.layersData,
85
+ record(value.result)?.layers_data,
86
+ record(value.result)?.layersData,
87
+ record(value.data)?.layers_data,
88
+ record(value.data)?.layersData,
89
+ ]) {
90
+ if (Array.isArray(candidate))
91
+ return candidate;
92
+ }
93
+ return null;
94
+ }
95
+ export function parseSeedreamLayerResult(resultJson) {
96
+ let parsed;
97
+ try {
98
+ parsed = JSON.parse(resultJson);
99
+ }
100
+ catch {
101
+ throw new Error("Seedream returned invalid result JSON");
102
+ }
103
+ const root = record(parsed);
104
+ if (!root)
105
+ throw new Error("Seedream returned an invalid layer result");
106
+ const entries = layerArray(root)?.flatMap((value) => {
107
+ const item = record(value);
108
+ const url = item ? stringValue(item.url ?? item.layer_url ?? item.layerUrl) : null;
109
+ if (!item || !url)
110
+ return [];
111
+ return [{ item, url }];
112
+ }) ?? [];
113
+ if (entries.length === 0) {
114
+ throw new Error("Seedream layer decomposition returned no downloadable layers");
115
+ }
116
+ const ordered = entries.sort((left, right) => (numberValue(left.item.z_index ?? left.item.zIndex) ?? 0)
117
+ - (numberValue(right.item.z_index ?? right.item.zIndex) ?? 0));
118
+ const baseIndex = ordered.findIndex(({ item }) => !stringValue(item.name ?? item.label) && !layerBox(item));
119
+ if (baseIndex < 0) {
120
+ throw new Error("Seedream layer decomposition returned no identifiable base layer");
121
+ }
122
+ const base = ordered[baseIndex];
123
+ const layers = ordered.flatMap(({ item, url }, index) => {
124
+ if (index === baseIndex)
125
+ return [];
126
+ const zIndex = Math.round(numberValue(item.z_index ?? item.zIndex) ?? index);
127
+ const box = layerBox(item);
128
+ return [{
129
+ url,
130
+ zIndex,
131
+ ...(stringValue(item.name ?? item.label)
132
+ ? { name: stringValue(item.name ?? item.label) }
133
+ : {}),
134
+ ...(stringValue(item.description)
135
+ ? { description: stringValue(item.description) }
136
+ : {}),
137
+ ...(box ? { normalizedBox: box } : {}),
138
+ }];
139
+ });
140
+ return { baseUrl: base.url, layers };
141
+ }
142
+ // The paygate prices layer decomposition per explicit size tier (1K/1.5K/2K);
143
+ // the upstream default "auto" selects no rate and mintKieOtp rejects it as
144
+ // unbounded, so we always pick the tier nearest the source's longest side.
145
+ export function seedreamLayerDecompositionSize(width, height) {
146
+ const longestSide = Math.max(width, height);
147
+ if (longestSide <= 1280)
148
+ return "1K";
149
+ if (longestSide <= 1792)
150
+ return "1.5K";
151
+ return "2K";
152
+ }
153
+ // Seedream rejects busy scenes with a 400 "the image content is too complex
154
+ // to decompose into layers" when elements are detected automatically (no
155
+ // prompt). An element-selection prompt sidesteps that rejection, so we retry
156
+ // once with this constrained selection before failing the analysis.
157
+ export const SEEDREAM_TOO_COMPLEX_PATTERN = /too complex to decompose/i;
158
+ export const SEEDREAM_LAYER_FALLBACK_PROMPT = "Extract only the most prominent subjects as separate layers: each distinct "
159
+ + "person in the foreground or midground, plus at most three large, clearly "
160
+ + "separable foreground objects. Merge all remaining detail into the base layer.";
161
+ const SEEDREAM_TOO_COMPLEX_GUIDANCE = "Seedream could not decompose this image into layers, even with a "
162
+ + "constrained subject selection. Use Draw region to target the area you "
163
+ + "want to edit instead.";
164
+ export function seedreamLayerDecompositionRequest(imageUrl, width, height, prompt) {
165
+ return SeedreamProLayerDecompositionRequestSchema.parse({
166
+ model: "seedream/5-pro-layer-decomposition",
167
+ input: {
168
+ image_url: imageUrl,
169
+ ...(prompt ? { prompt } : {}),
170
+ size: seedreamLayerDecompositionSize(width, height),
171
+ output_format: "jpeg",
172
+ },
173
+ });
174
+ }
175
+ function boxPositionWording(box) {
176
+ const cx = box.x + box.width / 2;
177
+ const cy = box.y + box.height / 2;
178
+ const horizontal = cx < 1 / 3 ? "left" : cx > 2 / 3 ? "right" : "center";
179
+ const vertical = cy < 1 / 3 ? "upper" : cy > 2 / 3 ? "lower" : "middle";
180
+ if (horizontal === "center" && vertical === "middle")
181
+ return "center";
182
+ if (horizontal === "center")
183
+ return vertical;
184
+ if (vertical === "middle")
185
+ return horizontal;
186
+ return `${vertical}-${horizontal}`;
187
+ }
188
+ function describeReplacementRegion(region, map) {
189
+ const subject = region.subjectId && map
190
+ ? map.subjects.find((entry) => entry.id === region.subjectId)
191
+ : undefined;
192
+ const box = region.box ?? subject?.normalizedBox;
193
+ const label = subject?.label
194
+ ?? (box ? `the ${boxPositionWording(box)} region` : "the selected region");
195
+ const boxText = box
196
+ ? `normalized box ${box.x.toFixed(4)},${box.y.toFixed(4)},${box.width.toFixed(4)},${box.height.toFixed(4)}`
197
+ : "unknown box";
198
+ const note = region.note?.trim() ? ` (${region.note.trim()})` : "";
199
+ return `${label} at ${boxText}${note}`;
200
+ }
201
+ export function regionReplacementPrompt(map, regions, orderedImageRefs, prompt) {
202
+ const replacementIndexByArtifact = new Map();
203
+ let nextIndex = 2;
204
+ for (const ref of orderedImageRefs) {
205
+ if (ref.role !== "replacement")
206
+ continue;
207
+ if (!replacementIndexByArtifact.has(ref.artifactId)) {
208
+ replacementIndexByArtifact.set(ref.artifactId, nextIndex);
209
+ nextIndex += 1;
210
+ }
211
+ }
212
+ const lines = regions.map((region, index) => {
213
+ const imageIndex = replacementIndexByArtifact.get(region.replacementArtifactId)
214
+ ?? index + 2;
215
+ return `${index + 1}. ${describeReplacementRegion(region, map)}: replace with reference image ${imageIndex}.`;
216
+ });
217
+ const user = prompt.trim();
218
+ const composed = [
219
+ "The first image is the base scene. Following images are identity references for the listed regions.",
220
+ "Replace only those regions and preserve every unreplaced person, object, background element, composition, and camera framing.",
221
+ ...lines,
222
+ ].join(" ");
223
+ return user ? `${user}\n${composed}` : composed;
224
+ }
225
+ export function seedreamProImageUrlCapacityError(replacementCount) {
226
+ const maxReplacements = SEEDREAM_PRO_I2I_MAX_IMAGE_URLS - 1;
227
+ return `Seedream 5 Pro image-to-image accepts at most ${SEEDREAM_PRO_I2I_MAX_IMAGE_URLS} image_urls (1 base + ${maxReplacements} replacements). This request needs 1 base + ${replacementCount} replacements.`;
228
+ }
229
+ function semanticEditPrompt(map, subjectIds, prompt) {
230
+ const selected = new Set(subjectIds);
231
+ const targets = map.subjects
232
+ .filter((subject) => selected.has(subject.id))
233
+ .map((subject) => {
234
+ const box = subject.normalizedBox;
235
+ return `${subject.label} at normalized box ${box.x.toFixed(4)},${box.y.toFixed(4)},${box.width.toFixed(4)},${box.height.toFixed(4)}`;
236
+ });
237
+ return `${prompt.trim()}\nModify only these selected subjects: ${targets.join("; ")}. Preserve every unselected person, object, background element, composition, and camera framing.`;
238
+ }
239
+ export function imageSubjectAnalysisDeadline(startedAt, fallbackNow = Date.now()) {
240
+ const parsed = Date.parse(startedAt);
241
+ return (Number.isFinite(parsed) ? parsed : fallbackNow) + KIE_RESULT_TIMEOUT_MS;
242
+ }
243
+ async function writeAnalysisFailure(engine, payload, dimensions, error, taskId) {
244
+ return await writeCurrentImageSubjectAnalysisRecord(engine, {
245
+ version: IMAGE_SUBJECT_MAP_VERSION,
246
+ status: "failed",
247
+ artifactId: payload.sourceArtifactId,
248
+ objectHash: payload.sourceObjectHash,
249
+ width: dimensions.width,
250
+ height: dimensions.height,
251
+ jobId: payload.jobId,
252
+ error,
253
+ failedAt: new Date().toISOString(),
254
+ ...(taskId ? { taskId } : {}),
255
+ });
256
+ }
257
+ function withoutInputError(inputs) {
258
+ const next = { ...(inputs ?? {}) };
259
+ delete next.error;
260
+ return next;
261
+ }
262
+ async function updateNotebookEditState(engine, payload, state, error) {
263
+ if (payload.managedByNotebook)
264
+ return;
265
+ const updatedAt = new Date().toISOString();
266
+ const result = await mutateNotebook(engine, payload.notebookId, state === "completed" ? "complete_image_subject_edit" : "fail_image_subject_edit", (notebook) => ({
267
+ ...notebook,
268
+ cells: notebook.cells.map((cell) => {
269
+ if (cell.id === payload.producerCellId) {
270
+ return {
271
+ ...cell,
272
+ inputs: {
273
+ ...withoutInputError(cell.inputs),
274
+ state,
275
+ ...(error ? { error } : {}),
276
+ },
277
+ };
278
+ }
279
+ if (cell.id === payload.outputCellId) {
280
+ return {
281
+ ...cell,
282
+ ...(state === "completed"
283
+ ? { outputArtifactId: payload.imageAssetId }
284
+ : {}),
285
+ inputs: {
286
+ ...withoutInputError(cell.inputs),
287
+ state,
288
+ ...(state === "completed"
289
+ ? { outputVersionArtifactIds: [payload.imageAssetId] }
290
+ : {}),
291
+ ...(error ? { error } : {}),
292
+ },
293
+ };
294
+ }
295
+ return cell;
296
+ }),
297
+ execution: {
298
+ ...(notebook.execution ?? {}),
299
+ [payload.producerCellId]: {
300
+ status: state,
301
+ outputArtifactId: payload.imageAssetId,
302
+ providerArtifactId: payload.imageAssetId,
303
+ runId: payload.jobId,
304
+ completedAt: updatedAt,
305
+ updatedAt,
306
+ tool: "edit_image_subjects",
307
+ ...(error ? { error } : {}),
308
+ },
309
+ [payload.outputCellId]: {
310
+ status: state,
311
+ outputArtifactId: payload.imageAssetId,
312
+ runId: payload.jobId,
313
+ completedAt: updatedAt,
314
+ updatedAt,
315
+ tool: "edit_image_subjects",
316
+ ...(error ? { error } : {}),
317
+ },
318
+ },
319
+ }), {
320
+ sourceArtifactId: payload.sourceArtifactId,
321
+ outputArtifactId: payload.imageAssetId,
322
+ subjectIds: payload.subjectIds,
323
+ state,
324
+ ...(error ? { error } : {}),
325
+ }, { artifactId: payload.imageAssetId });
326
+ if (!result.ok)
327
+ throw new Error(result.error.message);
328
+ }
329
+ async function updateNotebookEditProgress(engine, payload, status) {
330
+ if (payload.managedByNotebook)
331
+ return;
332
+ const updatedAt = new Date().toISOString();
333
+ const inputState = status === "awaiting_provider" ? "awaiting-output" : "working";
334
+ const result = await mutateNotebook(engine, payload.notebookId, "progress_image_subject_edit", (notebook) => {
335
+ const producerExecution = {
336
+ ...(notebook.execution?.[payload.producerCellId] ?? {}),
337
+ };
338
+ const outputExecution = {
339
+ ...(notebook.execution?.[payload.outputCellId] ?? {}),
340
+ };
341
+ delete producerExecution.error;
342
+ delete producerExecution.completedAt;
343
+ delete outputExecution.error;
344
+ delete outputExecution.completedAt;
345
+ return {
346
+ ...notebook,
347
+ cells: notebook.cells.map((cell) => cell.id === payload.producerCellId || cell.id === payload.outputCellId
348
+ ? {
349
+ ...cell,
350
+ inputs: {
351
+ ...withoutInputError(cell.inputs),
352
+ state: inputState,
353
+ },
354
+ }
355
+ : cell),
356
+ execution: {
357
+ ...(notebook.execution ?? {}),
358
+ [payload.producerCellId]: {
359
+ ...producerExecution,
360
+ status,
361
+ outputArtifactId: payload.imageAssetId,
362
+ providerArtifactId: payload.imageAssetId,
363
+ runId: payload.jobId,
364
+ updatedAt,
365
+ tool: "edit_image_subjects",
366
+ },
367
+ [payload.outputCellId]: {
368
+ ...outputExecution,
369
+ status,
370
+ outputArtifactId: payload.imageAssetId,
371
+ runId: payload.jobId,
372
+ updatedAt,
373
+ tool: "edit_image_subjects",
374
+ },
375
+ },
376
+ };
377
+ }, {
378
+ outputArtifactId: payload.imageAssetId,
379
+ status,
380
+ }, { artifactId: payload.imageAssetId });
381
+ if (!result.ok)
382
+ throw new Error(result.error.message);
383
+ }
384
+ async function persistProcessedImage(engine, payload, map, output, provenance) {
385
+ const outputMetadata = await sharp(output).metadata();
386
+ const width = outputMetadata.width;
387
+ const height = outputMetadata.height;
388
+ if (!width || !height)
389
+ throw new Error("Could not determine edited image dimensions");
390
+ const [landscape, portrait, square] = await Promise.all([
391
+ cropToFill(output, 1920, 1080),
392
+ cropToFill(output, 1080, 1920),
393
+ cropToFill(output, 1080, 1080),
394
+ ]);
395
+ const writes = await Promise.all([
396
+ engine.files.write(payload.imageAssetId, "original.png", output),
397
+ engine.files.write(payload.imageAssetId, "landscape.jpg", landscape),
398
+ engine.files.write(payload.imageAssetId, "portrait.jpg", portrait),
399
+ engine.files.write(payload.imageAssetId, "square.jpg", square),
400
+ ]);
401
+ const failed = writes.find((result) => !result.ok);
402
+ if (failed && !failed.ok)
403
+ throw new Error(failed.error.message);
404
+ const metadata = await engine.metadata.artifacts.write(payload.imageAssetId, "original", {
405
+ origin: "image_subject_remove",
406
+ source_asset: payload.sourceArtifactId,
407
+ source_object_hash: payload.sourceObjectHash,
408
+ subject_map_version: IMAGE_SUBJECT_MAP_VERSION,
409
+ ...(map ? { subject_analysis_id: map.analysisId } : {}),
410
+ selected_subject_ids: payload.subjectIds,
411
+ ...(payload.prompt ? { prompt: payload.prompt } : {}),
412
+ ...provenance,
413
+ width,
414
+ height,
415
+ generated_at: new Date().toISOString(),
416
+ });
417
+ if (!metadata.ok)
418
+ throw new Error(metadata.error.message);
419
+ }
420
+ export async function readStoredImageSubjectEditTask(engine, imageAssetId) {
421
+ const storedRequest = await engine.metadata.artifacts.read(imageAssetId, "generation-payload");
422
+ let request = null;
423
+ let adapterId = null;
424
+ if (storedRequest.ok) {
425
+ for (const adapter of IMAGE_EDIT_ADAPTERS) {
426
+ const parsed = adapter.parseStoredPayload(storedRequest.value);
427
+ if (parsed) {
428
+ request = parsed;
429
+ adapterId = adapter.id;
430
+ break;
431
+ }
432
+ }
433
+ if (!request) {
434
+ const parsedExact = GrokImagineImage2ImageEditRequestSchema.safeParse(storedRequest.value);
435
+ if (parsedExact.success) {
436
+ request = parsedExact.data;
437
+ adapterId = GROK_EXACT_MASK_ADAPTER_ID;
438
+ }
439
+ }
440
+ }
441
+ const storedTask = await engine.metadata.artifacts.read(imageAssetId, "kie-task-id");
442
+ const taskRecord = storedTask.ok ? record(storedTask.value) : null;
443
+ const taskId = stringValue(taskRecord?.taskId);
444
+ if (taskId && !request) {
445
+ throw new Error("Stored Kie subject edit task has no valid generation payload");
446
+ }
447
+ return taskId && request && adapterId
448
+ ? { adapterId, request, taskId }
449
+ : null;
450
+ }
451
+ const GROK_IMAGE_2_SOURCE_MODELS = new Set([
452
+ "grok-imagine-image-2-0/text-to-image",
453
+ "grok-imagine-image-2-0/image-edit",
454
+ ]);
455
+ export async function readGrokImagineImage2SourceTaskId(engine, artifactId) {
456
+ const payload = await engine.metadata.artifacts.read(artifactId, "generation-payload");
457
+ const model = payload.ok ? record(payload.value)?.model : undefined;
458
+ if (typeof model !== "string" || !GROK_IMAGE_2_SOURCE_MODELS.has(model)) {
459
+ return null;
460
+ }
461
+ const stored = await engine.metadata.artifacts.read(artifactId, "kie-task-id");
462
+ const taskId = stored.ok ? stringValue(record(stored.value)?.taskId) : null;
463
+ return taskId;
464
+ }
465
+ export function imageSubjectEditRoute(map, subjectIds) {
466
+ if (!map.grokSourceTaskId)
467
+ return "semantic";
468
+ return exactEditMaskIndexes(map.subjects, subjectIds) ? "exact" : "semantic";
469
+ }
470
+ export function grokImagineImage2EditRequest(sourceTaskId, prompt, maskIndexes) {
471
+ const mask_indexs = maskIndexes.filter((index) => grokMaskIndex(index) !== undefined);
472
+ if (mask_indexs.length === 0) {
473
+ throw new Error("Exact Grok mask edit requires nonnegative mask indexes");
474
+ }
475
+ return GrokImagineImage2ImageEditRequestSchema.parse({
476
+ model: "grok-imagine-image-2-0/image-edit",
477
+ input: {
478
+ prompt,
479
+ task_id: sourceTaskId,
480
+ mask_indexs,
481
+ },
482
+ });
483
+ }
484
+ function imageSubjectEditAdapter(modelId) {
485
+ const adapter = imageEditAdapter(modelId ?? DEFAULT_IMAGE_EDIT_MODEL_ID);
486
+ if (!adapter)
487
+ throw new Error(`Unknown image edit model: ${modelId}`);
488
+ return adapter;
489
+ }
490
+ export async function previewImageSubjectEditRequest(engine, payload, imageUrls) {
491
+ const source = await resolveImageSubjectSource(engine, payload.sourceArtifactId);
492
+ if (source.objectHash !== payload.sourceObjectHash) {
493
+ throw new Error("The requested image version is stale; refresh the asset before editing");
494
+ }
495
+ const stored = await readImageSubjectMapRecord(engine, payload.sourceArtifactId);
496
+ const map = stored?.status === "ready" && stored.objectHash === source.objectHash ? stored : null;
497
+ const regional = (payload.editRegions?.length ?? 0) > 0;
498
+ if (payload.operation !== "whole_image" && payload.operation !== "replace" && !regional && !map) {
499
+ throw new Error("Analyze image subjects before editing them");
500
+ }
501
+ if (payload.subjectIds.some((id) => !map?.subjects.some((subject) => subject.id === id))) {
502
+ throw new Error("The saved subjects are unavailable; analyze the source image again");
503
+ }
504
+ if (!regional && payload.operation !== "whole_image" && payload.operation !== "replace"
505
+ && payload.subjectIds.length === 0) {
506
+ throw new Error("Select at least one subject to edit");
507
+ }
508
+ if ((payload.operation === "edit" || payload.operation === "whole_image") && !payload.prompt?.trim()) {
509
+ throw new Error("A prompt is required to edit selected subjects");
510
+ }
511
+ if (payload.operation === "remove" && !regional) {
512
+ return { model: "sharp/layer-composite", input: {
513
+ source_artifact_id: payload.sourceArtifactId, subject_ids: payload.subjectIds,
514
+ } };
515
+ }
516
+ if (!regional && payload.operation === "edit" && map
517
+ && imageSubjectEditRoute(map, payload.subjectIds) === "exact") {
518
+ const request = grokImagineImage2EditRequest(map.grokSourceTaskId, payload.prompt ?? "", exactEditMaskIndexes(map.subjects, payload.subjectIds));
519
+ return { model: request.model, input: { ...request.input } };
520
+ }
521
+ const prompt = regional
522
+ ? imageRegionEditPrompt(payload.editRegions, payload.operation === "remove" ? "remove" : "edit", payload.prompt ?? "")
523
+ : payload.operation === "replace"
524
+ ? regionReplacementPrompt(map, payload.regions ?? [], [
525
+ { role: "base", artifactId: payload.sourceArtifactId },
526
+ ...(payload.replacementRefs ?? []).map((ref) => ({ role: "replacement", artifactId: ref.assetId })),
527
+ ], payload.prompt ?? "")
528
+ : payload.operation === "whole_image" ? payload.prompt ?? ""
529
+ : semanticEditPrompt(map, payload.subjectIds, payload.prompt ?? "");
530
+ const adapter = imageSubjectEditAdapter(payload.modelId);
531
+ const input = { prompt, imageUrls, width: source.width, height: source.height };
532
+ return payload.operation === "replace"
533
+ ? adapter.buildRegionRequest(input)
534
+ : adapter.buildSemanticRequest(input);
535
+ }
536
+ async function runAdapterImageSubjectEdit(engine, payload, adapter, route, prepared, sourceFilename, signal) {
537
+ const stored = await readStoredImageSubjectEditTask(engine, payload.imageAssetId);
538
+ const storedMatch = stored?.adapterId === adapter.id ? stored : null;
539
+ let request = storedMatch?.request ?? null;
540
+ let taskId = storedMatch?.taskId ?? null;
541
+ if (!request || !taskId) {
542
+ const imageUrls = [
543
+ await adapter.uploadAsset(engine, payload.sourceArtifactId, sourceFilename),
544
+ ];
545
+ if (route === "region") {
546
+ for (const ref of payload.replacementRefs ?? []) {
547
+ imageUrls.push(await adapter.uploadAsset(engine, ref.assetId, ref.filename));
548
+ }
549
+ }
550
+ request = route === "semantic"
551
+ ? adapter.buildSemanticRequest({ ...prepared, imageUrls })
552
+ : adapter.buildRegionRequest({ ...prepared, imageUrls });
553
+ taskId = await adapter.submit(engine, payload.imageAssetId, request);
554
+ }
555
+ if (payload.generationId) {
556
+ const generation = await engine.generations.update(payload.generationId, {
557
+ status: "awaiting_provider",
558
+ error: null,
559
+ });
560
+ if (!generation.ok)
561
+ throw new Error(generation.error.message);
562
+ }
563
+ await updateNotebookEditProgress(engine, payload, "awaiting_provider");
564
+ const images = await adapter.awaitResult(taskId, signal);
565
+ const [output, ...extras] = images;
566
+ if (!output)
567
+ throw new Error(`${adapter.id} image edit returned no image`);
568
+ return { output, extras, taskId, request };
569
+ }
570
+ async function generateSemanticSubjectEdit(engine, payload, adapter, map, sourceFilename, signal) {
571
+ return runAdapterImageSubjectEdit(engine, payload, adapter, "semantic", {
572
+ prompt: semanticEditPrompt(map, payload.subjectIds, payload.prompt ?? ""),
573
+ width: map.width,
574
+ height: map.height,
575
+ }, sourceFilename, signal);
576
+ }
577
+ async function generateWholeImageEdit(engine, payload, adapter, sourceFilename, width, height, signal) {
578
+ return runAdapterImageSubjectEdit(engine, payload, adapter, "semantic", {
579
+ prompt: payload.prompt ?? "",
580
+ width,
581
+ height,
582
+ }, sourceFilename, signal);
583
+ }
584
+ async function generateRegionReplacementEdit(engine, payload, adapter, map, sourceFilename, width, height, signal) {
585
+ const prompt = regionReplacementPrompt(map, payload.regions ?? [], payload.orderedImageRefs ?? [
586
+ { role: "base", artifactId: payload.sourceArtifactId },
587
+ ...(payload.replacementRefs ?? []).map((ref) => ({
588
+ role: "replacement",
589
+ artifactId: ref.assetId,
590
+ })),
591
+ ], payload.prompt ?? "");
592
+ return runAdapterImageSubjectEdit(engine, payload, adapter, "region", { prompt, width, height }, sourceFilename, signal);
593
+ }
594
+ async function downloadResultImages(urls, signal, label) {
595
+ const images = [];
596
+ for (const [index, url] of urls.entries()) {
597
+ const response = await fetch(url, { signal });
598
+ if (!response.ok) {
599
+ throw new Error(`Failed to download ${label} ${index + 1}: ${response.status} ${response.statusText}`);
600
+ }
601
+ images.push(await sharp(Buffer.from(await response.arrayBuffer())).png().toBuffer());
602
+ }
603
+ return images;
604
+ }
605
+ export async function persistExtraResultImages(engine, artifactId, extras) {
606
+ for (const [index, extra] of extras.entries()) {
607
+ const written = await engine.files.write(artifactId, `result-${index + 2}.png`, extra);
608
+ if (!written.ok)
609
+ throw new Error(written.error.message);
610
+ }
611
+ }
612
+ async function generateExactSubjectEdit(engine, payload, map, signal) {
613
+ if (!map.grokSourceTaskId) {
614
+ throw new Error("Exact Grok mask edit requires a Grok Imagine Image 2.0 source task");
615
+ }
616
+ const maskIndexes = exactEditMaskIndexes(map.subjects, payload.subjectIds);
617
+ if (!maskIndexes) {
618
+ throw new Error("Selected subjects are not capability-gated for exact mask edit");
619
+ }
620
+ const stored = await readStoredImageSubjectEditTask(engine, payload.imageAssetId);
621
+ const storedExact = stored?.adapterId === GROK_EXACT_MASK_ADAPTER_ID
622
+ ? stored
623
+ : null;
624
+ let request = storedExact?.request ?? null;
625
+ let taskId = storedExact?.taskId ?? null;
626
+ const provider = kieProvider();
627
+ if (!request || !taskId) {
628
+ request = grokImagineImage2EditRequest(map.grokSourceTaskId, payload.prompt ?? "", maskIndexes);
629
+ const metadata = await engine.metadata.artifacts.write(payload.imageAssetId, "generation-payload", request);
630
+ if (!metadata.ok)
631
+ throw new Error(metadata.error.message);
632
+ taskId = await submitMediaJob(provider, request, {
633
+ otp: mintKieOtp(request),
634
+ });
635
+ const taskMetadata = await engine.metadata.artifacts.write(payload.imageAssetId, "kie-task-id", { taskId });
636
+ if (!taskMetadata.ok)
637
+ throw new Error(taskMetadata.error.message);
638
+ }
639
+ if (payload.generationId) {
640
+ const generation = await engine.generations.update(payload.generationId, {
641
+ status: "awaiting_provider",
642
+ error: null,
643
+ });
644
+ if (!generation.ok)
645
+ throw new Error(generation.error.message);
646
+ }
647
+ await updateNotebookEditProgress(engine, payload, "awaiting_provider");
648
+ const resultJson = await waitForKieResult(provider, taskId, signal, Date.now() + KIE_RESULT_TIMEOUT_MS, "subject-edit");
649
+ const urls = parseResultUrls(resultJson);
650
+ const images = await downloadResultImages(urls, signal, "Grok exact edit");
651
+ const [output, ...extras] = images;
652
+ if (!output)
653
+ throw new Error("Grok image edit returned no image");
654
+ return { output, extras, taskId, request };
655
+ }
656
+ async function augmentMapWithGrokSegments(engine, sourceArtifactId, map, provider, signal, deadlineAt) {
657
+ const grokSourceTaskId = await readGrokImagineImage2SourceTaskId(engine, sourceArtifactId);
658
+ if (!grokSourceTaskId)
659
+ return map;
660
+ try {
661
+ const request = GrokImagineImage2SegmentMapRequestSchema.parse({
662
+ model: "grok-imagine-image-2-0/segment-map",
663
+ input: { task_id: grokSourceTaskId },
664
+ });
665
+ const segmentTaskId = await submitMediaJob(provider, request, {
666
+ otp: mintKieOtp(request),
667
+ });
668
+ const resultJson = await waitForKieResult(provider, segmentTaskId, signal, deadlineAt, "subject-analysis");
669
+ return {
670
+ ...map,
671
+ grokSourceTaskId,
672
+ subjects: applyGrokSegmentsToSubjects(map.subjects, parseGrokSegmentMapResult(resultJson)),
673
+ };
674
+ }
675
+ catch {
676
+ return { ...map, grokSourceTaskId };
677
+ }
678
+ }
679
+ function analyzeImageSubjectsPayload(value) {
680
+ if (value.type !== "analyze_image_subjects"
681
+ || typeof value.sourceArtifactId !== "string"
682
+ || typeof value.sourceObjectHash !== "string"
683
+ || typeof value.bookId !== "string"
684
+ || typeof value.jobId !== "string") {
685
+ return null;
686
+ }
687
+ return {
688
+ type: "analyze_image_subjects",
689
+ sourceArtifactId: value.sourceArtifactId,
690
+ sourceObjectHash: value.sourceObjectHash,
691
+ bookId: value.bookId,
692
+ jobId: value.jobId,
693
+ ...(typeof value.resumeTaskId === "string"
694
+ ? { resumeTaskId: value.resumeTaskId }
695
+ : {}),
696
+ };
697
+ }
698
+ function generateImageSubjectEditPayload(value) {
699
+ if (value.type !== "generate_image_subject_edit"
700
+ || typeof value.imageAssetId !== "string"
701
+ || typeof value.sourceArtifactId !== "string"
702
+ || typeof value.sourceObjectHash !== "string"
703
+ || !Array.isArray(value.subjectIds)
704
+ || !value.subjectIds.every((entry) => typeof entry === "string")
705
+ || (value.operation !== "remove" && value.operation !== "edit"
706
+ && value.operation !== "replace" && value.operation !== "whole_image")
707
+ || typeof value.bookId !== "string"
708
+ || typeof value.notebookId !== "string"
709
+ || typeof value.producerCellId !== "string"
710
+ || typeof value.outputCellId !== "string"
711
+ || typeof value.jobId !== "string") {
712
+ return null;
713
+ }
714
+ return {
715
+ type: "generate_image_subject_edit",
716
+ imageAssetId: value.imageAssetId,
717
+ sourceArtifactId: value.sourceArtifactId,
718
+ sourceObjectHash: value.sourceObjectHash,
719
+ subjectIds: value.subjectIds,
720
+ operation: value.operation,
721
+ bookId: value.bookId,
722
+ notebookId: value.notebookId,
723
+ producerCellId: value.producerCellId,
724
+ outputCellId: value.outputCellId,
725
+ jobId: value.jobId,
726
+ ...(typeof value.prompt === "string" ? { prompt: value.prompt } : {}),
727
+ ...(typeof value.modelId === "string" ? { modelId: value.modelId } : {}),
728
+ ...(typeof value.generationId === "string"
729
+ ? { generationId: value.generationId }
730
+ : {}),
731
+ ...(value.managedByNotebook === true ? { managedByNotebook: true } : {}),
732
+ };
733
+ }
734
+ function imageSubjectEditNeedsTerminalization(engine, payload) {
735
+ const notebook = engine.notebooks.read(payload.notebookId);
736
+ if (!notebook.ok)
737
+ return false;
738
+ const cellIds = new Set(notebook.value.cells.map((cell) => cell.id));
739
+ const states = [payload.producerCellId, payload.outputCellId].flatMap((cellId) => {
740
+ if (!cellIds.has(cellId))
741
+ return [];
742
+ const state = notebook.value.execution?.[cellId];
743
+ if (!state)
744
+ return [];
745
+ const matchesRun = state.runId === payload.jobId;
746
+ const matchesLegacy = !state.runId
747
+ && state.tool === "edit_image_subjects"
748
+ && state.outputArtifactId === payload.imageAssetId;
749
+ return matchesRun || matchesLegacy ? [state] : [];
750
+ });
751
+ return states.length > 0 && states.some((state) => state.status !== "completed" && state.status !== "error");
752
+ }
753
+ function imageSubjectAnalysisTerminalError(job) {
754
+ if (job.error?.message)
755
+ return job.error.message;
756
+ if (job.result && typeof job.result === "object" && !Array.isArray(job.result)) {
757
+ const result = job.result;
758
+ if (typeof result.error === "string" && result.error.trim()) {
759
+ return result.error.trim();
760
+ }
761
+ if (result.success === false && typeof result.message === "string" && result.message.trim()) {
762
+ return result.message.trim();
763
+ }
764
+ }
765
+ return job.state === "done"
766
+ ? "Subject analysis completed without publishing subjects. Retry the analysis."
767
+ : `Subject analysis job ${job.state}`;
768
+ }
769
+ export async function reconcileImageSubjectJobFailures(engine, bookId) {
770
+ let analysesFailed = 0;
771
+ let editsFailed = 0;
772
+ const analysisTerminalStates = ["failed", "aborted", "done"];
773
+ const analysisJobs = engine.jobs.queue.list({
774
+ states: [...analysisTerminalStates],
775
+ type: "analyze_image_subjects",
776
+ });
777
+ for (const job of analysisJobs) {
778
+ const payload = analyzeImageSubjectsPayload(job.payload);
779
+ if (!payload || payload.bookId !== bookId)
780
+ continue;
781
+ try {
782
+ const subjectMap = await readImageSubjectMapRecord(engine, payload.sourceArtifactId);
783
+ if (subjectMap?.status !== "analyzing"
784
+ || subjectMap.objectHash !== payload.sourceObjectHash
785
+ || subjectMap.jobId !== payload.jobId) {
786
+ continue;
787
+ }
788
+ const written = await writeAnalysisFailure(engine, payload, { width: subjectMap.width, height: subjectMap.height }, imageSubjectAnalysisTerminalError(job), subjectMap.taskId);
789
+ if (written)
790
+ analysesFailed += 1;
791
+ }
792
+ catch {
793
+ continue;
794
+ }
795
+ }
796
+ const editTerminalStates = ["failed", "aborted"];
797
+ const editJobs = engine.jobs.queue.list({
798
+ states: [...editTerminalStates],
799
+ type: "generate_image_subject_edit",
800
+ });
801
+ for (const job of editJobs) {
802
+ const payload = generateImageSubjectEditPayload(job.payload);
803
+ if (!payload
804
+ || payload.bookId !== bookId
805
+ || !imageSubjectEditNeedsTerminalization(engine, payload)) {
806
+ continue;
807
+ }
808
+ const message = job.error?.message ?? `Image subject edit job ${job.state}`;
809
+ const terminalized = await updateNotebookEditState(engine, payload, "error", message).then(() => true, () => false);
810
+ if (!terminalized)
811
+ continue;
812
+ if (payload.generationId) {
813
+ await engine.generations.update(payload.generationId, {
814
+ status: "failed",
815
+ error: message,
816
+ }).catch(() => undefined);
817
+ }
818
+ editsFailed += 1;
819
+ }
820
+ return { analysesFailed, editsFailed };
821
+ }
822
+ async function submitLayerDecompositionTask(engine, provider, source, jobId, startedAt, deadlineAt, prompt) {
823
+ const imageUrl = await uploadAssetToKie(engine, source.artifactId, source.file.name);
824
+ if (Date.now() >= deadlineAt) {
825
+ throw new Error("Subject analysis did not finish within 25 minutes. Retry the analysis.");
826
+ }
827
+ const request = seedreamLayerDecompositionRequest(imageUrl, source.width, source.height, prompt);
828
+ const taskId = await submitMediaJob(provider, request, {
829
+ otp: mintKieOtp(request),
830
+ });
831
+ const recorded = await writeCurrentImageSubjectAnalysisRecord(engine, {
832
+ version: IMAGE_SUBJECT_MAP_VERSION,
833
+ status: "analyzing",
834
+ artifactId: source.artifactId,
835
+ objectHash: source.objectHash,
836
+ width: source.width,
837
+ height: source.height,
838
+ jobId,
839
+ startedAt,
840
+ taskId,
841
+ });
842
+ if (!recorded) {
843
+ throw new Error("Subject analysis was superseded by a newer request");
844
+ }
845
+ return taskId;
846
+ }
847
+ export function registerImageSubjectJobs(engine) {
848
+ registerJobHandler("analyze_image_subjects", async (rawPayload, context) => {
849
+ const payload = rawPayload;
850
+ let dimensions = { width: 1, height: 1 };
851
+ let taskId;
852
+ try {
853
+ const source = await resolveImageSubjectSource(engine, payload.sourceArtifactId);
854
+ dimensions = { width: source.width, height: source.height };
855
+ if (source.objectHash !== payload.sourceObjectHash) {
856
+ throw new Error("The source image changed before subject analysis started");
857
+ }
858
+ const existing = await readImageSubjectMapRecord(engine, source.artifactId);
859
+ if (existing?.status !== "analyzing"
860
+ || existing.objectHash !== source.objectHash
861
+ || existing.jobId !== payload.jobId) {
862
+ throw new Error("Subject analysis was superseded by a newer request");
863
+ }
864
+ const startedAt = existing.startedAt;
865
+ taskId = existing.taskId;
866
+ const deadlineAt = imageSubjectAnalysisDeadline(startedAt);
867
+ if (Date.now() >= deadlineAt) {
868
+ throw new Error("Subject analysis did not finish within 25 minutes. Retry the analysis.");
869
+ }
870
+ const provider = kieProvider();
871
+ if (!taskId) {
872
+ taskId = await submitLayerDecompositionTask(engine, provider, source, payload.jobId, startedAt, deadlineAt);
873
+ }
874
+ let resultJson;
875
+ try {
876
+ resultJson = await waitForKieResult(provider, taskId, context.signal, deadlineAt, "subject-analysis");
877
+ }
878
+ catch (cause) {
879
+ const message = cause instanceof Error ? cause.message : String(cause);
880
+ if (!SEEDREAM_TOO_COMPLEX_PATTERN.test(message))
881
+ throw cause;
882
+ taskId = await submitLayerDecompositionTask(engine, provider, source, payload.jobId, startedAt, deadlineAt, SEEDREAM_LAYER_FALLBACK_PROMPT);
883
+ try {
884
+ resultJson = await waitForKieResult(provider, taskId, context.signal, deadlineAt, "subject-analysis");
885
+ }
886
+ catch (fallbackCause) {
887
+ const fallbackMessage = fallbackCause instanceof Error
888
+ ? fallbackCause.message
889
+ : String(fallbackCause);
890
+ if (SEEDREAM_TOO_COMPLEX_PATTERN.test(fallbackMessage)) {
891
+ throw new Error(SEEDREAM_TOO_COMPLEX_GUIDANCE);
892
+ }
893
+ throw fallbackCause;
894
+ }
895
+ }
896
+ const layerResult = parseSeedreamLayerResult(resultJson);
897
+ const seedreamReady = await persistSeedreamSubjectLayers(engine, source, {
898
+ jobId: payload.jobId,
899
+ taskId,
900
+ ...layerResult,
901
+ signal: context.signal,
902
+ });
903
+ const ready = await augmentMapWithGrokSegments(engine, source.artifactId, seedreamReady, provider, context.signal, deadlineAt);
904
+ const published = await writeCurrentImageSubjectAnalysisRecord(engine, ready);
905
+ if (!published) {
906
+ return {
907
+ success: false,
908
+ superseded: true,
909
+ artifactId: source.artifactId,
910
+ objectHash: source.objectHash,
911
+ };
912
+ }
913
+ return {
914
+ success: true,
915
+ artifactId: source.artifactId,
916
+ objectHash: source.objectHash,
917
+ subjects: ready.subjects.length,
918
+ };
919
+ }
920
+ catch (cause) {
921
+ const message = cause instanceof Error ? cause.message : String(cause);
922
+ await writeAnalysisFailure(engine, payload, dimensions, message, taskId).catch(() => undefined);
923
+ throw cause;
924
+ }
925
+ });
926
+ registerJobHandler("generate_image_subject_edit", async (rawPayload, context) => {
927
+ const payload = rawPayload;
928
+ try {
929
+ const source = await resolveImageSubjectSource(engine, payload.sourceArtifactId);
930
+ if (source.objectHash !== payload.sourceObjectHash) {
931
+ throw new Error("The source image changed before subject editing started");
932
+ }
933
+ const record = await readImageSubjectMapRecord(engine, payload.sourceArtifactId);
934
+ const readyMap = record?.status === "ready" && record.objectHash === source.objectHash
935
+ ? record
936
+ : null;
937
+ const editRegions = payload.editRegions ?? [];
938
+ const regional = editRegions.length > 0;
939
+ if (regional && payload.operation !== "edit" && payload.operation !== "remove") {
940
+ throw new Error("Drawn-region edits require an edit or remove operation");
941
+ }
942
+ if (payload.operation !== "replace"
943
+ && payload.operation !== "whole_image"
944
+ && !regional
945
+ && !readyMap) {
946
+ throw new Error("A current subject map is required before editing subjects");
947
+ }
948
+ if (payload.operation === "replace") {
949
+ const missingSubjects = (payload.regions ?? []).filter((region) => region.subjectId && !region.box && !readyMap);
950
+ if (missingSubjects.length > 0) {
951
+ throw new Error("Analyze image subjects before replacing by subject id");
952
+ }
953
+ }
954
+ await updateNotebookEditProgress(engine, payload, "running");
955
+ if ((payload.operation === "edit" || payload.operation === "whole_image") && !payload.prompt?.trim()) {
956
+ throw new Error("A prompt is required to edit selected subjects");
957
+ }
958
+ const adapter = payload.operation === "remove" && !regional
959
+ ? null
960
+ : imageSubjectEditAdapter(payload.modelId);
961
+ if (regional) {
962
+ const generated = await runAdapterImageSubjectEdit(engine, payload, adapter, "semantic", {
963
+ prompt: imageRegionEditPrompt(editRegions, payload.operation === "remove" ? "remove" : "edit", payload.prompt ?? ""),
964
+ width: source.width,
965
+ height: source.height,
966
+ }, source.file.name, context.signal);
967
+ const original = await engine.files.read(payload.sourceArtifactId, source.file.name);
968
+ if (!original.ok)
969
+ throw new Error(original.error.message);
970
+ const current = await resolveImageSubjectSource(engine, payload.sourceArtifactId);
971
+ if (current.objectHash !== payload.sourceObjectHash) {
972
+ throw new Error("The source image changed during region editing");
973
+ }
974
+ const output = await compositeImageRegionEdit(original.value, generated.output, editRegions);
975
+ await persistProcessedImage(engine, payload, readyMap, output, {
976
+ origin: payload.operation === "remove" ? "image_region_remove" : "image_region_edit",
977
+ model: generated.request.model,
978
+ kie_task_id: generated.taskId,
979
+ targeting: "drawn-regions",
980
+ edit_regions: editRegions,
981
+ });
982
+ }
983
+ else if (payload.operation === "whole_image") {
984
+ const generated = await generateWholeImageEdit(engine, payload, adapter, source.file.name, source.width, source.height, context.signal);
985
+ await persistProcessedImage(engine, payload, readyMap, generated.output, {
986
+ origin: "image_whole_edit",
987
+ model: generated.request.model,
988
+ kie_task_id: generated.taskId,
989
+ targeting: "whole-image",
990
+ });
991
+ }
992
+ else if (payload.operation === "replace") {
993
+ const regions = payload.regions ?? [];
994
+ if (regions.length === 0) {
995
+ throw new Error("Replacement edits require at least one region");
996
+ }
997
+ const generated = await generateRegionReplacementEdit(engine, payload, adapter, readyMap, source.file.name, source.width, source.height, context.signal);
998
+ await persistProcessedImage(engine, payload, readyMap, generated.output, {
999
+ origin: "image_subject_replace",
1000
+ model: generated.request.model,
1001
+ kie_task_id: generated.taskId,
1002
+ targeting: "region-replacement",
1003
+ image_urls: generated.request.input.image_urls,
1004
+ replacement_artifact_ids: (payload.replacementRefs ?? [])
1005
+ .map((ref) => ref.assetId),
1006
+ });
1007
+ }
1008
+ else if (payload.operation === "remove") {
1009
+ const output = await composeImageSubjectRemoval(engine, payload.sourceArtifactId, readyMap, payload.subjectIds);
1010
+ await persistProcessedImage(engine, payload, readyMap, output, {
1011
+ model: "local/sharp-layer-composite",
1012
+ });
1013
+ }
1014
+ else if (imageSubjectEditRoute(readyMap, payload.subjectIds) === "exact") {
1015
+ const generated = await generateExactSubjectEdit(engine, payload, readyMap, context.signal);
1016
+ await persistProcessedImage(engine, payload, readyMap, generated.output, {
1017
+ origin: "image_subject_edit",
1018
+ model: generated.request.model,
1019
+ kie_task_id: generated.taskId,
1020
+ targeting: "exact-mask",
1021
+ mask_indexs: generated.request.input.mask_indexs,
1022
+ });
1023
+ await persistExtraResultImages(engine, payload.imageAssetId, generated.extras);
1024
+ }
1025
+ else {
1026
+ const generated = await generateSemanticSubjectEdit(engine, payload, adapter, readyMap, source.file.name, context.signal);
1027
+ await persistProcessedImage(engine, payload, readyMap, generated.output, {
1028
+ origin: "image_subject_edit",
1029
+ model: generated.request.model,
1030
+ kie_task_id: generated.taskId,
1031
+ targeting: "semantic-label-and-box",
1032
+ });
1033
+ }
1034
+ await updateNotebookEditState(engine, payload, "completed");
1035
+ if (payload.generationId) {
1036
+ const updated = await engine.generations.update(payload.generationId, {
1037
+ status: "completed",
1038
+ outputArtifactId: payload.imageAssetId,
1039
+ error: null,
1040
+ });
1041
+ if (!updated.ok)
1042
+ throw new Error(updated.error.message);
1043
+ }
1044
+ await enqueueSearchIndex(engine, payload.imageAssetId);
1045
+ return { success: true, imageAssetId: payload.imageAssetId };
1046
+ }
1047
+ catch (cause) {
1048
+ const message = cause instanceof Error ? cause.message : String(cause);
1049
+ await updateNotebookEditState(engine, payload, "error", message).catch(() => undefined);
1050
+ if (payload.generationId) {
1051
+ await engine.generations.update(payload.generationId, {
1052
+ status: "failed",
1053
+ error: message,
1054
+ }).catch(() => undefined);
1055
+ }
1056
+ throw cause;
1057
+ }
1058
+ });
1059
+ }