@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,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { z } from "zod";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { subtitleModelDir } from "../../kernel/paths.js";
4
5
  import { projectLanguage } from "../../kernel/ports/languages.js";
5
6
  import { SubtitleMismatch } from "../../kernel/ports/subtitles.js";
@@ -7,6 +8,7 @@ import { narrationAliasesOf } from "../admission/rules.js";
7
8
  import { captionFont, captionFontDeps } from "../fonts/coverage.js";
8
9
  import { resolveFont } from "../fonts/index.js";
9
10
  import { validateCues } from "../revisions/rules.js";
11
+ import { donePiece, isReadyOutput, readyOutput } from "../revisions/view.js";
10
12
  import { discardPreparedAssets, writeAsset } from "../storage/assets.js";
11
13
  import { outputPath } from "../storage/layout.js";
12
14
  import { captionCues, serializeAss, serializeSrt, serializeVtt } from "../subtitles/captions.js";
@@ -47,6 +49,14 @@ const cuesSchema = z.object({
47
49
  })),
48
50
  omissions: wordsSchema.shape.omissions,
49
51
  });
52
+ // The narration's word timing (`subtitles:timing`), read for a step that needs it. `purpose` says
53
+ // what the step uses it for, in the sentence a missing timing fails with.
54
+ export function readTimingWords(deps, context, view, purpose) {
55
+ const timing = readyOutput(view, { workKey: "subtitles:timing", role: "subtitle_words" });
56
+ if (timing === undefined)
57
+ throw new Error(`The narration's word timing, which ${purpose}, is missing. Use More → Render the video again in the Video section, then Try again.`);
58
+ return wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
59
+ }
50
60
  export async function executeSubtitleRecipe(deps, context, piece) {
51
61
  if (!context.maySubmit(piece.id))
52
62
  return "held";
@@ -58,7 +68,7 @@ export async function executeSubtitleRecipe(deps, context, piece) {
58
68
  return cues(deps, context, piece, snapshot);
59
69
  if (piece.key === "subtitles:files")
60
70
  return files(deps, context, piece, snapshot);
61
- throw new Error("Slopify hit an internal error (unknown kind of caption step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
71
+ throw new Error(internalError("unknown kind of caption step"));
62
72
  }
63
73
  async function timing(deps, context, piece, snapshot) {
64
74
  const audio = await revisionAudio(deps, context, snapshot.view);
@@ -66,7 +76,7 @@ async function timing(deps, context, piece, snapshot) {
66
76
  throw new Error("Captions and the YouTube description need narration audio, but this project has none. Turn captions and the YouTube description off in Edit project, or turn narration on, then Try again.");
67
77
  const alignSubtitles = deps.alignSubtitles;
68
78
  if (alignSubtitles === undefined)
69
- throw new Error("Slopify hit an internal error (the caption timing tool is missing from this build). Try again; if it happens again, use Download diagnostics in Settings and report it.");
79
+ throw new Error(internalError("the caption timing tool is missing from this build"));
70
80
  if (!context.maySubmit(piece.id))
71
81
  return "held";
72
82
  const words = [];
@@ -131,10 +141,7 @@ async function cues(deps, context, piece, snapshot) {
131
141
  value = { cues: [...manual.cues], omissions: [] };
132
142
  }
133
143
  else {
134
- const output = view.outputs.find((one) => one.selected &&
135
- one.available &&
136
- one.state === "ready" &&
137
- one.workKey === "subtitles:timing");
144
+ const output = readyOutput(view, { workKey: "subtitles:timing" });
138
145
  if (output === undefined)
139
146
  throw new Error("Caption timing is missing. Use More → Render the video again in the Video section, then Try again.");
140
147
  const timed = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, output.output.path), "utf8")));
@@ -150,8 +157,8 @@ async function files(deps, context, piece, snapshot) {
150
157
  const { view } = snapshot;
151
158
  const config = view.revision.config.subtitles;
152
159
  if (config === undefined || config.mode === "off")
153
- throw new Error("Slopify hit an internal error (captions are off for this version of the project). Try again; if it happens again, use Download diagnostics in Settings and report it.");
154
- const row = view.pieces.find((one) => one.key === "subtitles:cues" && one.selected && one.available && one.piece.state === "done");
160
+ throw new Error(internalError("captions are off for this version of the project"));
161
+ const row = donePiece(view, "subtitles:cues");
155
162
  if (row?.piece.payload === null || row?.piece.payload === undefined)
156
163
  throw new Error("The captions haven't been prepared yet. Use More → Render the video again in the Video section, then Try again.");
157
164
  const value = cuesSchema.parse(JSON.parse(row.piece.payload));
@@ -219,9 +226,7 @@ async function files(deps, context, piece, snapshot) {
219
226
  ])
220
227
  prepared.push(preparedText(deps, context, piece, role, filename, text));
221
228
  prepared.push(preparedResult(deps, context, piece, "subtitle_font", writeAsset(deps, context.work.projectId, `selected${font.extension}`, readFileSync(font.path))));
222
- const media = view.outputs.find((one) => one.selected &&
223
- one.available &&
224
- one.state === "ready" &&
229
+ const media = view.outputs.find((one) => isReadyOutput(one) &&
225
230
  (one.output.role === "audio_export" ||
226
231
  (one.output.role === "video" && config.mode === "files")));
227
232
  if (media !== undefined) {
@@ -295,9 +300,6 @@ function mismatchChunks(snapshot, segment) {
295
300
  return [];
296
301
  }
297
302
  }
298
- export function mismatchMessage(snapshot, segment, mismatch) {
299
- return describeMismatch(mismatchChunks(snapshot, segment), segment, mismatch);
300
- }
301
303
  // The chunk that holds the words the audio stopped matching, by index; -1 when none does.
302
304
  export function mismatchChunk(chunks, mismatch) {
303
305
  const probe = comparable(mismatch.expected).split(" ").slice(0, 6).join(" ");
@@ -1,10 +1,11 @@
1
- import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { z } from "zod";
4
+ import { internalError } from "../../kernel/errors.js";
4
5
  import { masterFile, masterReport } from "../loudness/loudnorm.js";
5
6
  import { masterGoal } from "../loudness/model.js";
6
7
  import { allocateAsset, discardPreparedAssets, sealAsset } from "../storage/assets.js";
7
- import { outputPath, projectDir } from "../storage/layout.js";
8
+ import { projectDir } from "../storage/layout.js";
8
9
  import { audioExportArgs } from "../video/audio-export-args.js";
9
10
  import { runFfmpeg } from "../video/ffmpeg.js";
10
11
  import { audioChapters, audioFileArgs, ffmetadata } from "../voices/audio-files.js";
@@ -12,18 +13,17 @@ import { turnStarts } from "../voices/timing.js";
12
13
  import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
13
14
  import { lineLevelledAudio } from "./runtime-lines.js";
14
15
  import { preparedResult, publishResult } from "./runtime-publication.js";
15
- import { wordsSchema } from "./runtime-subtitles.js";
16
+ import { readTimingWords } from "./runtime-subtitles.js";
17
+ import { pieceAlreadyDone } from "./work-records.js";
16
18
  // The listening files of a multi-voice run: the MP3 and the M4B of the narration timeline,
17
19
  // with a chapter at each section of the script where its first turn is heard.
18
20
  export async function executeVoicesRecipe(deps, context, piece) {
19
- if (deps.db
20
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
21
- .get(piece.id, context.work.workId)?.state === "done")
21
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
22
22
  return "done";
23
23
  if (!context.maySubmit(piece.id))
24
24
  return "held";
25
25
  if (piece.key !== "voices:files" || piece.input.kind !== "local")
26
- throw new Error("Slopify hit an internal error (unknown kind of listening-file step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
26
+ throw new Error(internalError("unknown kind of listening-file step"));
27
27
  context.signal.throwIfAborted();
28
28
  const { view } = exportSnapshot(deps, context, piece);
29
29
  const sections = z
@@ -32,14 +32,7 @@ export async function executeVoicesRecipe(deps, context, piece) {
32
32
  .rest(z.unknown())
33
33
  .parse(piece.input.values)[2]
34
34
  .map(([title, firstTurn]) => ({ title, firstTurn }));
35
- const timing = view.outputs.find((row) => row.selected &&
36
- row.available &&
37
- row.state === "ready" &&
38
- row.workKey === "subtitles:timing" &&
39
- row.output.role === "subtitle_words");
40
- if (timing === undefined)
41
- throw new Error("The narration's word timing, which the chapter markers come from, is missing. Use More → Render the video again in the Video section, then Try again.");
42
- const { words } = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
35
+ const { words } = readTimingWords(deps, context, view, "the chapter markers come from");
43
36
  const audio = await revisionAudio(deps, context, view, { levelled: true });
44
37
  if (audio.length === 0)
45
38
  throw new Error("The MP3 and M4B files need narration audio, but this project has none. Turn narration on in Edit project, or turn Audio files off under Speakers (Play → Narration, or Edit project → Providers), then Try again.");
@@ -1,42 +1,33 @@
1
- import { readFileSync } from "node:fs";
1
+ import { internalError } from "../../kernel/errors.js";
2
2
  import { withLanguage } from "../../kernel/ports/languages.js";
3
- import { outputPath } from "../storage/layout.js";
4
3
  import { assembleDescription, checkDescriptionAnswer, descriptionMessages, tagsText, } from "../youtube/answer.js";
5
4
  import { defaultDescriptionPrompt } from "../youtube/model.js";
6
5
  import { keepsShape, titleShape } from "../youtube/titles.js";
7
6
  import { transcriptPassages, transcriptText } from "../youtube/transcript.js";
8
7
  import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
9
- import { savedCatalogue } from "./runtime-plan.js";
8
+ import { workLlm } from "./runtime-plan.js";
10
9
  import { preparedTexts, publishResult } from "./runtime-publication.js";
11
- import { wordsSchema } from "./runtime-subtitles.js";
10
+ import { readTimingWords } from "./runtime-subtitles.js";
11
+ import { pieceAlreadyDone } from "./work-records.js";
12
12
  // Writes the YouTube description, tags, pinned comment and other titles from the saved word timing: the transcript's
13
13
  // passages carry their start in the final video, so the chapters the model picks are real
14
14
  // times. An answer that breaks YouTube's chapter or tag rules is a failed attempt, which the
15
15
  // provider wrapper asks again while attempts remain.
16
16
  export async function executeYoutubeRecipe(deps, context, providers, piece) {
17
- if (deps.db
18
- .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
19
- .get(piece.id, context.work.workId)?.state === "done")
17
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
20
18
  return "done";
21
19
  if (!context.maySubmit(piece.id))
22
20
  return "held";
23
21
  context.signal.throwIfAborted();
24
22
  const input = piece.input;
25
23
  if (input.kind !== "local" || input.operation !== "youtube-description-v1")
26
- throw new Error("Slopify hit an internal error (the YouTube description step was set up wrongly). Try again; if it happens again, use Download diagnostics in Settings and report it.");
24
+ throw new Error(internalError("the YouTube description step was set up wrongly"));
27
25
  const { view } = exportSnapshot(deps, context, piece);
28
26
  const config = view.revision.config;
29
27
  const llm = config.llm;
30
28
  if (llm === undefined || llm.provider.trim() === "" || llm.model.trim() === "")
31
29
  throw new Error("The YouTube description needs an AI text model, and none is chosen. Choose one in Edit project → Providers, then Try again.");
32
- const timing = view.outputs.find((row) => row.selected &&
33
- row.available &&
34
- row.state === "ready" &&
35
- row.workKey === "subtitles:timing" &&
36
- row.output.role === "subtitle_words");
37
- if (timing === undefined)
38
- throw new Error("The narration's word timing, which the YouTube description's chapters come from, is missing. Use More → Render the video again in the Video section, then Try again.");
39
- const { words } = wordsSchema.parse(JSON.parse(readFileSync(outputPath(deps.paths, context.work.projectId, timing.output.path), "utf8")));
30
+ const { words } = readTimingWords(deps, context, view, "the YouTube description's chapters come from");
40
31
  const passages = transcriptPassages(words);
41
32
  if (passages.length === 0)
42
33
  throw new Error("The narration's word timing holds no words, so there is nothing to write the YouTube description from. Use More → Render the video again in the Video section, then Try again.");
@@ -56,25 +47,16 @@ export async function executeYoutubeRecipe(deps, context, providers, piece) {
56
47
  durationSeconds,
57
48
  transcript: transcriptText(passages),
58
49
  }), config.language);
59
- const row = deps.db
60
- .prepare("SELECT recipe_context FROM revision_work WHERE id=?")
61
- .get(context.work.workId);
62
- const model = savedCatalogue(row?.recipe_context).llm.find((one) => one.provider === llm.provider && one.id === llm.model);
50
+ const call = workLlm(deps.db, context.work.workId, llm, piece.key);
63
51
  if (!context.maySubmit(piece.id))
64
52
  return "held";
65
- const answer = await providers.forPiece(piece.id).llm({
66
- provider: llm.provider,
67
- model: llm.model,
68
- ...(llm.thinking === undefined ? {} : { thinking: llm.thinking }),
69
- thinkingConfig: llm.thinking === undefined ? null : (model?.llm.thinking?.[llm.thinking] ?? null),
53
+ const answer = await providers.forPiece(piece.id).llm(call({
70
54
  messages,
71
- webSearch: false,
72
- previewLabel: piece.key,
73
55
  check: (value) => {
74
56
  const checked = checkDescriptionAnswer(value.text, durationSeconds, config.title, shape);
75
57
  return checked.ok ? undefined : checked.reason;
76
58
  },
77
- });
59
+ }));
78
60
  if (!answer.ok)
79
61
  return "held";
80
62
  const checked = checkDescriptionAnswer(answer.value.text, durationSeconds, config.title);
@@ -103,6 +85,6 @@ export async function executeYoutubeRecipe(deps, context, providers, piece) {
103
85
  tokensIn: answer.value.usage?.inputTokens ?? 0,
104
86
  tokensOut: answer.value.usage?.outputTokens ?? 0,
105
87
  descriptions: 1,
106
- });
88
+ }, context.work.projectId);
107
89
  return "done";
108
90
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { currentRevisionId, revisionById } from "../revisions/repo.js";
4
5
  import { insertWorkPiece } from "./work-records.js";
5
6
  const reservationSchema = z.object({
@@ -19,10 +20,10 @@ export function transitionRevisionWork(deps, input) {
19
20
  const base = revisionById(db, input.projectId, input.baseRevisionId);
20
21
  const next = revisionById(db, input.projectId, input.revisionId);
21
22
  if (base === undefined || next === undefined)
22
- throw new Error("Slopify hit an internal error (a project version to switch between is missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
23
+ throw new Error(internalError("a project version to switch between is missing"));
23
24
  if (JSON.stringify(Object.entries(input.fingerprints).sort()) !==
24
25
  JSON.stringify(Object.entries(next.fingerprints).sort()))
25
- throw new Error("Slopify hit an internal error (the planned steps don't match the saved project version). Try again; if it happens again, use Download diagnostics in Settings and report it.");
26
+ throw new Error(internalError("the planned steps don't match the saved project version"));
26
27
  const reserved = db
27
28
  .prepare(`SELECT r.* FROM revision_work_reservations r JOIN revision_work w ON w.id=r.work_id WHERE r.project_id=? AND r.revision_id=? AND w.state IN ('pending','running','done')`)
28
29
  .all(input.projectId, input.baseRevisionId)
@@ -78,7 +79,7 @@ export function transitionRevisionWork(deps, input) {
78
79
  continue;
79
80
  const exact = input.recipes?.find((row) => row.key === key);
80
81
  if (exact !== undefined && exact.fingerprint !== fp)
81
- throw new Error("Slopify hit an internal error (a planned step doesn't match the saved project version). Try again; if it happens again, use Download diagnostics in Settings and report it.");
82
+ throw new Error(internalError("a planned step doesn't match the saved project version"));
82
83
  // An undo, a restore or a save repeated while a step waits for Resume asks for a
83
84
  // step the project already has a row for. A finished one is the result itself; a
84
85
  // held copy nobody started is the same placeholder. Minting another each time left a
@@ -93,7 +94,7 @@ export function transitionRevisionWork(deps, input) {
93
94
  .prepare("SELECT id FROM stages WHERE project_id=? AND kind=?")
94
95
  .get(input.projectId, kind);
95
96
  if (stage === undefined)
96
- throw new Error("Slopify hit an internal error (a planned step has no matching stage). Try again; if it happens again, use Download diagnostics in Settings and report it.");
97
+ throw new Error(internalError("a planned step has no matching stage"));
97
98
  const workId = deps.ids.next();
98
99
  const pieceId = deps.ids.next();
99
100
  db.prepare(`INSERT INTO revision_work(id,project_id,revision_id,stage_id,kind,fingerprint,state,dispatch_state,created_at) VALUES (?,?,?,?,?,?,'pending','held',?)`).run(workId, input.projectId, input.revisionId, z.string().parse(stage.id), kind, fp, deps.clock.now().toISOString());
@@ -178,5 +179,5 @@ export function stageForKey(key) {
178
179
  return "video";
179
180
  if (prefix === "document")
180
181
  return "document";
181
- throw new Error("Slopify hit an internal error (unknown kind of step). Try again; if it happens again, use Download diagnostics in Settings and report it.");
182
+ throw new Error(internalError("unknown kind of step"));
182
183
  }
@@ -42,3 +42,9 @@ export function insertWorkPiece(db, piece) {
42
42
  const input = recipeInputSchema.parse(piece.input);
43
43
  db.prepare(`INSERT INTO revision_work_pieces(id,work_id,work_key,request_fingerprint,fingerprint,input_json,continuation,generation_token,state,dispatch_state,submitted_at,logical_fingerprint) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)`).run(piece.id, piece.workId, piece.key, piece.requestFingerprint, piece.fingerprint, JSON.stringify(input), piece.continuation, piece.generationToken, piece.state, piece.dispatchState, piece.submittedAt, piece.logicalFingerprint ?? null);
44
44
  }
45
+ // A step run again after its piece finished (a restart, a second claim) has nothing to do.
46
+ export function pieceAlreadyDone(db, workId, pieceId) {
47
+ return (db
48
+ .prepare("SELECT state FROM revision_work_pieces WHERE id=? AND work_id=?")
49
+ .get(pieceId, workId)?.state === "done");
50
+ }
@@ -40,8 +40,7 @@ function clearsOf(action, kind) {
40
40
  // The previous video stays downloadable until the new render finishes. Clearing the video's
41
41
  // outputs here would take it away the moment the user pressed Re-render - or for the whole of
42
42
  // a re-narration, when the video comes along as a dependent. It is kept instead, and
43
- // `slices/video/run.ts` replaces the file and the rows in one swap once ffmpeg has exited
44
- // cleanly. A render that fails or is canceled leaves the old one standing.
43
+ // the render replaces the file and the rows in one swap once ffmpeg has exited cleanly. A render that fails or is canceled leaves the old one standing.
45
44
  if (kind === "video") {
46
45
  return "nothing";
47
46
  }
@@ -1,6 +1,7 @@
1
1
  import { rmSync } from "node:fs";
2
2
  import { z } from "zod";
3
3
  import { transact } from "../../kernel/db/tx.js";
4
+ import { errorText, internalError } from "../../kernel/errors.js";
4
5
  import { allPiecesOf, deletePiece, deletePieces, setPiece, } from "../../kernel/runner/piece-repo.js";
5
6
  import { projectById, stagesOf } from "../admission/repo.js";
6
7
  import { resetStage } from "../admission/stage-writes.js";
@@ -101,8 +102,7 @@ export function deleteImage(deps, projectId, outputId) {
101
102
  return { ok: false, reason: "last-image" };
102
103
  }
103
104
  return apply(deps, projectId, { kind: "image-deleted" }, loaded, () => {
104
- // `slices/images/run.ts` counts an image as landed only when its row and its file both
105
- // survive, so a delete removes both - and the piece that planned it, or the next run
105
+ // An image counts as landed only when its row and its file both survive, so a delete removes both - and the piece that planned it, or the next run
106
106
  // of the stage would read the plan and make the image again.
107
107
  deleteOutput(deps.db, image.id);
108
108
  dropImagePiece(deps, loaded.stages, image.path);
@@ -205,7 +205,7 @@ function clearStage(deps, stage, outputs) {
205
205
  // runs the returned cleanup only after commit. Finished narration is left intact.
206
206
  export function clearUnfinishedAudio(deps, projectId) {
207
207
  if (currentRevisionId(deps.db, projectId) !== undefined)
208
- throw new Error("Slopify hit an internal error (narration was changed the old way on a project that uses versions). Try again; if it happens again, use Download diagnostics in Settings and report it.");
208
+ throw new Error(internalError("narration was changed the old way on a project that uses versions"));
209
209
  const stage = stagesOf(deps.db, projectId).find((one) => one.kind === "audio");
210
210
  if (stage === undefined ||
211
211
  stage.state === "done" ||
@@ -219,8 +219,8 @@ export function clearUnfinishedAudio(deps, projectId) {
219
219
  return [() => removeFiles(deps, projectId, orphaned)];
220
220
  }
221
221
  // A regenerated image is replaced in place at the same index, so the piece keeps the prompt
222
- // and the index it was planned with and only stops naming a file: `slices/images/run.ts`
223
- // then sees one image of its plan still to make, and the file it named can be removed.
222
+ // and the index it was planned with and only stops naming a file: the images stage then
223
+ // sees one image of its plan still to make, and the file it named can be removed.
224
224
  function forgetImageFile(deps, stages, path) {
225
225
  for (const piece of imagePieces(deps, stages, path)) {
226
226
  if (piece.payload === null) {
@@ -274,7 +274,7 @@ function removeFiles(deps, projectId, orphaned) {
274
274
  // the action here would leave the user with a stage that cannot be re-run.
275
275
  deps.log.write("warn", "reruns.file", {
276
276
  projectId,
277
- detail: `${path}: ${messageOf(error)}`,
277
+ detail: `${path}: ${errorText(error)}`,
278
278
  });
279
279
  }
280
280
  }
@@ -291,6 +291,3 @@ function stageOf(stages, kind) {
291
291
  function imageOf(outputs, outputId) {
292
292
  return outputs.find((output) => output.id === outputId && output.role === "image");
293
293
  }
294
- function messageOf(error) {
295
- return error instanceof Error ? error.message : String(error);
296
- }
@@ -1,3 +1,6 @@
1
+ // Who may answer an external review: the agent it waits for, or the person. The in-app
2
+ // assistant never does (`edge/http/external-reviews.ts`).
3
+ export const externalAnswerers = ["person", "agent"];
1
4
  // ceiling: the owner's number. An hourly agent routine answers well inside two hours; a run
2
5
  // with no agent at all still finishes the same evening.
3
6
  export const externalReviewWaitMs = 2 * 60 * 60 * 1000;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { transact } from "../../kernel/db/tx.js";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { liveProject } from "../admission/repo.js";
4
5
  import { externalReviewByFingerprint, externalReviewById } from "./external-rows.js";
5
6
  import { liveWork, reviewStanding, wantedBy } from "./external-waiting.js";
@@ -17,7 +18,7 @@ export function openExternalReview(db, input) {
17
18
  WHERE external_reviews.state IN ('superseded','canceled')`).run(input.id, input.projectId, input.revisionId, input.workId, input.pieceId, input.workKey, input.itemKey, input.stage, input.itemFingerprint, input.reviewFingerprint, JSON.stringify(input.material), input.waitingSince, input.deadlineAt);
18
19
  const saved = externalReviewByFingerprint(db, input.projectId, input.reviewFingerprint);
19
20
  if (saved === undefined)
20
- throw new Error("Slopify hit an internal error (an external review could not be saved). Retry stage; if it happens again, use Download diagnostics in Settings and report it.");
21
+ throw new Error(internalError("an external review could not be saved"));
21
22
  return saved;
22
23
  }
23
24
  // Cancel stops a run's reviews waiting. Retried, its step asks again (`openExternalReview`) and
@@ -1,6 +1,5 @@
1
1
  import { z } from "zod";
2
- import { actors } from "../agents/model.js";
3
- import { externalReviewStates } from "./external-model.js";
2
+ import { externalAnswerers, externalReviewStates } from "./external-model.js";
4
3
  import { reviewStages } from "./model.js";
5
4
  const materialSchema = z.object({
6
5
  request: z.object({
@@ -28,7 +27,7 @@ const rowSchema = z.object({
28
27
  waiting_since: z.string(),
29
28
  deadline_at: z.string(),
30
29
  verdict: z.string().nullable(),
31
- answered_by: z.enum(actors).nullable(),
30
+ answered_by: z.enum(externalAnswerers).nullable(),
32
31
  answered_at: z.string().nullable(),
33
32
  });
34
33
  // One stored review as the slice speaks of it; throws for a row that can't be read back.
@@ -1,8 +1,27 @@
1
1
  // Automatic reviews: a reviewer model looks at each finished item of a stage (the article,
2
- // every image, the narration, the thumbnail, every short) before the run moves on, and says
2
+ // the opening's promise, every image, the narration, the thumbnail, every short) before the
3
+ // run moves on, and says
3
4
  // pass or fail with its reasons. A failed item is flagged, or made again while its retries
4
5
  // last. Browser-safe: Play, Edit project and the project page read these names and limits too.
5
- export const reviewStages = ["article", "images", "narration", "thumbnail", "shorts"];
6
+ // "promise" reads the written article's opening against the title and thumbnail before the
7
+ // paid narration starts; it is last so every list saved before it keeps its order.
8
+ export const reviewStages = [
9
+ "article",
10
+ "images",
11
+ "narration",
12
+ "thumbnail",
13
+ "shorts",
14
+ "promise",
15
+ ];
16
+ // The stages in the order a run reaches them, as the Reviews rows list them.
17
+ export const reviewStagesInRunOrder = [
18
+ "article",
19
+ "promise",
20
+ "images",
21
+ "narration",
22
+ "thumbnail",
23
+ "shorts",
24
+ ];
6
25
  // Off is the same as the stage having no entry, which is what every project saved before
7
26
  // reviews existed has.
8
27
  export const reviewModes = ["off", "flag", "redo"];
@@ -26,6 +45,7 @@ export const reviewStageLabels = {
26
45
  narration: "Narration",
27
46
  thumbnail: "Thumbnail",
28
47
  shorts: "Shorts",
48
+ promise: "Promise check (before narration)",
29
49
  };
30
50
  export const reviewModeLabels = {
31
51
  off: "Off",
@@ -47,9 +67,6 @@ export function reviewRetriesOf(settings) {
47
67
  export function reviewedByAgent(settings) {
48
68
  return settings?.reviewer === "agent";
49
69
  }
50
- export function reviewedStages(settings) {
51
- return reviewStages.filter((stage) => reviewModeOf(settings, stage) !== "off");
52
- }
53
70
  // The key a stage's picked Review prompt renders under in `config.rendered`.
54
71
  export function reviewPromptKey(stage) {
55
72
  return `review.${stage}`;
@@ -57,12 +74,12 @@ export function reviewPromptKey(stage) {
57
74
  // A review's step is `review:` and the key of the item it reviews. The narration is
58
75
  // reviewed as a whole, so its item key names no step.
59
76
  export const narrationItemKey = "narration";
77
+ // The promise check reviews the article, but as its own item, so its verdict never stands in
78
+ // for the article review's.
79
+ export const promiseItemKey = "promise";
60
80
  export function reviewKey(itemKey) {
61
81
  return `review:${itemKey}`;
62
82
  }
63
- export function reviewedItemKey(key) {
64
- return key.startsWith("review:") ? key.slice("review:".length) : undefined;
65
- }
66
83
  export function reviewSettingsForm(settings) {
67
84
  return {
68
85
  provider: settings.provider,
@@ -116,4 +133,5 @@ export const defaultReviewPrompts = {
116
133
  narration: "Review this narration against the text it was read from. The transcript was heard back from the audio; the gaps list stretches of text the timing could not hear. Fail it when words, sentences or passages are missing or garbled in a way a listener would notice. Ignore punctuation, numbers written out, and small differences in how words were transcribed.",
117
134
  thumbnail: "Review this thumbnail as it would be seen on a phone, about 320 pixels wide. Fail it when its text (if any) cannot be read at that size, when the subject is unclear or cluttered, when a hand or body is malformed, or when it shows stray letters or watermarks.",
118
135
  shorts: "Review these images from one vertical short, as they would be seen full screen on a phone. Fail them when the subject is unclear, when a hand or body is malformed, when they show stray text, letters or watermarks, or when they do not fit the short's title.",
136
+ promise: "Review whether this video's opening keeps the promise its title and thumbnail make. Pass it when the first 30 seconds start delivering what the title and thumbnail promise and give the viewer a reason to keep watching. Fail it when the opening is a slow preamble, greets the viewer or talks about the channel (subscribe, welcome back) before the subject, is about something other than the title, or promises something the rest of the article never delivers. When you fail it, name the sentence from the article that should come first.",
119
137
  };
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { actors } from "../agents/model.js";
3
4
  import { reviewAnswerers, reviewStages } from "./model.js";
4
5
  const rowSchema = z.object({
@@ -56,7 +57,7 @@ export function saveVerdict(db, record) {
56
57
  created_at=excluded.created_at`).run(record.id, record.projectId, record.revisionId, record.itemKey, record.stage, record.itemFingerprint, record.reviewFingerprint, record.passed ? 1 : 0, JSON.stringify(record.reasons), record.outcome, record.attempt, record.action, record.actionAt, record.redoState, record.redoError, record.answeredBy, record.actionBy, record.createdAt);
57
58
  const saved = verdictByReview(db, record.projectId, record.reviewFingerprint);
58
59
  if (saved === undefined)
59
- throw new Error("Slopify hit an internal error (a review's verdict could not be saved). Try again; if it happens again, use Download diagnostics in Settings and report it.");
60
+ throw new Error(internalError("a review's verdict could not be saved"));
60
61
  return saved;
61
62
  }
62
63
  export function verdictByReview(db, projectId, reviewFingerprint) {
@@ -6,6 +6,7 @@ export function stageMakesItems(draft, stage) {
6
6
  const { sources } = draft;
7
7
  switch (stage) {
8
8
  case "article":
9
+ case "promise":
9
10
  return sources.article === "generate";
10
11
  case "images":
11
12
  return sources.images === "generate";
@@ -1,9 +1,12 @@
1
- import { narrationItemKey } from "./model.js";
1
+ import { narrationItemKey, promiseItemKey } from "./model.js";
2
2
  // The step whose output a verdict is about: the word timing for the narration (it is what
3
- // the reviewer heard), a short's render for the short, and the item's own step otherwise.
3
+ // the reviewer heard), the article for the promise check, a short's render for the short, and
4
+ // the item's own step otherwise.
4
5
  export function reviewedOutputKey(itemKey) {
5
6
  if (itemKey === narrationItemKey)
6
7
  return "subtitles:timing";
8
+ if (itemKey === promiseItemKey)
9
+ return "article:body";
7
10
  if (/^shorts:\d+$/.test(itemKey))
8
11
  return `${itemKey}:render`;
9
12
  return itemKey;
@@ -8,6 +8,7 @@ import { contentTypeOf, downloadName, fileVersion, slugOf, versionedName, } from
8
8
  import { outputPath } from "../storage/layout.js";
9
9
  import { projectTitle } from "../storage/repo.js";
10
10
  import { outputSchema } from "../storage/schema.js";
11
+ import { assetPathOrUndefined } from "./mutation-assets.js";
11
12
  import { positionedOutput } from "./projection.js";
12
13
  import { outputsForRevision, revisionById } from "./repo.js";
13
14
  import { stagePieceSchema } from "./schema.js";
@@ -71,12 +72,10 @@ export function revisionSetZip(deps, projectId, revisionId, set, only) {
71
72
  .map((row) => ({ ...positionedOutput(revision, row), slot: row.slot }))
72
73
  .sort((a, b) => setOrder(a.output.role, a.output.meta) - setOrder(b.output.role, b.output.meta));
73
74
  for (const row of members) {
74
- const asset = deps.db
75
- .prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
76
- .get(projectId, row.assetId);
77
- if (asset === undefined)
75
+ const saved = assetPathOrUndefined(deps, projectId, row.assetId);
76
+ if (saved === undefined)
78
77
  throw new Error("Revision image asset is not registered.");
79
- const path = outputPath(deps.paths, projectId, z.string().parse(asset.path));
78
+ const path = outputPath(deps.paths, projectId, saved);
80
79
  if (statSync(path, { throwIfNoEntry: false })?.isFile() !== true)
81
80
  continue;
82
81
  // Images and video are already compressed, so they are stored rather than deflated.
@@ -186,11 +186,14 @@ export async function measureAudio(deps, projectId, path) {
186
186
  return duration;
187
187
  }
188
188
  export function assetPath(deps, projectId, id) {
189
- return z
190
- .object({ path: z.string() })
191
- .parse(deps.db
189
+ return z.string().parse(assetPathOrUndefined(deps, projectId, id));
190
+ }
191
+ // The same, or undefined when the project has no such asset.
192
+ export function assetPathOrUndefined(deps, projectId, id) {
193
+ const path = deps.db
192
194
  .prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
193
- .get(projectId, id)).path;
195
+ .get(projectId, id)?.path;
196
+ return typeof path === "string" ? path : undefined;
194
197
  }
195
198
  // A thumbnail step that draws its thumbnails, so each can be replaced by a file of one's own.
196
199
  export function generatesThumbnail(config) {
@@ -6,6 +6,7 @@ import { usesVoices } from "../voices/model.js";
6
6
  import { cueSpeaker } from "../voices/timing.js";
7
7
  import { assetPath, measureAudio } from "./mutation-assets.js";
8
8
  import { validateCues } from "./rules.js";
9
+ import { readyOutput } from "./view.js";
9
10
  // Edited captions of a multi-voice run keep their speakers. A cue that comes back without one
10
11
  // (the caption editor edits text and times only) takes the speaker it had before, found by the
11
12
  // cue's id, and a new cue the speaker of the narration under it, from the word timing. A cue
@@ -22,11 +23,7 @@ export function withCueSpeakers(deps, base, edit) {
22
23
  const timing = () => {
23
24
  if (words !== undefined)
24
25
  return words;
25
- const row = base.outputs.find((one) => one.selected &&
26
- one.available &&
27
- one.state === "ready" &&
28
- one.workKey === "subtitles:timing" &&
29
- one.output.role === "subtitle_words");
26
+ const row = readyOutput(base, { workKey: "subtitles:timing", role: "subtitle_words" });
30
27
  try {
31
28
  words =
32
29
  row === undefined
@@ -26,7 +26,6 @@ import { bindNarrationSources, validateNarrationIntent, validateTemplateIntent }
26
26
  import { revisionEditSchema } from "./schema.js";
27
27
  import { keptSubject } from "./subject.js";
28
28
  import { withEarlierJoins } from "./view.js";
29
- export { bindUpload } from "./mutation-assets.js";
30
29
  export { restoreRevision } from "./restore.js";
31
30
  export async function saveRevision(deps, input) {
32
31
  const parsed = revisionEditSchema.safeParse(input.edit);
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { agentRevisionIds } from "../agents/repo.js";
2
+ import { agentRevisionIds, assistantRevisionIds } from "../agents/repo.js";
3
3
  import { projectRevisionSchema } from "./schema.js";
4
4
  export { insertManifestOutput, insertManifestPiece, outputsForRevision, piecesForRevision, selectOutputRecord, selectPieceRecord, } from "./manifest-repo.js";
5
5
  const revisionRow = z.object({
@@ -39,6 +39,7 @@ export function currentRevisionId(db, projectId) {
39
39
  export function listRevisionHistory(db, projectId) {
40
40
  const head = currentRevisionId(db, projectId);
41
41
  const byAgent = agentRevisionIds(db, projectId);
42
+ const byAssistant = assistantRevisionIds(db, projectId);
42
43
  return db
43
44
  .prepare("SELECT * FROM project_revisions WHERE project_id=? ORDER BY created_at DESC,id DESC")
44
45
  .all(projectId)
@@ -52,6 +53,7 @@ export function listRevisionHistory(db, projectId) {
52
53
  createdAt: revision.createdAt,
53
54
  current: revision.id === head,
54
55
  ...(byAgent.has(revision.id) ? { byAgent: true } : {}),
56
+ ...(byAssistant.has(revision.id) ? { byAssistant: true } : {}),
55
57
  };
56
58
  });
57
59
  }
@@ -221,6 +221,7 @@ export const revisionSummarySchema = z.object({
221
221
  createdAt: z.string(),
222
222
  current: z.boolean(),
223
223
  byAgent: z.boolean().optional(),
224
+ byAssistant: z.boolean().optional(),
224
225
  });
225
226
  export const revisionMutationSuccessSchema = z.object({
226
227
  ok: z.literal(true),