@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
@@ -0,0 +1,198 @@
1
+ import { errorText } from "../../kernel/errors.js";
2
+ import { choiceSites } from "../model-upkeep/usage.js";
3
+ import { isUncataloguedProvider, providerById, providerIds, providers, } from "../settings/model.js";
4
+ import { readSetting, writeSetting } from "../settings/repo.js";
5
+ import { keyOfModel, weeklyAtKey, } from "./model.js";
6
+ import { accountOf, readAuto, readChecks } from "./repo.js";
7
+ import { newForYou, standingOf } from "./rules.js";
8
+ import { needsConfirmation, planChecks, runChecks, } from "./run.js";
9
+ // ceiling: the weekly look runs once seven days have passed since the last one.
10
+ export const weeklyIntervalMs = 7 * 24 * 60 * 60_000;
11
+ function familyOfSlot(slot) {
12
+ return slot === "llm" ? "llm" : slot === "audio" ? "tts" : "image";
13
+ }
14
+ export function chosenModels(db) {
15
+ const chosen = new Set();
16
+ for (const site of choiceSites(db))
17
+ for (const [slot, choice] of Object.entries(site.choices)) {
18
+ if (choice === undefined || choice.provider === "" || choice.model.trim() === "")
19
+ continue;
20
+ chosen.add(keyOfModel({ provider: choice.provider, family: familyOfSlot(slot), model: choice.model }));
21
+ }
22
+ return chosen;
23
+ }
24
+ export function createModelChecks(deps) {
25
+ const running = new Set();
26
+ const stopping = new AbortController();
27
+ const offered = async (provider) => {
28
+ const { family } = providerById(provider);
29
+ if (isUncataloguedProvider(provider)) {
30
+ const models = await deps.providerModels(provider, family).catch(() => []);
31
+ return models.map((model) => ({ provider, family, model: model.id }));
32
+ }
33
+ return deps
34
+ .catalogue()[family].filter((row) => row.provider === provider && row.enabled && !row.deprecated)
35
+ .map((row) => ({ provider, family, model: row.id }));
36
+ };
37
+ const withAccount = () => providerIds.filter((id) => accountOf(deps.db, id) !== undefined);
38
+ // Named models of a CLI are taken as named: asking the CLI for its list would start it, and
39
+ // a picker asks this for its one chosen model whenever it opens.
40
+ const offeredFor = (provider, models) => {
41
+ if (models === undefined || !isUncataloguedProvider(provider))
42
+ return offered(provider);
43
+ const { family } = providerById(provider);
44
+ return Promise.resolve(models.map((model) => ({ provider, family, model })));
45
+ };
46
+ const plan = async (scope) => {
47
+ const scoped = scope.provider === undefined ? withAccount() : [scope.provider];
48
+ return planChecks({
49
+ catalogue: deps.catalogue(),
50
+ rows: readChecks(deps.db),
51
+ offered: (await Promise.all(scoped.map((id) => offeredFor(id, scope.models)))).flat(),
52
+ accounts: (id) => accountOf(deps.db, id),
53
+ chosen: chosenModels(deps.db),
54
+ }, scope);
55
+ };
56
+ // A provider is taken before the first await, so two starts that arrive together can't both
57
+ // pass the running check and pay for the same checks twice.
58
+ const reserve = (ids) => {
59
+ let held = [...ids];
60
+ for (const id of held)
61
+ running.add(id);
62
+ const release = () => {
63
+ for (const id of held)
64
+ running.delete(id);
65
+ held = [];
66
+ };
67
+ // Once planned, only the providers it checks stay taken.
68
+ const keepFor = (checks) => {
69
+ const owned = held.filter((id) => checks.targets.some((t) => t.provider === id));
70
+ for (const id of held)
71
+ if (!owned.includes(id))
72
+ running.delete(id);
73
+ held = owned;
74
+ };
75
+ return { release, keepFor };
76
+ };
77
+ const launch = (checks, release) => runChecks({ db: deps.db, clock: deps.clock, log: deps.log, check: deps.check }, checks,
78
+ // A manual plan's estimate was confirmed in `start`; an automatic one is free or cheap.
79
+ { confirmedDollars: checks.totalDollars, signal: stopping.signal })
80
+ .then((answer) => answer.ok
81
+ ? answer.summary
82
+ : { checked: 0, works: 0, noAccess: 0, failed: 0, skipped: 0, spent: 0 })
83
+ .finally(release);
84
+ const start = async (scope, confirmedDollars) => {
85
+ const scoped = scope.provider === undefined ? withAccount() : [scope.provider];
86
+ const busy = scoped.filter((id) => running.has(id));
87
+ if (busy.length > 0)
88
+ return { ok: false, reason: "running", providers: busy };
89
+ const { release, keepFor } = reserve(scoped);
90
+ let checks;
91
+ try {
92
+ checks = await plan(scope);
93
+ }
94
+ catch (error) {
95
+ release();
96
+ throw error;
97
+ }
98
+ keepFor(checks);
99
+ if (scope.trigger === "manual" && needsConfirmation(checks, confirmedDollars)) {
100
+ release();
101
+ return { ok: false, reason: "estimate", plan: checks };
102
+ }
103
+ return { ok: true, plan: checks, done: launch(checks, release) };
104
+ };
105
+ const automatic = async (trigger, provider) => {
106
+ const scoped = provider === undefined ? withAccount() : [provider];
107
+ for (const id of scoped)
108
+ await deps.listing.refresh(id, true).catch((error) => {
109
+ deps.log.write("warn", "model-checks.listing", {
110
+ detail: `${id}: ${errorText(error)}`,
111
+ });
112
+ });
113
+ if (readAuto(deps.db) === "off")
114
+ return;
115
+ const free = scoped.filter((id) => !running.has(id));
116
+ if (free.length === 0)
117
+ return;
118
+ const { release, keepFor } = reserve(free);
119
+ let kept;
120
+ try {
121
+ const checks = await plan(provider === undefined ? { trigger } : { trigger, provider });
122
+ kept = {
123
+ ...checks,
124
+ targets: checks.targets.filter((t) => free.some((id) => id === t.provider)),
125
+ };
126
+ keepFor(kept);
127
+ if (trigger === "weekly")
128
+ writeSetting(deps.db, weeklyAtKey, deps.clock.now().toISOString());
129
+ }
130
+ catch (error) {
131
+ release();
132
+ throw error;
133
+ }
134
+ if (kept.targets.length > 0)
135
+ await launch(kept, release);
136
+ else
137
+ release();
138
+ };
139
+ return {
140
+ offered,
141
+ plan,
142
+ start,
143
+ automatic,
144
+ weeklyDue: () => {
145
+ if (readAuto(deps.db) === "off")
146
+ return false;
147
+ const last = readSetting(deps.db, weeklyAtKey);
148
+ return (last === undefined || deps.clock.now().getTime() - Date.parse(last) >= weeklyIntervalMs);
149
+ },
150
+ standings: (provider, models) => {
151
+ const { family } = providerById(provider);
152
+ const account = accountOf(deps.db, provider);
153
+ const rows = new Map(readChecks(deps.db, provider)
154
+ .filter((row) => row.family === family)
155
+ .map((row) => [row.model, row]));
156
+ return Object.fromEntries(models.map((model) => [
157
+ model.id,
158
+ standingOf(rows.get(model.id), account, deps.retiring?.({ provider, family, model: model.id })),
159
+ ]));
160
+ },
161
+ overview: () => {
162
+ const rows = readChecks(deps.db);
163
+ const catalogue = deps.catalogue();
164
+ const nameOf = (key) => catalogue[key.family].find((m) => m.provider === key.provider && m.id === key.model)
165
+ ?.name ?? key.model;
166
+ const states = providers.map((provider) => {
167
+ const account = accountOf(deps.db, provider.id);
168
+ const own = rows.filter((row) => row.provider === provider.id && row.account === account);
169
+ const times = own.flatMap((row) => (row.checkedAt === null ? [] : [row.checkedAt])).sort();
170
+ return {
171
+ provider: provider.id,
172
+ family: provider.family,
173
+ running: running.has(provider.id),
174
+ lastCheckedAt: times.at(-1) ?? null,
175
+ spent: own.reduce((sum, row) => sum + row.cost, 0),
176
+ works: own.filter((row) => row.verdict === "works").length,
177
+ noAccess: own.filter((row) => row.verdict === "no-access").length,
178
+ listed: own.filter((row) => row.listed === true).length,
179
+ };
180
+ });
181
+ const current = rows.filter((row) => providerIds.includes(row.provider) &&
182
+ row.account === accountOf(deps.db, row.provider));
183
+ return {
184
+ auto: readAuto(deps.db),
185
+ providers: states,
186
+ newForYou: newForYou(current).map((row) => ({
187
+ provider: row.provider,
188
+ family: row.family,
189
+ model: row.model,
190
+ name: nameOf(row),
191
+ firstWorksAt: row.firstWorksAt ?? "",
192
+ })),
193
+ };
194
+ },
195
+ running: () => [...running],
196
+ stop: () => stopping.abort(),
197
+ };
198
+ }
@@ -3,7 +3,7 @@
3
3
  export const usageSlots = ["llm", "audio", "images", "animate"];
4
4
  export const usageKinds = ["template", "schedule", "draft", "project"];
5
5
  export const slotLabels = {
6
- llm: "Text model",
6
+ llm: "Writing model",
7
7
  audio: "Voice model",
8
8
  images: "Image model",
9
9
  animate: "Animation model",
@@ -6,8 +6,10 @@ import { templateById } from "../project-templates/repo.js";
6
6
  import { updateTemplate } from "../project-templates/service.js";
7
7
  import { ensureBaseline, saveRevision } from "../revisions/index.js";
8
8
  import { currentRevisionId } from "../revisions/repo.js";
9
+ import { carryGateOnto } from "../template-studio/repo.js";
10
+ import { templateGate } from "../template-studio/schedule-gate.js";
9
11
  import { configChoices, formChoices, retiredModelUsage, retiredStatus, } from "./usage.js";
10
- function keepThinking(catalogue, provider, model, thinking) {
12
+ export function keepThinking(catalogue, provider, model, thinking) {
11
13
  if (thinking === undefined)
12
14
  return {};
13
15
  const row = catalogue.llm.find((entry) => entry.provider === provider && entry.id === model);
@@ -92,7 +94,7 @@ export async function switchRetiredModel(deps, catalogue, request) {
92
94
  if (row.version !== row.head)
93
95
  return {
94
96
  ok: false,
95
- message: "This schedule runs an older version of its template. In Library → Schedules, choose Edit, pick the template again so it runs the latest version, then save.",
97
+ message: "This schedule runs an older version of its template. In Calendar → Schedules, choose Edit, pick the template again so it runs the latest version, then save.",
96
98
  };
97
99
  return switchTemplate(deps, catalogue, request, String(row.templateId));
98
100
  }
@@ -103,32 +105,75 @@ export async function switchRetiredModel(deps, catalogue, request) {
103
105
  }
104
106
  }
105
107
  function switchTemplate(deps, catalogue, request, templateId) {
106
- return transact(deps.db, () => {
107
- const template = templateById(deps.db, templateId);
108
- if (template === undefined)
109
- return { ok: false, message: changedMeanwhile };
110
- const form = switchForm(template.document.form, catalogue, request);
111
- if (form === undefined)
112
- return { ok: true, changed: false };
113
- const saved = updateTemplate(deps, {
108
+ const template = templateById(deps.db, templateId);
109
+ if (template === undefined)
110
+ return { ok: false, message: changedMeanwhile };
111
+ try {
112
+ return transact(deps.db, () => switchTemplateIn(deps, catalogue, request, template));
113
+ }
114
+ catch (error) {
115
+ if (!(error instanceof SwitchRefused))
116
+ throw error;
117
+ return {
118
+ ok: false,
119
+ message: error.reason === "missing-prompt"
120
+ ? `The template "${template.name}" uses a prompt or intro/outro that was deleted, so it cannot be saved. Open it in Templates → Edit template, choose another, and save.`
121
+ : changedMeanwhile,
122
+ };
123
+ }
124
+ }
125
+ // A refused save inside the switch's transaction: thrown so the carried version written before
126
+ // it is rolled back with it.
127
+ class SwitchRefused extends Error {
128
+ reason;
129
+ constructor(reason) {
130
+ super(reason);
131
+ this.reason = reason;
132
+ }
133
+ }
134
+ function switchTemplateIn(deps, catalogue, request, template) {
135
+ const form = switchForm(template.document.form, catalogue, request);
136
+ // The version schedules run, when it isn't the head (an unchecked edit waits on Mark ready).
137
+ const runs = templateGate(deps.db, template.id)?.runs ?? null;
138
+ const running = runs === null || runs === template.version
139
+ ? undefined
140
+ : templateById(deps.db, template.id, runs);
141
+ const runningForm = running === undefined ? undefined : switchForm(running.document.form, catalogue, request);
142
+ if (form === undefined && runningForm === undefined)
143
+ return { ok: true, changed: false };
144
+ let base = template.version;
145
+ if (running !== undefined && runningForm !== undefined) {
146
+ // The version that runs, switched, carried from it: schedules never keep running a
147
+ // retired model while the person's newer edit waits to be marked ready.
148
+ const carried = updateTemplate(deps, {
114
149
  id: template.id,
115
- name: template.name,
116
- document: { ...template.document, form },
117
- baseVersion: template.version,
150
+ name: running.name,
151
+ document: { ...running.document, form: runningForm },
152
+ baseVersion: base,
118
153
  mutationId: deps.uuid(),
119
154
  });
120
- if (!saved.ok)
121
- return {
122
- ok: false,
123
- message: saved.reason === "missing-prompt"
124
- ? `The template "${template.name}" uses a prompt or intro/outro that was deleted, so it cannot be saved. Open it in Library → Templates, choose another, and save.`
125
- : changedMeanwhile,
126
- };
127
- deps.db
128
- .prepare("UPDATE schedules SET template_version=?,version=version+1,updated_at=? WHERE template_id=? AND template_version=? AND deleted_at IS NULL AND status IN ('active','paused')")
129
- .run(saved.value.version, deps.clock.now().toISOString(), template.id, template.version);
130
- return { ok: true, changed: true };
155
+ if (!carried.ok)
156
+ throw new SwitchRefused(carried.reason);
157
+ carryGateOnto(deps.db, template.id, running.version, carried.value.version);
158
+ base = carried.value.version;
159
+ }
160
+ // The head stays the person's newest work: switched too when it picks the model, and
161
+ // written again above the carried version so it is still the newest one.
162
+ const saved = updateTemplate(deps, {
163
+ id: template.id,
164
+ name: template.name,
165
+ document: { ...template.document, form: form ?? template.document.form },
166
+ baseVersion: base,
167
+ mutationId: deps.uuid(),
131
168
  });
169
+ if (!saved.ok)
170
+ throw new SwitchRefused(saved.reason);
171
+ // Slopify's own change: a schedule running the version it came from keeps running.
172
+ carryGateOnto(deps.db, template.id, template.version, saved.value.version);
173
+ deps.db
174
+ .prepare("UPDATE schedules SET template_version=?,version=version+1,updated_at=? WHERE template_id=? AND template_version=? AND deleted_at IS NULL AND status IN ('active','paused')")
175
+ .run(saved.value.version, deps.clock.now().toISOString(), template.id, template.version);
176
+ return { ok: true, changed: true };
132
177
  }
133
178
  function switchDraft(deps, catalogue, request) {
134
179
  const row = draftRow(deps.db, request.id);
@@ -185,16 +230,16 @@ async function switchProject(deps, catalogue, request) {
185
230
  ? " because the project changed meanwhile"
186
231
  : "fields" in saved && saved.fields?.[0] !== undefined
187
232
  ? ` (${saved.fields[0].message})`
188
- : ""}. Open the project, choose Edit project and change it under Providers.`,
233
+ : ""}. Open the project and change it in Edit settings.`,
189
234
  };
190
235
  }
191
236
  // "Switch all": every listed use that has a suggestion and no reason to hold back, one by one.
192
- export async function switchAllRetired(deps, catalogue) {
237
+ export async function switchAllRetired(deps, catalogue, lifecycle) {
193
238
  let switched = 0;
194
239
  const failed = [];
195
240
  // Templates first: switching one also moves the schedules that follow it.
196
241
  const order = ["template", "schedule", "draft", "project"];
197
- const usages = [...retiredModelUsage(deps.db, catalogue)].sort((a, b) => order.indexOf(a.kind) - order.indexOf(b.kind));
242
+ const usages = [...retiredModelUsage(deps.db, catalogue, lifecycle)].sort((a, b) => order.indexOf(a.kind) - order.indexOf(b.kind));
198
243
  for (const usage of usages) {
199
244
  if (usage.blocked !== null || usage.replacement === null)
200
245
  continue;
@@ -1,16 +1,29 @@
1
1
  import { z } from "zod";
2
+ import { lifecycleReplacement } from "../../catalog/lifecycle.js";
2
3
  import { isVideoModel } from "../../catalog/schema.js";
3
4
  import { listProjects, stagesOf } from "../admission/repo.js";
4
5
  import { draftRows } from "../play-drafts/repo.js";
5
6
  import { documentOf } from "../play-drafts/service.js";
6
7
  import { templateById } from "../project-templates/repo.js";
7
8
  import { currentRevisionId, revisionById } from "../revisions/repo.js";
8
- import { isUncataloguedProvider } from "../settings/model.js";
9
+ import { isSampleStandIn, isUncataloguedProvider } from "../settings/model.js";
10
+ import { runnableTemplate } from "../template-studio/schedule-gate.js";
9
11
  import { usageSlots } from "./model.js";
10
12
  export { slotLabels, usageKinds, usageSlots } from "./model.js";
11
13
  function familyOf(slot) {
12
14
  return slot === "llm" ? "llm" : slot === "audio" ? "tts" : "image";
13
15
  }
16
+ // A Create draft's name as Create's header says it: its title once every keyword in it is
17
+ // filled, else "Untitled draft". Never a raw {{Topic}}.
18
+ function draftName(title, values) {
19
+ const filled = title
20
+ .replace(/\{\{\s*([^{}]+?)\s*\}\}/g, (keyword, name) => {
21
+ const value = values[name]?.trim() ?? "";
22
+ return value === "" ? keyword : value;
23
+ })
24
+ .trim();
25
+ return filled === "" || filled.includes("{{") ? "Untitled draft" : filled;
26
+ }
14
27
  // The model choices a Play form (a draft or a template) makes.
15
28
  export function formChoices(form) {
16
29
  const edit = form.videoEdit;
@@ -36,11 +49,13 @@ export function configChoices(config) {
36
49
  };
37
50
  }
38
51
  // Whether a choice names a catalogued model that is no longer offered. Command-line tools
39
- // list their own models and are checked by the health check instead.
52
+ // list their own models and are checked by the health check instead; the samples' stand-ins
53
+ // were never catalogued.
40
54
  export function retiredStatus(catalogue, slot, choice) {
41
55
  if (choice.provider === "" ||
42
56
  choice.model.trim() === "" ||
43
- isUncataloguedProvider(choice.provider))
57
+ isUncataloguedProvider(choice.provider) ||
58
+ isSampleStandIn(choice.provider))
44
59
  return null;
45
60
  const row = catalogue[familyOf(slot)].find((model) => model.provider === choice.provider &&
46
61
  model.id === choice.model &&
@@ -51,9 +66,17 @@ export function retiredStatus(catalogue, slot, choice) {
51
66
  return "retired";
52
67
  return row.enabled ? null : "unlisted";
53
68
  }
54
- // The closest active model from the same provider and list: the one sharing the longest
55
- // start of its ID (a newer version of the same line), the catalogue's order breaking ties.
56
- export function suggestReplacement(catalogue, slot, choice) {
69
+ // The replacement models-lifecycle.yaml names, when it is live; else the closest active model
70
+ // from the same provider and list: the one sharing the longest start of its ID (a newer
71
+ // version of the same line), the catalogue's order breaking ties.
72
+ export function suggestReplacement(catalogue, slot, choice, lifecycle) {
73
+ const named = lifecycleReplacement(lifecycle, catalogue, {
74
+ family: familyOf(slot),
75
+ provider: choice.provider,
76
+ model: choice.model,
77
+ });
78
+ if (named !== undefined && named.id !== choice.model)
79
+ return named;
57
80
  let best = null;
58
81
  for (const row of catalogue[familyOf(slot)]) {
59
82
  if (row.provider !== choice.provider ||
@@ -74,7 +97,7 @@ function sharedStart(a, b) {
74
97
  index++;
75
98
  return index;
76
99
  }
77
- function usagesOf(catalogue, base, choices, blocked) {
100
+ function usagesOf(catalogue, base, choices, blocked, lifecycle) {
78
101
  const found = [];
79
102
  for (const slot of usageSlots) {
80
103
  const choice = choices[slot];
@@ -83,7 +106,7 @@ function usagesOf(catalogue, base, choices, blocked) {
83
106
  const why = retiredStatus(catalogue, slot, choice);
84
107
  if (why === null)
85
108
  continue;
86
- const replacement = suggestReplacement(catalogue, slot, choice);
109
+ const replacement = suggestReplacement(catalogue, slot, choice, lifecycle);
87
110
  found.push({
88
111
  ...base,
89
112
  key: `${base.kind}:${base.id}:${slot}`,
@@ -128,7 +151,10 @@ export function choiceSites(db) {
128
151
  .prepare("SELECT id,name,template_id,template_version FROM schedules WHERE deleted_at IS NULL AND status IN ('active','paused') ORDER BY lower(name),id")
129
152
  .all()) {
130
153
  const row = scheduleRow.parse(raw);
131
- const template = templateById(db, row.template_id, row.template_version);
154
+ // What the schedule actually runs (`template-studio/schedule-gate.ts`): with an unchecked
155
+ // head that is an older ready version, whose models are the ones its runs use.
156
+ const template = runnableTemplate(db, row.template_id) ??
157
+ templateById(db, row.template_id, row.template_version);
132
158
  if (template === undefined)
133
159
  continue;
134
160
  const head = heads.get(row.template_id) ??
@@ -140,7 +166,7 @@ export function choiceSites(db) {
140
166
  choices: formChoices(template.document.form),
141
167
  blocked: head === row.template_version
142
168
  ? null
143
- : `This schedule runs an older version of the template "${template.name}". In Library → Schedules, choose Edit, pick the template again so it runs the latest version, then save.`,
169
+ : `This schedule runs an older version of the template "${template.name}". In Calendar → Schedules, choose Edit, pick the template again so it runs the latest version, then save.`,
144
170
  });
145
171
  }
146
172
  for (const row of draftRows(db)) {
@@ -152,7 +178,7 @@ export function choiceSites(db) {
152
178
  sites.push({
153
179
  kind: "draft",
154
180
  id: row.id,
155
- name: row.title.trim() === "" ? "Untitled draft" : row.title,
181
+ name: draftName(row.title, document.form.values),
156
182
  choices: formChoices(document.form),
157
183
  blocked: null,
158
184
  });
@@ -180,6 +206,6 @@ export function choiceSites(db) {
180
206
  }
181
207
  // Every template, schedule, draft and unfinished project that still picks a retired or
182
208
  // unlisted model, with the suggested replacement. Nothing is changed here.
183
- export function retiredModelUsage(db, catalogue) {
184
- return choiceSites(db).flatMap((site) => usagesOf(catalogue, { kind: site.kind, id: site.id, name: site.name }, site.choices, site.blocked));
209
+ export function retiredModelUsage(db, catalogue, lifecycle) {
210
+ return choiceSites(db).flatMap((site) => usagesOf(catalogue, { kind: site.kind, id: site.id, name: site.name }, site.choices, site.blocked, lifecycle));
185
211
  }
@@ -1,6 +1,8 @@
1
1
  import { mkdirSync, rmSync } from "node:fs";
2
2
  import { open, stat } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
+ import { errorText } from "../../kernel/errors.js";
5
+ import { editSettings } from "../../kernel/ports/settings-places.js";
4
6
  import { runFfmpeg } from "../video/ffmpeg.js";
5
7
  import { breathRate, scanBreaths, softenChunk } from "./breaths-detect.js";
6
8
  // Soften breaths, made in the join (`breaths-model.ts` says why there). Each narration piece is
@@ -38,7 +40,7 @@ export async function softenBreaths(run, files, directory) {
38
40
  catch (error) {
39
41
  if (run.signal.aborted)
40
42
  throw error;
41
- throw new Error(`Soften breaths could not process narration piece ${String(index + 1)} (${error instanceof Error ? error.message : String(error)}), so the narration can't be joined together. Use Try again; if it fails again, turn off Soften breaths in Edit project → Pauses and volume and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
43
+ throw new Error(`Soften breaths could not process narration piece ${String(index + 1)} (${errorText(error)}), so the narration can't be joined together. Use Try again; if it fails again, turn off Soften breaths in ${editSettings.sound} and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
42
44
  }
43
45
  finally {
44
46
  rmSync(raw, { force: true });
@@ -1,5 +1,7 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { errorText } from "../../kernel/errors.js";
4
+ import { editSettings } from "../../kernel/ports/settings-places.js";
3
5
  import { runFfmpeg } from "../video/ffmpeg.js";
4
6
  import { softenBreaths } from "./breaths.js";
5
7
  import { deEsser } from "./breaths-model.js";
@@ -36,7 +38,7 @@ export async function deEssPieces(run, files, directory) {
36
38
  catch (error) {
37
39
  if (run.signal.aborted)
38
40
  throw error;
39
- throw new Error(`Soften S sounds could not process narration piece ${String(index + 1)} (${error instanceof Error ? error.message : String(error)}), so the narration can't be joined together. Use Try again; if it fails again, turn off Soften S sounds in Edit project → Pauses and volume and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
41
+ throw new Error(`Soften S sounds could not process narration piece ${String(index + 1)} (${errorText(error)}), so the narration can't be joined together. Use Try again; if it fails again, turn off Soften S sounds in ${editSettings.sound} and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
40
42
  }
41
43
  written.push(output);
42
44
  }
@@ -1,5 +1,6 @@
1
1
  import { copyFileSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { probeDurationMs, runFfmpeg } from "../video/ffmpeg.js";
4
5
  // The concat demuxer's script format. A path is single-quoted, and the one character that
5
6
  // can end the quoting is spelled out, so a folder with an apostrophe in it cannot become
@@ -46,7 +47,7 @@ export async function joinNarration(deps, input) {
46
47
  const first = input.files[0];
47
48
  if (first === undefined) {
48
49
  // The stage refuses an empty narration before it gets here, so this is a bug.
49
- throw new Error("Slopify hit an internal error (there is no narration audio to join). Try again; if it happens again, use Download diagnostics in Settings and report it.");
50
+ throw new Error(internalError("there is no narration audio to join"));
50
51
  }
51
52
  mkdirSync(dirname(input.output), { recursive: true, mode: 0o700 });
52
53
  if (input.files.length === 1 && input.reencode !== true) {
@@ -9,9 +9,10 @@
9
9
  // A paragraph's minimum is separate and 0 (the sentence minimum only) unless set: the pause
10
10
  // between paragraphs is mostly the narration prompt's and the voice's to make.
11
11
  //
12
- // Browser-safe: Play and Edit project read these limits. A run without either has neither on
12
+ // Browser-safe: Create and Edit settings read these limits. A run without either has neither on
13
13
  // its config, which is what every project saved before them was, and the joins keep their
14
14
  // fingerprints then.
15
+ import { createRows, editSettings } from "../../kernel/ports/settings-places.js";
15
16
  // ceiling: a quarter second reads brisk, 0.35 to 0.45 natural or documentary, 0.5 to 0.8 an
16
17
  // audiobook's relaxed pace, and above 0.8 sleep content; beyond 2 s it stops sounding like a
17
18
  // pause between sentences.
@@ -33,7 +34,7 @@ export function pauseProblem(value, name) {
33
34
  value <= pauseSecondsMax &&
34
35
  Math.abs(steps - Math.round(steps)) < 1e-6
35
36
  ? undefined
36
- : `The pause between ${name} must be between 0 and ${String(pauseSecondsMax)} seconds, in steps of ${String(pauseSecondsStep)}. Change it under Play → Video and style, or Edit project → Pauses and volume.`;
37
+ : `The pause between ${name} must be between 0 and ${String(pauseSecondsMax)} seconds, in steps of ${String(pauseSecondsStep)}. Change it under ${createRows.video}, or ${editSettings.sound}.`;
37
38
  }
38
39
  export function pauseFields(config) {
39
40
  const fields = [];
@@ -1,4 +1,5 @@
1
1
  import { execFile } from "node:child_process";
2
+ import { isDonePiece, readyOutput } from "../revisions/view.js";
2
3
  // The live view's narration waveform: each finished narration piece as a row of peaks, in the
3
4
  // order it is spoken, growing as pieces land. Once the whole narration is joined (or was
4
5
  // uploaded) that one file replaces the pieces.
@@ -12,7 +13,7 @@ const decodeMaxBytes = 3 * 3600 * decodeRate * 2;
12
13
  // The saved narration audio of a revision, in spoken order: the joined narration when it is
13
14
  // ready, else each finished request of the intro, the body and the outro.
14
15
  export function narrationAudio(view) {
15
- const joined = view.outputs.find((row) => row.selected && row.available && row.state === "ready" && row.output.role === "audio_body");
16
+ const joined = readyOutput(view, { role: "audio_body" });
16
17
  if (joined !== undefined)
17
18
  return {
18
19
  complete: true,
@@ -22,10 +23,8 @@ export function narrationAudio(view) {
22
23
  return {
23
24
  complete: false,
24
25
  pieces: view.pieces
25
- .filter((row) => row.selected &&
26
- row.available &&
26
+ .filter((row) => isDonePiece(row) &&
27
27
  row.stageKind === "audio" &&
28
- row.piece.state === "done" &&
29
28
  row.assetId !== null &&
30
29
  row.piece.payload !== null &&
31
30
  spoken(row.piece.payload))
@@ -1,3 +1,4 @@
1
+ import { internalError } from "../../kernel/errors.js";
1
2
  import { fingerprint } from "../../kernel/runner/work.js";
2
3
  import { anchoredChunks, defaultChunkCharacters, defaultChunkWords, } from "./chunk.js";
3
4
  import { pronunciationMatches } from "./pronunciation.js";
@@ -32,7 +33,7 @@ export function pronunciationChunks(source, chunking, entries, overriddenKeys =
32
33
  const chunks = narrationChunkTexts(source, chunking, anchors).map((text) => {
33
34
  const start = source.indexOf(text, offset);
34
35
  if (start < 0)
35
- throw new Error("Slopify hit an internal error (a narration chunk isn't in the article text). Try again; if it happens again, use Download diagnostics in Settings and report it.");
36
+ throw new Error(internalError("a narration chunk isn't in the article text"));
36
37
  offset = start + text.length;
37
38
  return { key: nextKey(text), text, start, end: offset };
38
39
  });
@@ -85,7 +86,7 @@ export function pronunciationChunks(source, chunking, entries, overriddenKeys =
85
86
  }
86
87
  const chunk = chunks[index];
87
88
  if (chunk === undefined)
88
- throw new Error("Slopify hit an internal error (a narration chunk went missing). Try again; if it happens again, use Download diagnostics in Settings and report it.");
89
+ throw new Error(internalError("a narration chunk went missing"));
89
90
  while ((matches[matchIndex]?.end ?? Infinity) <= chunk.end)
90
91
  matchIndex++;
91
92
  const match = matches[matchIndex];
@@ -97,7 +98,7 @@ export function pronunciationChunks(source, chunking, entries, overriddenKeys =
97
98
  continue;
98
99
  const first = chunks[from];
99
100
  if (first === undefined)
100
- throw new Error("Slopify hit an internal error (a group of narration chunks is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
101
+ throw new Error(internalError("a group of narration chunks is empty"));
101
102
  const text = source.slice(first.start, chunk.end);
102
103
  result.push(from === index
103
104
  ? { key: chunk.key, text }