@gentbajko/slopify 3.25.8 → 4.0.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 (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -1,9 +1,11 @@
1
1
  import { basename } from "node:path";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { referenceKey, thumbnailVariant } from "../admission/model.js";
3
4
  import { plainText } from "../article/plain.js";
4
5
  import { splitEndMatter } from "../article/split.js";
5
6
  import { checkAppearance } from "../images/appearance.js";
6
7
  import { checkScenes, sceneCountOf } from "../images/scenes.js";
8
+ import { checkDigest } from "../keywords/digest.js";
7
9
  import { spokenPassage } from "../narration/describe.js";
8
10
  import { observeNarration } from "../narration/live.js";
9
11
  import { validatePreparation } from "../narration/preparation.js";
@@ -11,24 +13,25 @@ import { prepareRequests } from "../narration/steering.js";
11
13
  import { chaptersFrom } from "../research/planner.js";
12
14
  import { sourcedAnswer } from "../research/synthesis.js";
13
15
  import { imageReviewers } from "../reviews/model.js";
16
+ import { checkPost, postText } from "../social/post-prompt.js";
14
17
  import { discardPreparedAssets, writeAsset } from "../storage/assets.js";
15
18
  import { outputPath } from "../storage/layout.js";
16
19
  import { probeDurationMs } from "../video/ffmpeg.js";
17
20
  import { parseAttribution } from "../voices/attribution.js";
18
21
  import { parseScript } from "../voices/script.js";
19
22
  import { imageAppearanceKey } from "./recipe-appearance.js";
23
+ import { digestKey } from "./recipe-digest.js";
20
24
  import { imageScenesKey, thumbnailScenesKey } from "./recipe-scenes.js";
21
25
  import { executeArticleRequests } from "./runtime-article.js";
22
26
  import { imageCall } from "./runtime-image.js";
23
27
  import { frozenInstructions } from "./runtime-instructions.js";
24
28
  import { articlePicture } from "./runtime-pictures.js";
25
- import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
29
+ import { executionPlan, executionView, workCatalogue, workCatalogueIfSaved, } from "./runtime-plan.js";
26
30
  import { preparedResult, preparedTexts, publishResult } from "./runtime-publication.js";
27
31
  import { clearSoftening, softenedPrompt, softeningRequested, softenMessages } from "./soften.js";
32
+ import { pieceAlreadyDone } from "./work-records.js";
28
33
  export async function executeProviderRecipe(deps, context, providers, piece) {
29
- if (deps.db
30
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
31
- .get(piece.id, context.work.workId)?.state === "done")
34
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
32
35
  return "done";
33
36
  const input = piece.input;
34
37
  const wrapped = providers.forPiece(piece.id);
@@ -64,7 +67,7 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
64
67
  ...(segment === undefined ? {} : { segment }),
65
68
  tokensIn: answer.value.usage?.inputTokens ?? 0,
66
69
  tokensOut: answer.value.usage?.outputTokens ?? 0,
67
- });
70
+ }, context.work.projectId);
68
71
  return "done";
69
72
  }
70
73
  if (input.kind === "tts") {
@@ -116,7 +119,7 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
116
119
  provider: input.provider,
117
120
  model: input.model,
118
121
  ...(durationMs === undefined ? {} : { audioSeconds: durationMs / 1000 }),
119
- });
122
+ }, context.work.projectId);
120
123
  }
121
124
  finally {
122
125
  discardPreparedAssets(deps, [asset]);
@@ -164,10 +167,10 @@ export async function executeProviderRecipe(deps, context, providers, piece) {
164
167
  provider: input.provider,
165
168
  model: input.model,
166
169
  ...(variant !== undefined ? { thumbnails: 1 } : { images: 1 }),
167
- });
170
+ }, context.work.projectId);
168
171
  return "done";
169
172
  }
170
- throw new Error("Slopify hit an internal error (this step has no provider request to run). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
173
+ throw new Error(internalError("this step has no provider request to run"));
171
174
  }
172
175
  // Soften and retry (`soften.ts`): the project's AI model rewords a refused image prompt
173
176
  // before the image is drawn again. Undefined when nobody asked, or the project has no model.
@@ -209,6 +212,14 @@ function checkAnswer(piece, answer) {
209
212
  const checked = checkAppearance(answer.text);
210
213
  return checked.ok ? undefined : checked.reason;
211
214
  }
215
+ // The keyword digest: every part written.
216
+ if (piece.key === digestKey && piece.input.kind === "llm") {
217
+ const checked = checkDigest(answer.text);
218
+ return checked.ok ? undefined : checked.reason;
219
+ }
220
+ // A post for another site: within its limit.
221
+ if (piece.input.kind === "llm" && piece.input.social !== undefined)
222
+ return checkPost(answer.text, piece.input.social);
212
223
  if (piece.key === "research:planner")
213
224
  return chaptersFrom(answer.text).length === 0
214
225
  ? "The AI model's research plan listed no chapters, so research could not go on. Use Try again; if it keeps happening, choose another model in the Providers section of Edit project."
@@ -240,7 +251,11 @@ async function publishText(deps, context, piece, answer) {
240
251
  throw new Error(checked.reason);
241
252
  await publishResult(deps, context, piece, preparedTexts(deps, context, piece, [
242
253
  ["instructions", "instructions.md", frozenInstructions(deps, context, piece)],
243
- ]), { scenes: checked.scenes, text: answer.text });
254
+ ]), {
255
+ scenes: checked.scenes,
256
+ ...(checked.details === undefined ? {} : { details: checked.details }),
257
+ text: answer.text,
258
+ });
244
259
  return;
245
260
  }
246
261
  if (piece.key === imageAppearanceKey && piece.input.kind === "llm") {
@@ -252,6 +267,37 @@ async function publishText(deps, context, piece, answer) {
252
267
  ]), { appearance: checked.appearance, text: answer.text });
253
268
  return;
254
269
  }
270
+ if (piece.input.kind === "llm" && piece.input.social !== undefined) {
271
+ const target = piece.input.social;
272
+ const problem = checkPost(answer.text, target);
273
+ if (problem !== undefined)
274
+ throw new Error(problem);
275
+ const text = postText(answer.text);
276
+ const asset = writeAsset(deps, context.work.projectId, `post-${target.platform}.txt`, Buffer.from(text));
277
+ let outputs;
278
+ try {
279
+ outputs = [
280
+ preparedResult(deps, context, piece, "social_post", asset, null, {
281
+ platform: target.platform,
282
+ }),
283
+ ];
284
+ }
285
+ catch (error) {
286
+ discardPreparedAssets(deps, [asset]);
287
+ throw error;
288
+ }
289
+ await publishResult(deps, context, piece, outputs, { text });
290
+ return;
291
+ }
292
+ if (piece.key === digestKey && piece.input.kind === "llm") {
293
+ const checked = checkDigest(answer.text);
294
+ if (!checked.ok)
295
+ throw new Error(checked.reason);
296
+ await publishResult(deps, context, piece, preparedTexts(deps, context, piece, [
297
+ ["instructions", "instructions.md", frozenInstructions(deps, context, piece)],
298
+ ]), { digest: checked.digest, text: answer.text });
299
+ return;
300
+ }
255
301
  if (piece.input.kind === "llm" && piece.input.preparation !== undefined) {
256
302
  const checked = validatePreparation(answer.text, piece.input.preparation.source);
257
303
  if (!checked.ok)
@@ -314,7 +360,7 @@ async function publishText(deps, context, piece, answer) {
314
360
  : [];
315
361
  const title = titles[Number(piece.key.split(":").at(-1)) - 1];
316
362
  if (typeof title !== "string")
317
- throw new Error("Slopify hit an internal error (a research chapter has no title in the research plan). Use Try again; if it happens again, use Download diagnostics in Settings and report it.");
363
+ throw new Error(internalError("a research chapter has no title in the research plan"));
318
364
  const asset = writeAsset(deps, context.work.projectId, `research-${piece.key.split(":").at(-1)}.md`, Buffer.from(answer.text));
319
365
  await publishResult(deps, context, piece, [], { title, notes: answer.text }, asset);
320
366
  return;
@@ -328,13 +374,11 @@ function narrationLabel(deps, context, piece) {
328
374
  return "Narration";
329
375
  const name = piece.input.segment === "body" ? "Body" : piece.input.segment === "intro" ? "Intro" : "Outro";
330
376
  const view = executionView(deps, context.work.projectId, context.work.revisionId);
331
- const row = deps.db
332
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
333
- .get(context.work.workId);
334
- if (view === undefined || row?.recipe_context === undefined || row.recipe_context === null)
377
+ const catalogue = workCatalogueIfSaved(deps.db, context.work.workId);
378
+ if (view === undefined || catalogue === undefined)
335
379
  return name;
336
380
  const segment = piece.input.segment;
337
- const parts = executionPlan(deps, view, savedCatalogue(row.recipe_context)).recipes.filter((recipe) => recipe.input.kind === "tts" && recipe.input.segment === segment);
381
+ const parts = executionPlan(deps, view, catalogue).recipes.filter((recipe) => recipe.input.kind === "tts" && recipe.input.segment === segment);
338
382
  const index = parts.findIndex((recipe) => recipe.key === piece.key);
339
383
  return parts.length > 1 && index >= 0 ? `${name} part ${index + 1} of ${parts.length}` : name;
340
384
  }
@@ -345,10 +389,7 @@ function checkPreparation(deps, context, piece, answer) {
345
389
  const checked = validatePreparation(answer.text, source);
346
390
  if (!checked.ok)
347
391
  return checked.reason;
348
- const row = deps.db
349
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
350
- .get(context.work.workId);
351
- const model = savedCatalogue(row?.recipe_context).tts.find((model) => model.provider === "inworld" && model.id === "inworld-tts-2");
392
+ const model = workCatalogue(deps.db, context.work.workId).tts.find((model) => model.provider === "inworld" && model.id === "inworld-tts-2");
352
393
  const prepared = prepareRequests(source, checked.cues, model?.tts.maxCharacters ?? 4000);
353
394
  return prepared.ok ? undefined : prepared.reason;
354
395
  }
@@ -1,11 +1,12 @@
1
1
  import { transact } from "../../kernel/db/tx.js";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { fingerprint } from "../../kernel/runner/work.js";
3
4
  import { publicationAuthority } from "../revisions/publication-rules.js";
4
5
  import { commitRevisionOutputs } from "../revisions/publish.js";
5
6
  import { insertAsset, insertManifestOutput, revisionById } from "../revisions/repo.js";
6
7
  import { discardPreparedAssets, writeAsset } from "../storage/assets.js";
7
8
  import { rebindPublishedNarration } from "./runtime-narration-publication.js";
8
- import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
9
+ import { executionPlan, executionView, workCatalogueIfSaved } from "./runtime-plan.js";
9
10
  export function preparedResult(deps, context, piece, role, asset, durationMs = null, meta = {}) {
10
11
  return {
11
12
  // A project holds many images, and one video and several images per short.
@@ -15,6 +16,8 @@ export function preparedResult(deps, context, piece, role, asset, durationMs = n
15
16
  role === "animated_image" ||
16
17
  // Level the volume: one levelled narration per segment.
17
18
  role === "audio_levelled" ||
19
+ // A post per site.
20
+ role === "social_post" ||
18
21
  // The second and third thumbnails; the first keeps the slot it always had.
19
22
  (role === "thumbnail" && piece.key !== "thumbnail:image")
20
23
  ? piece.key
@@ -39,7 +42,7 @@ export function preparedResult(deps, context, piece, role, asset, durationMs = n
39
42
  export function preparedText(deps, context, piece, role, filename, text) {
40
43
  const output = preparedTexts(deps, context, piece, [[role, filename, text]])[0];
41
44
  if (output === undefined)
42
- throw new Error("Slopify hit an internal error (a text file could not be prepared). Try again; if it happens again, use Download diagnostics in Settings and report it.");
45
+ throw new Error(internalError("a text file could not be prepared"));
43
46
  return output;
44
47
  }
45
48
  export function preparedTexts(deps, context, piece, texts) {
@@ -173,11 +176,9 @@ function pieceIndex(deps, context, piece) {
173
176
  if (localIndex >= 0)
174
177
  return localIndex + 1;
175
178
  const view = executionView(deps, context.work.projectId, context.work.revisionId);
176
- const row = deps.db
177
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
178
- .get(context.work.workId);
179
- if (view !== undefined && row?.recipe_context !== null && row?.recipe_context !== undefined) {
180
- const keys = executionPlan(deps, view, savedCatalogue(row.recipe_context)).recipes.filter((recipe) => recipe.stage === context.work.kind &&
179
+ const catalogue = workCatalogueIfSaved(deps.db, context.work.workId);
180
+ if (view !== undefined && catalogue !== undefined) {
181
+ const keys = executionPlan(deps, view, catalogue).recipes.filter((recipe) => recipe.stage === context.work.kind &&
181
182
  (piece.input.kind !== "llm" ||
182
183
  piece.input.preparation?.segment !== "body" ||
183
184
  (recipe.input.kind === "llm" && recipe.input.preparation?.segment === "body")) &&
@@ -206,10 +207,10 @@ export function retainPartialArticle(deps, context, piece, text) {
206
207
  if (authority === undefined)
207
208
  return;
208
209
  if (authority.key !== piece.key || authority.fingerprint !== piece.fingerprint)
209
- throw new Error("Slopify hit an internal error (a partly written article doesn't match its saved step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
210
+ throw new Error(internalError("a partly written article doesn't match its saved step"));
210
211
  const revision = revisionById(deps.db, context.work.projectId, context.work.revisionId);
211
212
  if (revision === undefined)
212
- throw new Error("Slopify hit an internal error (the project version for a partly written article is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
213
+ throw new Error(internalError("the project version for a partly written article is missing"));
213
214
  insertAsset(deps.db, result.asset);
214
215
  insertManifestOutput(deps.db, revision, {
215
216
  slot: `article:partial:${piece.id}`,
@@ -1,17 +1,21 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { z } from "zod";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { thinkingModes } from "../../kernel/ports/llm.js";
4
5
  import { plainText } from "../article/plain.js";
5
6
  import { defaultReviewPrompts, narrationItemKey, reviewStages, } from "../reviews/model.js";
6
7
  import { nextAttempt, reviewOutcome } from "../reviews/outcome.js";
7
8
  import { latestVerdict, saveVerdict } from "../reviews/repo.js";
8
9
  import { parseVerdict, reviewMessages } from "../reviews/verdict.js";
10
+ import { isReadyOutput } from "../revisions/view.js";
9
11
  import { pickedShortsOf } from "../shorts/clips.js";
10
12
  import { outputPath } from "../storage/layout.js";
13
+ import { openingOf, spokenArticle } from "./review-promise.js";
11
14
  import { exportSnapshot, revisionTranscript, } from "./runtime-export-inputs.js";
12
- import { savedCatalogue } from "./runtime-plan.js";
15
+ import { workLlm } from "./runtime-plan.js";
13
16
  import { publishResult } from "./runtime-publication.js";
14
17
  import { wordsSchema } from "./runtime-subtitles.js";
18
+ import { pieceAlreadyDone } from "./work-records.js";
15
19
  // What `recipe-reviews.ts` saves in the step's values, by position.
16
20
  const valuesSchema = z.tuple([
17
21
  z.enum(reviewStages),
@@ -32,9 +36,7 @@ const textMax = 60_000;
32
36
  // saved as a pending redo, which `review-redo.ts` starts once this step has finished, and
33
37
  // until then the item's dependents are held (`reviewHold`).
34
38
  export async function executeReviewRecipe(deps, context, providers, piece) {
35
- if (deps.db
36
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
37
- .get(piece.id, context.work.workId)?.state === "done")
39
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
38
40
  return "done";
39
41
  if (!context.maySubmit(piece.id))
40
42
  return "held";
@@ -44,33 +46,24 @@ export async function executeReviewRecipe(deps, context, providers, piece) {
44
46
  ? valuesSchema.safeParse(input.values)
45
47
  : undefined;
46
48
  if (values === undefined || !values.success)
47
- throw new Error("Slopify hit an internal error (a review step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
49
+ throw new Error(internalError("a review step was set up wrongly"));
48
50
  const [stage, itemKey, , provider, model, thinking, prompt, mode, retries] = values.data;
49
51
  const snapshot = exportSnapshot(deps, context, piece);
50
52
  const material = reviewMaterial(deps, context, snapshot, stage, itemKey, prompt);
51
53
  const previous = latestVerdict(deps.db, context.work.projectId, itemKey);
52
54
  const attempt = nextAttempt(previous, material.itemFingerprint);
53
- const row = deps.db
54
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
55
- .get(context.work.workId);
56
- const catalogued = savedCatalogue(row?.recipe_context).llm.find((one) => one.provider === provider && one.id === model);
57
55
  const level = thinking === null ? undefined : z.enum(thinkingModes).parse(thinking);
56
+ const call = workLlm(deps.db, context.work.workId, { provider, model, thinking: level }, piece.key);
58
57
  if (!context.maySubmit(piece.id))
59
58
  return "held";
60
- const answer = await providers.forPiece(piece.id).llm({
61
- provider,
62
- model,
63
- ...(level === undefined ? {} : { thinking: level }),
64
- thinkingConfig: level === undefined ? null : (catalogued?.llm.thinking?.[level] ?? null),
59
+ const answer = await providers.forPiece(piece.id).llm(call({
65
60
  messages: reviewMessages(material.request),
66
- webSearch: false,
67
61
  ...(material.images.length === 0 ? {} : { images: material.images }),
68
- previewLabel: piece.key,
69
62
  check: (value) => {
70
63
  const parsed = parseVerdict(value.text);
71
64
  return parsed.ok ? undefined : parsed.reason;
72
65
  },
73
- });
66
+ }));
74
67
  if (!answer.ok)
75
68
  return "held";
76
69
  const verdict = parseVerdict(answer.value.text);
@@ -126,10 +119,7 @@ export async function executeReviewRecipe(deps, context, providers, piece) {
126
119
  // and outro, and the prepared or hand-edited spoken text rather than the article as written.
127
120
  // Falls back to the article when those texts can't be read.
128
121
  function spokenText(deps, snapshot) {
129
- const present = ["intro", "body", "outro"].filter((kind) => snapshot.view.outputs.some((one) => one.selected &&
130
- one.available &&
131
- one.state === "ready" &&
132
- one.output.role === `audio_${kind}`));
122
+ const present = ["intro", "body", "outro"].filter((kind) => snapshot.view.outputs.some((one) => isReadyOutput(one) && one.output.role === `audio_${kind}`));
133
123
  try {
134
124
  const text = present.map((kind) => revisionTranscript(deps, snapshot, kind)).join("\n\n");
135
125
  if (text.trim() !== "")
@@ -145,9 +135,7 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
145
135
  const recipes = snapshot.plan.recipes;
146
136
  const config = view.revision.config;
147
137
  const output = (workKey, role) => view.outputs.find((row) => row.workKey === workKey &&
148
- row.selected &&
149
- row.available &&
150
- row.state === "ready" &&
138
+ isReadyOutput(row) &&
151
139
  (role === undefined || row.output.role === role));
152
140
  const path = (row) => outputPath(deps.paths, context.work.projectId, row.output.path);
153
141
  const text = (row) => clip(readFileSync(path(row), "utf8"));
@@ -178,6 +166,26 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
178
166
  itemFingerprint: article.fingerprint,
179
167
  };
180
168
  }
169
+ // The opening as the viewer will hear it, against what the title and the thumbnail brief
170
+ // promise; the whole article shows whether the promise is kept later on.
171
+ case "promise": {
172
+ const article = output("article:body", "article_md");
173
+ if (article === undefined)
174
+ throw missing("article", "Use More → Write the article again in the Article section");
175
+ const spoken = spokenArticle(readFileSync(path(article), "utf8"));
176
+ const thumbnail = recipes.find((one) => one.key === "thumbnail:image");
177
+ const brief = thumbnail?.input.kind === "image" ? thumbnail.input.prompt.trim() : "";
178
+ return {
179
+ request: request([
180
+ { label: "Video title", text: config.title },
181
+ ...(brief === "" ? [] : [{ label: "Thumbnail brief", text: clip(brief) }]),
182
+ { label: "Opening (first ~30 s)", text: openingOf(spoken) },
183
+ { label: "Whole article", text: clip(spoken) },
184
+ ]),
185
+ images: [],
186
+ itemFingerprint: article.fingerprint,
187
+ };
188
+ }
181
189
  case "images":
182
190
  case "thumbnail": {
183
191
  const image = output(itemKey);
@@ -252,10 +260,7 @@ export function reviewMaterial(deps, context, snapshot, stage, itemKey, prompt)
252
260
  const title = pickedShortsOf(pick?.piece.payload)?.shorts.find((clip) => clip.number === number)?.title ??
253
261
  "";
254
262
  const stills = view.outputs
255
- .filter((row) => row.workKey.startsWith(`${itemKey}:image:`) &&
256
- row.selected &&
257
- row.available &&
258
- row.state === "ready")
263
+ .filter((row) => row.workKey.startsWith(`${itemKey}:image:`) && isReadyOutput(row))
259
264
  .slice(0, 6);
260
265
  if (stills.length === 0)
261
266
  throw missing("short's images", "Use Make this short again in Edit project → Shorts");
@@ -2,6 +2,7 @@ import { executeAgentReviewRecipe } from "./runtime-agent-review.js";
2
2
  import { executeAnimateRecipe } from "./runtime-animate.js";
3
3
  import { executeDocumentRecipe } from "./runtime-document.js";
4
4
  import { executeExportRecipe } from "./runtime-export.js";
5
+ import { lateKeywordProviders, lateValuesOf } from "./runtime-keywords.js";
5
6
  import { executeLocalRecipe } from "./runtime-local.js";
6
7
  import { pieceLabel } from "./runtime-piece-label.js";
7
8
  import { executeProviderRecipe } from "./runtime-provider.js";
@@ -11,7 +12,9 @@ import { executeSubtitleRecipe } from "./runtime-subtitles.js";
11
12
  import { executeVoicesRecipe } from "./runtime-voices.js";
12
13
  import { executeYoutubeRecipe } from "./runtime-youtube.js";
13
14
  import { workPieces } from "./work-records.js";
14
- export async function runRevisionInvocation(deps, context, providers) {
15
+ export async function runRevisionInvocation(deps, context, sent) {
16
+ // `{{Slopify_Release_Date}}` and `{{Slopify_Duration}}` are filled as a request is sent.
17
+ const providers = lateKeywordProviders(sent, () => lateValuesOf(deps, context));
15
18
  const pieces = workPieces(deps.db, context.work.workId).filter((piece) => !piece.key.startsWith("article:continuation:"));
16
19
  let waiting = false;
17
20
  for (const piece of pieces) {
@@ -1,10 +1,10 @@
1
- import { readFileSync } from "node:fs";
2
- import { z } from "zod";
1
+ import { errorText, internalError } from "../../kernel/errors.js";
3
2
  import { withLanguage, wordTimingUnavailable } from "../../kernel/ports/languages.js";
4
3
  import { usesFigureCards, usesShorts } from "../admission/rules.js";
5
4
  import { captionBoldFont } from "../fonts/coverage.js";
6
- import { resolveBoldFont } from "../fonts/index.js";
7
5
  import { masterGoal } from "../loudness/model.js";
6
+ import { assetPathOrUndefined } from "../revisions/mutation-assets.js";
7
+ import { readyOutput } from "../revisions/view.js";
8
8
  import { clipWords } from "../shorts/captions.js";
9
9
  import { effectiveClips, pickedShortsOf } from "../shorts/clips.js";
10
10
  import { anchorKept, lostMessage, withNewPicks } from "../shorts/keep.js";
@@ -18,21 +18,21 @@ import { probeDurationMs } from "../video/ffmpeg.js";
18
18
  import { usesVoices } from "../voices/model.js";
19
19
  import { sentencesText, transcriptSentences } from "../youtube/transcript.js";
20
20
  import { shortsPickKey } from "./recipe-shorts.js";
21
+ import { exportMachine, warningsOf } from "./runtime-export.js";
21
22
  import { figureShots } from "./runtime-export-edit.js";
22
23
  import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
23
24
  import { imageCall } from "./runtime-image.js";
24
- import { savedCatalogue } from "./runtime-plan.js";
25
+ import { workLlm } from "./runtime-plan.js";
25
26
  import { softenIfAsked } from "./runtime-provider.js";
26
27
  import { preparedResult, preparedTexts, publishResult } from "./runtime-publication.js";
27
- import { wordsSchema } from "./runtime-subtitles.js";
28
+ import { readTimingWords } from "./runtime-subtitles.js";
28
29
  import { clearSoftening } from "./soften.js";
30
+ import { pieceAlreadyDone } from "./work-records.js";
29
31
  // The Shorts step's four kinds of work (`recipe-shorts.ts`): the pick, each clip's image
30
32
  // prompts, each image, and each clip's render. Each is one piece of its own, so a failure
31
33
  // names the clip it belongs to and Try again redoes only what did not finish.
32
34
  export async function executeShortsRecipe(deps, context, providers, piece) {
33
- if (deps.db
34
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
35
- .get(piece.id, context.work.workId)?.state === "done")
35
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
36
36
  return "done";
37
37
  if (!context.maySubmit(piece.id))
38
38
  return "held";
@@ -67,7 +67,7 @@ async function pick(deps, context, providers, piece, snapshot) {
67
67
  const llm = config.llm;
68
68
  if (llm === undefined || llm.provider.trim() === "" || llm.model.trim() === "")
69
69
  throw new Error("Shorts need an AI text model to pick the clips, and none is chosen. Choose one in Edit project → Providers, then Try again.");
70
- const sentences = transcriptSentences(timingWords(deps, context, view));
70
+ const sentences = transcriptSentences(readTimingWords(deps, context, view, shortsTimingPurpose).words);
71
71
  if (sentences.length === 0)
72
72
  throw new Error("The narration's word timing holds no words, so there is nothing to cut shorts from. Use More → Render the video again in the Video section, then Try again.");
73
73
  const audio = await revisionAudio(deps, context, view);
@@ -236,12 +236,12 @@ async function render(deps, context, piece, snapshot, clip) {
236
236
  .filter((recipe) => recipe.key.startsWith(prefix))
237
237
  .toSorted((left, right) => imageIndex(left.key) - imageIndex(right.key))
238
238
  .map((recipe) => {
239
- const row = view.outputs.find((one) => one.workKey === recipe.key && one.selected && one.available && one.state === "ready");
239
+ const row = readyOutput(view, { workKey: recipe.key });
240
240
  if (row === undefined)
241
241
  throw new Error(`Short ${String(clip.number)} is missing one of its images. Try again to make it again; if it keeps happening, use Make this short again in Edit project → Shorts.`);
242
242
  return outputPath(deps.paths, context.work.projectId, row.output.path);
243
243
  });
244
- const allWords = timingWords(deps, context, view);
244
+ const allWords = readTimingWords(deps, context, view, shortsTimingPurpose).words;
245
245
  const words = clipWords(allWords, clip.start, clip.end);
246
246
  // The upright cards whose descriptions fall in the clip, on the clip's own timeline.
247
247
  const figures = usesFigureCards(config)
@@ -268,8 +268,11 @@ async function render(deps, context, piece, snapshot, clip) {
268
268
  const prepared = [];
269
269
  const seconds = (clip.end - clip.start) / speed;
270
270
  try {
271
+ const machine = await exportMachine(deps, context.work.projectId);
271
272
  const master = await renderShort({
272
273
  bin: deps.ffmpeg,
274
+ machine: machine.machine,
275
+ notice: machine.notice,
273
276
  master: masterGoal(config, "video"),
274
277
  timeline,
275
278
  start: clip.start,
@@ -303,12 +306,13 @@ async function render(deps, context, piece, snapshot, clip) {
303
306
  prepared.push(preparedResult(deps, context, piece, "short_video", asset, durationMs, {
304
307
  short: clip.number,
305
308
  sentences: [clip.first, clip.last],
309
+ ...warningsOf(machine.notices),
306
310
  ...(master === undefined ? {} : { master }),
307
311
  }));
308
312
  await publishResult(deps, context, piece, prepared, { short: clip.number, start: clip.start, end: clip.end, durationMs }, asset);
309
313
  // One per rendered short, with no stage: stage "video" would read as a finished video.
310
314
  // Its pick and prompt tokens are not counted here.
311
- deps.count?.("stage.completed", { shorts: 1 });
315
+ deps.count?.("stage.completed", { shorts: 1 }, context.work.projectId);
312
316
  return "done";
313
317
  }
314
318
  finally {
@@ -317,12 +321,16 @@ async function render(deps, context, piece, snapshot, clip) {
317
321
  }
318
322
  // The clips the pick saved for this plan, with any range set by hand in place: the
319
323
  // selected pick whose fingerprint is the one the plan asks for.
320
- function savedClips(snapshot) {
324
+ export function savedClips(snapshot) {
321
325
  const recipe = snapshot.plan.recipes.find((one) => one.key === shortsPickKey);
322
- const row = snapshot.view.pieces.find((one) => one.key === shortsPickKey &&
323
- one.selected &&
324
- one.piece.state === "done" &&
325
- one.fingerprint === recipe?.fingerprint);
326
+ const done = snapshot.view.pieces.filter((one) => one.key === shortsPickKey && one.selected && one.piece.state === "done");
327
+ // A project from before an older fingerprint scheme keeps its pick under another
328
+ // fingerprint; the plan planned its clips from the version's one selected pick
329
+ // (`savedPicked` in `recipe-shorts.ts`), so the clips are that pick's.
330
+ const row = done.find((one) => one.fingerprint === recipe?.fingerprint) ??
331
+ (done.length === 1 && snapshot.plan.recipes.some((one) => /^shorts:\d+:/.test(one.key))
332
+ ? done[0]
333
+ : undefined);
326
334
  const picked = pickedShortsOf(row?.piece.payload);
327
335
  const shorts = snapshot.view.revision.config.shorts;
328
336
  if (picked === undefined || recipe === undefined || shorts === undefined)
@@ -337,57 +345,33 @@ async function backgroundMusic(deps, context, view) {
337
345
  if (assetId === undefined || shorts === undefined)
338
346
  return undefined;
339
347
  const fix = "Choose another file under Background music in Edit project → Shorts, or remove the music, then Try again.";
340
- const row = z
341
- .object({ path: z.string() })
342
- .safeParse(deps.db
343
- .prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
344
- .get(context.work.projectId, assetId));
345
- if (!row.success)
348
+ const saved = assetPathOrUndefined(deps, context.work.projectId, assetId);
349
+ if (saved === undefined)
346
350
  throw new Error(`The shorts' background music file is no longer in the project. ${fix}`);
347
- const path = outputPath(deps.paths, context.work.projectId, row.data.path);
351
+ const path = outputPath(deps.paths, context.work.projectId, saved);
348
352
  let durationMs;
349
353
  try {
350
354
  durationMs = await probeDurationMs(deps.ffmpeg, path, context.signal, deps.log);
351
355
  }
352
356
  catch (error) {
353
357
  context.signal.throwIfAborted();
354
- throw new Error(`The shorts' background music couldn't be read as audio (${error instanceof Error ? error.message : String(error)}). ${fix}`);
358
+ throw new Error(`The shorts' background music couldn't be read as audio (${errorText(error)}). ${fix}`);
355
359
  }
356
360
  if (durationMs <= 0)
357
361
  throw new Error(`The shorts' background music file holds no sound. ${fix}`);
358
362
  return { path, volume: musicVolumeOf(shorts) };
359
363
  }
360
- export function timingWords(deps, context, view) {
361
- const timing = view.outputs.find((row) => row.selected &&
362
- row.available &&
363
- row.state === "ready" &&
364
- row.workKey === "subtitles:timing" &&
365
- row.output.role === "subtitle_words");
366
- if (timing === undefined)
367
- throw new Error("The narration's word timing, which the shorts are cut and captioned from, is missing. Use More → Render the video again in the Video section, then Try again.");
368
- return wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8"))).words;
369
- }
364
+ // What the shorts read the narration's word timing for, in the sentence a missing one fails with.
365
+ const shortsTimingPurpose = "the shorts are cut and captioned from";
370
366
  // The project's text model, with the thinking settings saved for this step's catalogue.
371
367
  function llmCall(deps, context, piece, view, check) {
372
368
  const llm = view.revision.config.llm ?? { provider: "", model: "" };
373
- const row = deps.db
374
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
375
- .get(context.work.workId);
376
- const model = savedCatalogue(row?.recipe_context).llm.find((one) => one.provider === llm.provider && one.id === llm.model);
377
- return (messages) => ({
378
- provider: llm.provider,
379
- model: llm.model,
380
- ...(llm.thinking === undefined ? {} : { thinking: llm.thinking }),
381
- thinkingConfig: llm.thinking === undefined ? null : (model?.llm.thinking?.[llm.thinking] ?? null),
382
- messages,
383
- webSearch: false,
384
- previewLabel: piece.key,
385
- check: (value) => check(value.text),
386
- });
369
+ const call = workLlm(deps.db, context.work.workId, llm, piece.key);
370
+ return (messages) => call({ messages, check: (value) => check(value.text) });
387
371
  }
388
372
  function imageIndex(key) {
389
373
  return Number(key.split(":").at(-1));
390
374
  }
391
375
  function setupError() {
392
- return new Error("Slopify hit an internal error (a Shorts step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
376
+ return new Error(internalError("a Shorts step was set up wrongly"));
393
377
  }
@@ -6,7 +6,7 @@ import { sourceOf } from "../admission/model.js";
6
6
  import { usesShorts, usesYoutubeDescription } from "../admission/rules.js";
7
7
  import { reviewsNarration } from "../reviews/rules.js";
8
8
  import { currentRevisionId } from "../revisions/repo.js";
9
- import { getRevisionView } from "../revisions/view.js";
9
+ import { getRevisionView, isDonePiece, isReadyOutput } from "../revisions/view.js";
10
10
  import { settleScheduleRunsForProject } from "../schedules/repo.js";
11
11
  import { editNeedsTiming } from "../video/edit-settings.js";
12
12
  import { executionPlan, executionView, savedCatalogue } from "./runtime-plan.js";
@@ -146,14 +146,9 @@ export function executionStandings(deps, projectId, derived) {
146
146
  .map((entry) => {
147
147
  const pieces = workPieces(deps.db, entry.work.workId);
148
148
  const retained = pieces.length > 0 &&
149
- pieces.every((piece) => view.outputs.some((output) => output.workKey === piece.key &&
150
- output.selected &&
151
- output.available &&
152
- output.state === "ready") ||
149
+ pieces.every((piece) => view.outputs.some((output) => output.workKey === piece.key && isReadyOutput(output)) ||
153
150
  view.pieces.some((output) => output.key === piece.key &&
154
- output.selected &&
155
- output.available &&
156
- output.piece.state === "done" &&
151
+ isDonePiece(output) &&
157
152
  (output.fingerprint === piece.fingerprint ||
158
153
  madeFrom(deps, projectId, piece.key, output.fingerprint, piece.fingerprint))));
159
154
  // Save can bind an existing asset without granting its placeholder invocation.