@gentbajko/slopify 3.25.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -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);
@@ -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 Library → Templates, 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);
@@ -189,12 +234,12 @@ async function switchProject(deps, catalogue, request) {
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,11 +1,13 @@
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) {
@@ -36,11 +38,13 @@ export function configChoices(config) {
36
38
  };
37
39
  }
38
40
  // 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.
41
+ // list their own models and are checked by the health check instead; the samples' stand-ins
42
+ // were never catalogued.
40
43
  export function retiredStatus(catalogue, slot, choice) {
41
44
  if (choice.provider === "" ||
42
45
  choice.model.trim() === "" ||
43
- isUncataloguedProvider(choice.provider))
46
+ isUncataloguedProvider(choice.provider) ||
47
+ isSampleStandIn(choice.provider))
44
48
  return null;
45
49
  const row = catalogue[familyOf(slot)].find((model) => model.provider === choice.provider &&
46
50
  model.id === choice.model &&
@@ -51,9 +55,17 @@ export function retiredStatus(catalogue, slot, choice) {
51
55
  return "retired";
52
56
  return row.enabled ? null : "unlisted";
53
57
  }
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) {
58
+ // The replacement models-lifecycle.yaml names, when it is live; else the closest active model
59
+ // from the same provider and list: the one sharing the longest start of its ID (a newer
60
+ // version of the same line), the catalogue's order breaking ties.
61
+ export function suggestReplacement(catalogue, slot, choice, lifecycle) {
62
+ const named = lifecycleReplacement(lifecycle, catalogue, {
63
+ family: familyOf(slot),
64
+ provider: choice.provider,
65
+ model: choice.model,
66
+ });
67
+ if (named !== undefined && named.id !== choice.model)
68
+ return named;
57
69
  let best = null;
58
70
  for (const row of catalogue[familyOf(slot)]) {
59
71
  if (row.provider !== choice.provider ||
@@ -74,7 +86,7 @@ function sharedStart(a, b) {
74
86
  index++;
75
87
  return index;
76
88
  }
77
- function usagesOf(catalogue, base, choices, blocked) {
89
+ function usagesOf(catalogue, base, choices, blocked, lifecycle) {
78
90
  const found = [];
79
91
  for (const slot of usageSlots) {
80
92
  const choice = choices[slot];
@@ -83,7 +95,7 @@ function usagesOf(catalogue, base, choices, blocked) {
83
95
  const why = retiredStatus(catalogue, slot, choice);
84
96
  if (why === null)
85
97
  continue;
86
- const replacement = suggestReplacement(catalogue, slot, choice);
98
+ const replacement = suggestReplacement(catalogue, slot, choice, lifecycle);
87
99
  found.push({
88
100
  ...base,
89
101
  key: `${base.kind}:${base.id}:${slot}`,
@@ -128,7 +140,10 @@ export function choiceSites(db) {
128
140
  .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
141
  .all()) {
130
142
  const row = scheduleRow.parse(raw);
131
- const template = templateById(db, row.template_id, row.template_version);
143
+ // What the schedule actually runs (`template-studio/schedule-gate.ts`): with an unchecked
144
+ // head that is an older ready version, whose models are the ones its runs use.
145
+ const template = runnableTemplate(db, row.template_id) ??
146
+ templateById(db, row.template_id, row.template_version);
132
147
  if (template === undefined)
133
148
  continue;
134
149
  const head = heads.get(row.template_id) ??
@@ -180,6 +195,6 @@ export function choiceSites(db) {
180
195
  }
181
196
  // Every template, schedule, draft and unfinished project that still picks a retired or
182
197
  // 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));
198
+ export function retiredModelUsage(db, catalogue, lifecycle) {
199
+ return choiceSites(db).flatMap((site) => usagesOf(catalogue, { kind: site.kind, id: site.id, name: site.name }, site.choices, site.blocked, lifecycle));
185
200
  }
@@ -1,6 +1,7 @@
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";
4
5
  import { runFfmpeg } from "../video/ffmpeg.js";
5
6
  import { breathRate, scanBreaths, softenChunk } from "./breaths-detect.js";
6
7
  // Soften breaths, made in the join (`breaths-model.ts` says why there). Each narration piece is
@@ -38,7 +39,7 @@ export async function softenBreaths(run, files, directory) {
38
39
  catch (error) {
39
40
  if (run.signal.aborted)
40
41
  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 });
42
+ 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 Edit project → Pauses and volume and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
42
43
  }
43
44
  finally {
44
45
  rmSync(raw, { force: true });
@@ -1,5 +1,6 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { errorText } from "../../kernel/errors.js";
3
4
  import { runFfmpeg } from "../video/ffmpeg.js";
4
5
  import { softenBreaths } from "./breaths.js";
5
6
  import { deEsser } from "./breaths-model.js";
@@ -36,7 +37,7 @@ export async function deEssPieces(run, files, directory) {
36
37
  catch (error) {
37
38
  if (run.signal.aborted)
38
39
  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 });
40
+ 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 Edit project → Pauses and volume and use Save changes, then use Download diagnostics in Settings and report it.`, { cause: error });
40
41
  }
41
42
  written.push(output);
42
43
  }
@@ -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) {
@@ -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 }
@@ -1,5 +1,6 @@
1
1
  import { remark } from "remark";
2
2
  import remarkGfm from "remark-gfm";
3
+ import { internalError } from "../../kernel/errors.js";
3
4
  import { remembered } from "../../kernel/remembered.js";
4
5
  export const currentPronunciationRules = 2;
5
6
  export function pronunciationRulesOf(config) {
@@ -211,7 +212,7 @@ function matchPronunciations(source, entries, possessives = false) {
211
212
  continue;
212
213
  const entry = ordered.find((_, index) => match[index + 1] !== undefined);
213
214
  if (entry === undefined)
214
- throw new Error("Slopify hit an internal error (a pronunciation glossary term has no pronunciation). Try again; if it happens again, use Download diagnostics in Settings and report it.");
215
+ throw new Error(internalError("a pronunciation glossary term has no pronunciation"));
215
216
  const possessive = possessives ? match[ordered.length + 1] : undefined;
216
217
  matches.push({
217
218
  start: match.index,
@@ -237,7 +238,7 @@ export function pronunciationSpans(source, entries, possessives = false) {
237
238
  for (const [index, word] of words.entries()) {
238
239
  const ipa = match.entry.ipa[index];
239
240
  if (ipa === undefined)
240
- throw new Error("Slopify hit an internal error (a pronunciation glossary term is missing a word). Try again; if it happens again, use Download diagnostics in Settings and report it.");
241
+ throw new Error(internalError("a pronunciation glossary term is missing a word"));
241
242
  const start = match.start + word.index;
242
243
  const spoken = match.possessive !== undefined && index === words.length - 1 ? withPossessive(ipa) : ipa;
243
244
  spans.push({ start, end: start + word[0].length, text: `/${spoken}/` });
@@ -1,3 +1,4 @@
1
+ import { internalError } from "../../kernel/errors.js";
1
2
  import { sourceSentences } from "./preparation.js";
2
3
  const cannotFit = {
3
4
  ok: false,
@@ -9,14 +10,14 @@ function atoms(source, from, to, spans) {
9
10
  const span = spans.get(at);
10
11
  if (span !== undefined) {
11
12
  if (span.end > to || span.end <= at)
12
- throw new Error("Slopify hit an internal error (a pronunciation fix spans more than one word). Try again; if it happens again, use Download diagnostics in Settings and report it.");
13
+ throw new Error(internalError("a pronunciation fix spans more than one word"));
13
14
  result.push({ text: span.text, spokenText: source.slice(at, span.end) });
14
15
  at = span.end;
15
16
  }
16
17
  else {
17
18
  const point = source.codePointAt(at);
18
19
  if (point === undefined)
19
- throw new Error("Slopify hit an internal error (a narration position is outside the article text). Try again; if it happens again, use Download diagnostics in Settings and report it.");
20
+ throw new Error(internalError("a narration position is outside the article text"));
20
21
  const text = String.fromCodePoint(point);
21
22
  // An empty span is a later word of a multi-word alias (its spoken form sits on the first
22
23
  // word), so the whitespace in front of it would leave two spaces where one word was.
@@ -45,7 +46,7 @@ function checkedSpans(source, pronunciation) {
45
46
  span.end <= span.start ||
46
47
  span.end > source.length ||
47
48
  !/^[^\s\uD800-\uDFFF]+$/u.test(source.slice(span.start, span.end)))
48
- throw new Error("Slopify hit an internal error (pronunciation fixes overlap or split a word). Try again; if it happens again, use Download diagnostics in Settings and report it.");
49
+ throw new Error(internalError("pronunciation fixes overlap or split a word"));
49
50
  spans.set(span.start, span);
50
51
  end = span.end;
51
52
  }
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  import { redact } from "../../kernel/log.js";
2
3
  import { webhookBody } from "./address.js";
3
4
  import { autoPassNoticeText, noticeOf, noticeText, reviewNoticeText, runNotices, topicsNoticeText, } from "./rules.js";
@@ -84,7 +85,7 @@ export function createRunNotifier(deps) {
84
85
  .catch((error) => {
85
86
  deps.log.write("warn", "notification.failed", {
86
87
  ...about,
87
- detail: `${kind} notification not delivered: ${error instanceof Error ? error.message : String(error)}`,
88
+ detail: `${kind} notification not delivered: ${errorText(error)}`,
88
89
  });
89
90
  })
90
91
  .finally(() => {
@@ -123,7 +124,7 @@ export function createRunNotifier(deps) {
123
124
  catch (error) {
124
125
  deps.log.write("warn", "notification.failed", {
125
126
  projectId: event.projectId,
126
- detail: `notification skipped: ${error instanceof Error ? error.message : String(error)}`,
127
+ detail: `notification skipped: ${errorText(error)}`,
127
128
  });
128
129
  }
129
130
  },
@@ -138,7 +139,7 @@ export function createRunNotifier(deps) {
138
139
  }
139
140
  catch (error) {
140
141
  deps.log.write("warn", "notification.failed", {
141
- detail: `topics notification skipped: ${error instanceof Error ? error.message : String(error)}`,
142
+ detail: `topics notification skipped: ${errorText(error)}`,
142
143
  });
143
144
  }
144
145
  },
@@ -29,6 +29,7 @@ const reviewItemNames = {
29
29
  narration: "the narration",
30
30
  thumbnail: "the thumbnail",
31
31
  shorts: "a short",
32
+ promise: "the article's opening (promise check)",
32
33
  };
33
34
  export function reviewNoticeText(subject) {
34
35
  const title = subject.title.trim() === "" ? "Untitled project" : subject.title.trim();
@@ -48,6 +49,8 @@ export function reviewItemName(itemKey, imageOrder) {
48
49
  return "the article";
49
50
  if (itemKey === "narration")
50
51
  return "the narration";
52
+ if (itemKey === "promise")
53
+ return "the article's opening (promise check)";
51
54
  if (itemKey === "thumbnail:image")
52
55
  return "the thumbnail";
53
56
  const variant = /^thumbnail:image:(\d+)$/.exec(itemKey);
@@ -139,8 +142,8 @@ export function shortReason(reason) {
139
142
  }
140
143
  export function noticeText(kind, subject) {
141
144
  const text = runNoticeText(kind, subject);
142
- return subject.startedBy === "agent"
143
- ? { ...text, headline: `${text.headline} · started by agent` }
145
+ return subject.startedBy === "agent" || subject.startedBy === "assistant"
146
+ ? { ...text, headline: `${text.headline} · started by ${subject.startedBy}` }
144
147
  : text;
145
148
  }
146
149
  function runNoticeText(kind, subject) {
@@ -1,19 +1,11 @@
1
- import { readSetting, writeSetting } from "../settings/repo.js";
1
+ import { z } from "zod";
2
+ import { deleteSetting, readJsonSetting, writeSetting } from "../settings/repo.js";
2
3
  import { notificationUrlProblem } from "./address.js";
3
4
  // Its own row in the key/value `settings` table. Backups leave it out
4
5
  // (`slices/storage/portable.ts`): an ntfy topic in the URL is as good as a password.
5
6
  export const notificationUrlKey = "notificationUrl";
6
7
  export function readNotificationUrl(db) {
7
- const stored = readSetting(db, notificationUrlKey);
8
- if (stored === undefined)
9
- return null;
10
- let value;
11
- try {
12
- value = JSON.parse(stored);
13
- }
14
- catch {
15
- return null;
16
- }
8
+ const value = readJsonSetting(db, notificationUrlKey, z.unknown(), null);
17
9
  // A hand-edited row that no longer passes the rules is treated as not set rather than
18
10
  // POSTed to.
19
11
  return typeof value === "string" && value !== "" && notificationUrlProblem(value) === undefined
@@ -26,7 +18,7 @@ export function saveNotificationUrl(db, raw) {
26
18
  return { ok: false, message: problem };
27
19
  const url = raw.trim();
28
20
  if (url === "") {
29
- db.prepare("DELETE FROM settings WHERE key = ?").run(notificationUrlKey);
21
+ deleteSetting(db, notificationUrlKey);
30
22
  return { ok: true, url: null };
31
23
  }
32
24
  writeSetting(db, notificationUrlKey, JSON.stringify(url));
@@ -3,6 +3,7 @@ import { projectById, stagesOf } from "../admission/repo.js";
3
3
  import { stepFailedWhileWaiting } from "../admission/state-words.js";
4
4
  import { lastStarter } from "../agents/repo.js";
5
5
  import { projectHolds } from "../reviews/holds.js";
6
+ import { isTemplateSample } from "../template-studio/repo.js";
6
7
  import { uploadedAndSettled } from "../uploads/repo.js";
7
8
  import { settledStatus } from "../uploads/rules.js";
8
9
  import { atItsDay, failuresOf } from "./rules.js";
@@ -10,7 +11,7 @@ import { atItsDay, failuresOf } from "./rules.js";
10
11
  // Projects list reads it with (`edge/http/projects.ts`).
11
12
  export function noticeSubject(db, projectId) {
12
13
  const project = projectById(db, projectId);
13
- if (project === undefined)
14
+ if (project === undefined || isTemplateSample(db, projectId))
14
15
  return undefined;
15
16
  const stages = stagesOf(db, projectId);
16
17
  const settled = uploadedAndSettled(db, projectId);
@@ -1,6 +1,7 @@
1
1
  import { copyFileSync, lstatSync, mkdirSync, readdirSync, rmSync } from "node:fs";
2
2
  import { dirname, join, relative, sep } from "node:path";
3
3
  import { transact } from "../../kernel/db/tx.js";
4
+ import { internalError } from "../../kernel/errors.js";
4
5
  import { executionPlan } from "../rebuild/runtime-plan.js";
5
6
  import { currentRevisionId } from "../revisions/repo.js";
6
7
  import { getRevisionView } from "../revisions/view.js";
@@ -104,7 +105,7 @@ function rebind(deps, projectId) {
104
105
  .prepare(`UPDATE ${table} SET ${column}=replace(${column},?,?) WHERE project_id=? AND instr(${column},?)>0`)
105
106
  .run(before, after, projectId, before);
106
107
  }
107
- throw new Error("Slopify hit an internal error (the copy of the sample kept changing while its steps were matched up). Delete the copy and try Make my own copy again; if it happens again, use Download diagnostics in Settings and report it.");
108
+ throw new Error(internalError("the copy of the sample kept changing while its steps were matched up", "Delete the copy and try Make my own copy again"));
108
109
  }
109
110
  function requestOf(payload) {
110
111
  try {
@@ -1,16 +1,17 @@
1
1
  import { readinessIsUsable } from "../../kernel/ports/model.js";
2
2
  import { templateById } from "../project-templates/repo.js";
3
+ import { notTemplateSample } from "../template-studio/repo.js";
3
4
  import { starterPacks } from "./packs.js";
4
5
  import { sampleProjectIds } from "./sample.js";
5
6
  import { firstRunDismissed, readPackRecords } from "./state.js";
6
7
  // The first-run screen shows on a fresh install and never again once it is skipped or a real
7
- // project (anything but the samples) exists. Reading it writes nothing: a real project that
8
+ // project (anything but the samples and the template studio's hidden projects) exists. Reading it writes nothing: a real project that
8
9
  // exists before the screen was recorded as done is reported as `settle`, and the client
9
10
  // records it with POST /dismiss, so deleting every project later does not bring it back.
10
11
  export function firstRunView(db, statuses) {
11
12
  const samples = sampleProjectIds(db);
12
13
  const real = db
13
- .prepare("SELECT 1 FROM projects WHERE id IS NOT ? AND id IS NOT ? AND id IS NOT ? LIMIT 1")
14
+ .prepare(`SELECT 1 FROM projects WHERE id IS NOT ? AND id IS NOT ? AND id IS NOT ? AND ${notTemplateSample()} LIMIT 1`)
14
15
  .get(samples.library, samples.audiobook, samples.podcast) !== undefined;
15
16
  const dismissed = firstRunDismissed(db);
16
17
  const packs = readPackRecords(db);
@@ -26,7 +26,7 @@ export function fullVideoDraft(deps, input) {
26
26
  const config = project.config;
27
27
  if (!usesShortMode(config))
28
28
  return { ok: false, reason: "not-short" };
29
- const topic = config.values.topic?.trim() || project.title;
29
+ const topic = config.values.Topic?.trim() || config.values.topic?.trim() || project.title;
30
30
  const pack = shortPack(deps, config.articlePrompt ?? "");
31
31
  const document = packDocument(deps, pack);
32
32
  if (document === undefined)
@@ -46,7 +46,8 @@ export function fullVideoDraft(deps, input) {
46
46
  form: {
47
47
  ...form,
48
48
  title: project.title,
49
- values: { ...form.values, topic },
49
+ // {{Topic}} since 4.0; a pack installed before asks for {{topic}}.
50
+ values: { ...form.values, Topic: topic, topic },
50
51
  llm: provider(config.llm, form.llm),
51
52
  images: provider(config.images, form.images),
52
53
  audio: config.audio === undefined
@@ -1,4 +1,5 @@
1
1
  import { transact } from "../../kernel/db/tx.js";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { detectSlots } from "../admission/substitute.js";
3
4
  import { nameMax } from "../library/model.js";
4
5
  import { insertPrompt, promptById, promptByName } from "../library/repo.js";
@@ -48,7 +49,7 @@ export function installPack(deps, packId, options = { template: true }) {
48
49
  document: packTemplate(pack, names, voice.voiceId),
49
50
  });
50
51
  if (!created.ok)
51
- throw new Error(`Slopify hit an internal error (the ${pack.name} pack's template was refused: ${created.reason}). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
52
+ throw new Error(internalError(`the ${pack.name} pack's template was refused: ${created.reason}`));
52
53
  template = id;
53
54
  added = true;
54
55
  }
@@ -71,7 +72,7 @@ export function installPack(deps, packId, options = { template: true }) {
71
72
  };
72
73
  });
73
74
  }
74
- function placePrompt(deps, kind, name, body, at) {
75
+ export function placePrompt(deps, kind, name, body, at) {
75
76
  for (let n = 1;; n += 1) {
76
77
  const suffix = n === 1 ? "" : ` (${String(n)})`;
77
78
  const candidate = `${name.slice(0, nameMax - suffix.length).trimEnd()}${suffix}`;
@@ -109,7 +110,7 @@ function placeVoice(deps, pack, before) {
109
110
  });
110
111
  return { id, voiceId: pack.voice.voiceId, added: true };
111
112
  }
112
- function freeTemplateName(deps, name) {
113
+ export function freeTemplateName(deps, name) {
113
114
  const taken = new Set(templateSummaries(deps.db).map((row) => row.name.toLowerCase()));
114
115
  for (let n = 1;; n += 1) {
115
116
  const candidate = n === 1 ? name : `${name} (${String(n)})`;
@@ -63,22 +63,6 @@ export const quickShortInputSchema = z
63
63
  export const fullVideoInputSchema = z
64
64
  .object({ projectId: z.string().min(1).max(64), draftId: z.uuid() })
65
65
  .strict();
66
- export const quickShortResultSchema = z.object({
67
- projectId: z.string(),
68
- replayed: z.boolean(),
69
- });
70
- export const installedPackSchema = z.object({
71
- packId: z.string(),
72
- added: z.boolean(),
73
- prompts: z.record(z.string(), z.string()),
74
- templateId: z.string().nullable(),
75
- voice: z.string().nullable(),
76
- });
77
- export const sampleCopySchema = z.object({ projectId: z.string() });
78
- export const sampleStateSchema = z.object({
79
- projectId: z.string().nullable(),
80
- samples: sampleProjectsSchema,
81
- });
82
66
  // Make my own copy of one sample; without a project, the Library of Alexandria.
83
67
  export const sampleCopyInputSchema = z
84
68
  .object({ projectId: z.string().max(60).optional() })