@gentbajko/slopify 3.25.9 → 4.1.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 (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -1,45 +1,37 @@
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";
3
+ import { editSettings } from "../../kernel/ports/settings-places.js";
4
4
  import { assembleDescription, checkDescriptionAnswer, descriptionMessages, tagsText, } from "../youtube/answer.js";
5
5
  import { defaultDescriptionPrompt } from "../youtube/model.js";
6
6
  import { keepsShape, titleShape } from "../youtube/titles.js";
7
7
  import { transcriptPassages, transcriptText } from "../youtube/transcript.js";
8
8
  import { exportSnapshot, revisionAudio } from "./runtime-export-inputs.js";
9
- import { savedCatalogue } from "./runtime-plan.js";
9
+ import { workLlm } from "./runtime-plan.js";
10
10
  import { preparedTexts, publishResult } from "./runtime-publication.js";
11
- import { wordsSchema } from "./runtime-subtitles.js";
11
+ import { readTimingWords } from "./runtime-subtitles.js";
12
+ import { pieceAlreadyDone } from "./work-records.js";
12
13
  // Writes the YouTube description, tags, pinned comment and other titles from the saved word timing: the transcript's
13
14
  // passages carry their start in the final video, so the chapters the model picks are real
14
15
  // times. An answer that breaks YouTube's chapter or tag rules is a failed attempt, which the
15
16
  // provider wrapper asks again while attempts remain.
16
17
  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")
18
+ if (pieceAlreadyDone(deps.db, context.work.workId, piece.id))
20
19
  return "done";
21
20
  if (!context.maySubmit(piece.id))
22
21
  return "held";
23
22
  context.signal.throwIfAborted();
24
23
  const input = piece.input;
25
24
  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.");
25
+ throw new Error(internalError("the YouTube description step was set up wrongly"));
27
26
  const { view } = exportSnapshot(deps, context, piece);
28
27
  const config = view.revision.config;
29
28
  const llm = config.llm;
30
29
  if (llm === undefined || llm.provider.trim() === "" || llm.model.trim() === "")
31
- 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
+ throw new Error(`The YouTube description needs an AI model, and none is chosen. Choose a Service and a Model in ${editSettings.writer}, then Try again.`);
31
+ const { words } = readTimingWords(deps, context, view, "the YouTube description's chapters come from");
40
32
  const passages = transcriptPassages(words);
41
33
  if (passages.length === 0)
42
- 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.");
34
+ throw new Error("The narration's word timing holds no words, so there is nothing to write the YouTube description from. Press Make the video again in the project's Video section, then Try again.");
43
35
  // The same timeline the timing walked: silence at the start, intro, gaps, body, outro and
44
36
  // silence at the end.
45
37
  const audio = await revisionAudio(deps, context, view);
@@ -56,25 +48,16 @@ export async function executeYoutubeRecipe(deps, context, providers, piece) {
56
48
  durationSeconds,
57
49
  transcript: transcriptText(passages),
58
50
  }), 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);
51
+ const call = workLlm(deps.db, context.work.workId, llm, piece.key);
63
52
  if (!context.maySubmit(piece.id))
64
53
  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),
54
+ const answer = await providers.forPiece(piece.id).llm(call({
70
55
  messages,
71
- webSearch: false,
72
- previewLabel: piece.key,
73
56
  check: (value) => {
74
57
  const checked = checkDescriptionAnswer(value.text, durationSeconds, config.title, shape);
75
58
  return checked.ok ? undefined : checked.reason;
76
59
  },
77
- });
60
+ }));
78
61
  if (!answer.ok)
79
62
  return "held";
80
63
  const checked = checkDescriptionAnswer(answer.value.text, durationSeconds, config.title);
@@ -103,6 +86,6 @@ export async function executeYoutubeRecipe(deps, context, providers, piece) {
103
86
  tokensIn: answer.value.usage?.inputTokens ?? 0,
104
87
  tokensOut: answer.value.usage?.outputTokens ?? 0,
105
88
  descriptions: 1,
106
- });
89
+ }, context.work.projectId);
107
90
  return "done";
108
91
  }
@@ -33,9 +33,14 @@ export async function checkReadiness(deps, snapshot, view) {
33
33
  if (provider === undefined || !readinessIsUsable(provider.readiness)) {
34
34
  fields.push({
35
35
  field: choice.family,
36
+ // Said under the step's name on the project page ("Images: …"), in the person's words:
37
+ // a service Slopify does not offer here (a sample's own) is changed in the project's
38
+ // settings, one it offers is set up under Keys & services.
36
39
  message: provider !== undefined && provider.readiness.kind !== "keyed" && provider.readiness.issue
37
40
  ? provider.readiness.issue
38
- : "Configure this provider before rebuilding.",
41
+ : provider === undefined
42
+ ? "This project's service isn't one Slopify can use here. Choose another in Edit settings, then try again."
43
+ : "Its service isn't set up yet. Add it in Settings → Keys & services, then try again.",
39
44
  });
40
45
  continue;
41
46
  }
@@ -72,7 +77,10 @@ export function localReadiness(deps, snapshot, view, providers, catalogue) {
72
77
  message: "The configured CLI command changed. Check readiness again before rebuilding.",
73
78
  });
74
79
  if (provider?.readiness.kind === "keyed" && !hasKey(deps.db, provider.id))
75
- fields.push({ field: choice.family, message: "Save the provider key before rebuilding." });
80
+ fields.push({
81
+ field: choice.family,
82
+ message: "Its key isn't saved yet. Add it in Settings → Keys & services, then try again.",
83
+ });
76
84
  if (choice.voice !== undefined &&
77
85
  !listVoices(deps.db).some((row) => row.provider === choice.provider && row.voiceId === choice.voice))
78
86
  fields.push({ field: "audio.voice", message: "Choose a saved voice before rebuilding." });
@@ -88,7 +96,7 @@ export function localReadiness(deps, snapshot, view, providers, catalogue) {
88
96
  if (maximum !== undefined && input.text.length > maximum)
89
97
  fields.push({
90
98
  field: `work.${recipe.key}.text`,
91
- message: "This saved physical request exceeds the current model limit. Regenerate this narration group to plan new parts.",
99
+ message: "This saved request is longer than the voice model takes now. Make this part of the narration again, so it is split into parts that fit.",
92
100
  });
93
101
  }
94
102
  else if (input.kind === "llm") {
@@ -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,4 +1,5 @@
1
1
  import { documentIndex } from "../../kernel/ports/llm-documents.js";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
2
3
  import { researchDocuments } from "./documents.js";
3
4
  import { briefText } from "./planner.js";
4
5
  // An editorial pass over every sub-agent's output that selects and organizes the findings; it
@@ -41,9 +42,9 @@ export function endsWithSources(text) {
41
42
  // but cannot be used is a failed attempt, so the wrapper retries it.
42
43
  export function sourcedAnswer(who, text) {
43
44
  if (text.trim() === "") {
44
- return `The AI model returned an empty answer for ${who}. Try again; if it keeps happening, choose a different model in Edit project → Providers.`;
45
+ return `The AI model returned an empty answer for ${who}. Try again; if it keeps happening, choose a different model in ${editSettings.writer}.`;
45
46
  }
46
47
  return endsWithSources(text)
47
48
  ? undefined
48
- : `The AI model's answer for ${who} had no sources. Research needs web sources: Try again, or choose a model with web search in Edit project → Providers.`;
49
+ : `The AI model's answer for ${who} had no sources. Research needs web sources: Try again, or choose a model with web search in ${editSettings.writer}.`;
49
50
  }
@@ -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;
@@ -17,5 +20,5 @@ export const closedReviewSentences = {
17
20
  superseded: "This review was replaced: its item was made again or edited, and the new one waits for its own verdict. Call needs_attention to see it.",
18
21
  "replaced-pending": "This review was replaced: its item was made again or edited, so it takes no verdict, and no new review waits for it yet. One opens once the new item is made; an edit saved but not continued makes none until the run is continued. There is nothing to answer here: call needs_attention to see what is waiting now.",
19
22
  canceled: "This review's run was canceled, so nothing waits on it and it takes no verdict: a canceled run's reviews never pass. Call needs_attention to see what is waiting now.",
20
- trashed: "This review's project is in the Trash, so nothing waits on it and it takes no verdict. Restore the project in Settings → Backup & storage → Trash; the review then waits for its verdict again.",
23
+ trashed: "This review's project is in the Trash, so nothing waits on it and it takes no verdict. Restore the project in Settings → Trash; the review then waits for its verdict again.",
21
24
  };
@@ -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.
@@ -4,7 +4,7 @@ import { pendingAdmitted } from "../admission/admitted-work.js";
4
4
  import { liveProject, oneProject, stageStandingsByProject } from "../admission/repo.js";
5
5
  import { checkpointStageSchema } from "../checkpoints/schema.js";
6
6
  import { heldForItsDay } from "../schedules/prepare.js";
7
- import { uploadedAndSettled } from "../uploads/repo.js";
7
+ import { settledProjects } from "../uploads/repo.js";
8
8
  import { projectsWaitingOnAgents } from "./external-waiting.js";
9
9
  const gateRow = z.object({
10
10
  project_id: z.string(),
@@ -18,7 +18,7 @@ const gateRow = z.object({
18
18
  const projectRow = z.object({ project_id: z.string() });
19
19
  // Every project's holds, or one project's alone when `projectId` is given. A caller that has
20
20
  // already read the stage standings (`stageStandingsByProject`) or the uploaded projects that
21
- // read Done (`uploadedAndSettled`) passes them; one that lists what it can read passes `skip`,
21
+ // read Done (`settledProjects`) passes them; one that lists what it can read passes `skip`,
22
22
  // given each checkpoint row that can't be read instead of failing them all.
23
23
  export function projectHolds(db, scope = {}) {
24
24
  const { projectId, skip } = scope;
@@ -74,8 +74,9 @@ export function projectHolds(db, scope = {}) {
74
74
  .map((row) => projectRow.parse(row).project_id);
75
75
  const agents = projectsWaitingOnAgents(db, projectId);
76
76
  const runs = heldRuns(db, projectId);
77
- // An uploaded project with nothing admitted reads Done (`edge/http/projects.ts`).
78
- const settled = scope.settled ?? uploadedAndSettled(db, projectId);
77
+ // An uploaded project with nothing admitted, or a finished one an edit left only outdated,
78
+ // reads Done (`edge/http/projects.ts`).
79
+ const settled = scope.settled ?? settledProjects(db, projectId);
79
80
  const waitingForAgent = new Set([...agents].filter((id) => !reachedTheirs.has(id) && runs.get(id)?.held !== true));
80
81
  const stopped = new Set([...runs]
81
82
  .filter(([, run]) => run.stuck)
@@ -1,8 +1,28 @@
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: the narration is
17
+ // made before the images (`kernel/pipeline.ts`), as the pause ticks say.
18
+ export const reviewStagesInRunOrder = [
19
+ "article",
20
+ "promise",
21
+ "narration",
22
+ "images",
23
+ "thumbnail",
24
+ "shorts",
25
+ ];
6
26
  // Off is the same as the stage having no entry, which is what every project saved before
7
27
  // reviews existed has.
8
28
  export const reviewModes = ["off", "flag", "redo"];
@@ -11,9 +31,11 @@ export const reviewers = ["model", "agent"];
11
31
  // Who gave a verdict: the reviewer model, an external agent, the person (through the verdict
12
32
  // route), or nobody when the review passed at its deadline.
13
33
  export const reviewAnswerers = ["model", "agent", "person", "nobody"];
34
+ // Said to a person who never heard of MCP: a model Slopify pays per check, or an AI agent they
35
+ // connected themselves (Settings → AI agents), which answers for free.
14
36
  export const reviewerLabels = {
15
- model: "Model",
16
- agent: "External agent (MCP)",
37
+ model: "An AI model",
38
+ agent: "Your own AI agent",
17
39
  };
18
40
  // ceiling: the owner's numbers. Two redos is what a person would try before looking at it
19
41
  // themselves; five is already six paid images for one slot.
@@ -26,6 +48,7 @@ export const reviewStageLabels = {
26
48
  narration: "Narration",
27
49
  thumbnail: "Thumbnail",
28
50
  shorts: "Shorts",
51
+ promise: "Promise check (before narration)",
29
52
  };
30
53
  export const reviewModeLabels = {
31
54
  off: "Off",
@@ -47,9 +70,6 @@ export function reviewRetriesOf(settings) {
47
70
  export function reviewedByAgent(settings) {
48
71
  return settings?.reviewer === "agent";
49
72
  }
50
- export function reviewedStages(settings) {
51
- return reviewStages.filter((stage) => reviewModeOf(settings, stage) !== "off");
52
- }
53
73
  // The key a stage's picked Review prompt renders under in `config.rendered`.
54
74
  export function reviewPromptKey(stage) {
55
75
  return `review.${stage}`;
@@ -57,12 +77,12 @@ export function reviewPromptKey(stage) {
57
77
  // A review's step is `review:` and the key of the item it reviews. The narration is
58
78
  // reviewed as a whole, so its item key names no step.
59
79
  export const narrationItemKey = "narration";
80
+ // The promise check reviews the article, but as its own item, so its verdict never stands in
81
+ // for the article review's.
82
+ export const promiseItemKey = "promise";
60
83
  export function reviewKey(itemKey) {
61
84
  return `review:${itemKey}`;
62
85
  }
63
- export function reviewedItemKey(key) {
64
- return key.startsWith("review:") ? key.slice("review:".length) : undefined;
65
- }
66
86
  export function reviewSettingsForm(settings) {
67
87
  return {
68
88
  provider: settings.provider,
@@ -106,7 +126,7 @@ export function reviewSettingsFromForm(form, makes) {
106
126
  ...(valid
107
127
  ? {}
108
128
  : {
109
- retriesProblem: `Enter a number of redos between ${String(reviewRetriesMin)} and ${String(reviewRetriesMax)} in the Reviews section.`,
129
+ retriesProblem: `Enter a number of redos between ${String(reviewRetriesMin)} and ${String(reviewRetriesMax)} under Pauses and checks → Checker and stages.`,
110
130
  }),
111
131
  };
112
132
  }
@@ -116,4 +136,5 @@ export const defaultReviewPrompts = {
116
136
  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
137
  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
138
  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.",
139
+ 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
140
  };
@@ -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";
@@ -43,14 +44,14 @@ export function reviewFields(draft) {
43
44
  if (!byAgent && (reviews.provider.trim() === "" || reviews.model.trim() === ""))
44
45
  fields.push({
45
46
  field: "reviews.provider",
46
- message: "Automatic reviews need a reviewer model. Choose one in the Reviews section, or set every review to Off.",
47
+ message: "AI checks need a model to check with. Choose one under Pauses and checks → Checker and stages → Checked by, or set every check to Off.",
47
48
  });
48
49
  const retries = reviews.retries;
49
50
  if (retries !== undefined &&
50
51
  (!Number.isInteger(retries) || retries < reviewRetriesMin || retries > reviewRetriesMax))
51
52
  fields.push({
52
53
  field: "reviews.retries",
53
- message: `Enter a number of redos between ${reviewRetriesMin} and ${reviewRetriesMax} in the Reviews section.`,
54
+ message: `Enter a number of redos between ${reviewRetriesMin} and ${reviewRetriesMax} under Pauses and checks → Checker and stages.`,
54
55
  });
55
56
  const pictures = active.filter((stage) => visionStages.includes(stage));
56
57
  if (!byAgent &&
@@ -59,7 +60,7 @@ export function reviewFields(draft) {
59
60
  !reviewerSeesImages(reviews.provider))
60
61
  fields.push({
61
62
  field: "reviews.provider",
62
- message: `The ${pictures.map((stage) => reviewStageLabels[stage]).join(", ")} ${pictures.length === 1 ? "review looks" : "reviews look"} at pictures, and Slopify can't show pictures to this reviewer. In the Reviews section, choose Claude Code or Codex as the reviewer, or set ${pictures.length === 1 ? "that review" : "those reviews"} to Off.`,
63
+ message: `The ${pictures.map((stage) => reviewStageLabels[stage]).join(", ")} ${pictures.length === 1 ? "check looks" : "checks look"} at images, and Slopify can't show images to this checker. Under Pauses and checks → Checker and stages → Checked by, choose Claude Code or Codex, or set ${pictures.length === 1 ? "that check" : "those checks"} to Off.`,
63
64
  });
64
65
  return fields;
65
66
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { editSettings } from "../../kernel/ports/settings-places.js";
2
3
  // The whole answer the reviewer may give: one JSON object, nothing else. A failed item names
3
4
  // at least one reason, since a reason is what the person reads beside it.
4
5
  export const reviewReasonMax = 500;
@@ -46,7 +47,7 @@ export function reviewMessages(material) {
46
47
  },
47
48
  ];
48
49
  }
49
- const retryHint = "Slopify asks again while attempts remain; if it keeps happening, choose another reviewer model in Edit project → Reviews, then use Try again.";
50
+ const retryHint = `Slopify asks again while attempts remain; if it keeps happening, choose another reviewer model in ${editSettings.checks}, then use Try again.`;
50
51
  // Strict: one JSON object in the shape above, optionally inside a single ```json fence (the
51
52
  // one wrapping models add unasked). Anything else is refused with what was wrong with it.
52
53
  export function parseVerdict(text) {
@@ -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.
@@ -86,7 +86,7 @@ export function replanImagePrompts(input) {
86
86
  if (body === undefined) {
87
87
  fields.push({
88
88
  field: `imagePrompts.${String(index)}.name`,
89
- message: `The image prompt "${prompt.name}" is not in the Library any more. Untick it under Images, or add it back on Library → Prompts.`,
89
+ message: `The image prompt "${prompt.name}" is not in Prompts any more. Untick it under Images, or add it back in Prompts → Image.`,
90
90
  });
91
91
  continue;
92
92
  }
@@ -1,5 +1,6 @@
1
1
  import { statSync } from "node:fs";
2
2
  import { z } from "zod";
3
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
4
  import { referenceKey, thumbnailCountOf } from "../admission/model.js";
4
5
  import { usesReference } from "../admission/rules.js";
5
6
  import { outputPath, stagingPath } from "../storage/layout.js";
@@ -59,7 +60,7 @@ export function validateUploads(deps, edit) {
59
60
  if (to.kind === "shortsMusic" && edit.config.shorts?.enabled !== true)
60
61
  fields.push({
61
62
  field,
62
- message: "Background music is only used by the shorts. Turn Shorts on in Edit project → Shorts, then add the music.",
63
+ message: `Background music is only used by the shorts. Turn Shorts on in ${editSettings.shorts}, then add the music.`,
63
64
  });
64
65
  const destination = JSON.stringify(to);
65
66
  if (staged.has(upload.stagedFileId) || destinations.has(destination))
@@ -186,11 +187,14 @@ export async function measureAudio(deps, projectId, path) {
186
187
  return duration;
187
188
  }
188
189
  export function assetPath(deps, projectId, id) {
189
- return z
190
- .object({ path: z.string() })
191
- .parse(deps.db
190
+ return z.string().parse(assetPathOrUndefined(deps, projectId, id));
191
+ }
192
+ // The same, or undefined when the project has no such asset.
193
+ export function assetPathOrUndefined(deps, projectId, id) {
194
+ const path = deps.db
192
195
  .prepare("SELECT path FROM project_assets WHERE project_id=? AND id=?")
193
- .get(projectId, id)).path;
196
+ .get(projectId, id)?.path;
197
+ return typeof path === "string" ? path : undefined;
194
198
  }
195
199
  // A thumbnail step that draws its thumbnails, so each can be replaced by a file of one's own.
196
200
  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