@gentbajko/slopify 3.25.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -1,270 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
- import { dirname } from "node:path";
3
- import { z } from "zod";
4
- import { piecesOf, setPiece } from "../../kernel/runner/piece-repo.js";
5
- import { entryModes } from "../admission/model.js";
6
- import { projectById, stagesOf } from "../admission/repo.js";
7
- import { setStageProgress } from "../admission/stage-writes.js";
8
- import { entryCategories } from "../library/model.js";
9
- import { outputFileName, outputPath } from "../storage/layout.js";
10
- import { insertOutput, outputsOf } from "../storage/repo.js";
11
- import { probeDurationMs } from "../video/ffmpeg.js";
12
- import { chunkNarration, defaultChunking } from "./chunk.js";
13
- import { joinNarration } from "./concat.js";
14
- import { planLegacyNarration } from "./legacy-plan.js";
15
- import { observeNarration } from "./live.js";
16
- // An empty narration source fails the stage immediately with no retries. Thrown from the
17
- // slice rather than from a provider call, so the attempt wrapper never sees it.
18
- export const nothingToNarrate = "There is nothing to narrate because the article is empty. Add article text in Edit project → Article, then Try again.";
19
- // What a chunk piece carries between runs: the text that was sent, and the file it came
20
- // back as once the provider answered. That is the whole of the resume.
21
- const chunkPayload = z.object({ text: z.string(), file: z.string().optional() });
22
- // What the article stage left on its `segment` pieces.
23
- const segmentPayload = z.object({
24
- category: z.enum(entryCategories),
25
- name: z.string(),
26
- mode: z.enum(entryModes),
27
- text: z.string(),
28
- });
29
- // The chunk audio sits in a folder of its own under the project. It is not an output: the
30
- // concatenated body plus the picked intro and outro are the project's only audio outputs,
31
- // so the pieces name these files and the boot reconcile keeps them by that name
32
- // (`slices/storage/reconcile.ts`).
33
- const chunkDir = "audio-chunks";
34
- export async function runNarration(deps, context, providers) {
35
- const { projectId } = context.stage;
36
- deps.audioPreviews?.clear(projectId);
37
- const project = projectById(deps.db, projectId);
38
- if (project === undefined) {
39
- throw new Error("This project no longer exists; it may have been deleted while it was running.");
40
- }
41
- const choice = project.config.audio;
42
- if (choice === undefined) {
43
- // Admission refuses a run whose audio is Generate without a provider and a voice, so
44
- // reaching here is a bug in admission rather than something the user did.
45
- throw new Error("No voice is set for the narration. Choose a voice in Edit project → Providers, then Try again.");
46
- }
47
- // Read once, before anything is written: the segment steps below ask it what a previous
48
- // run already stored, and the body step is the only thing that adds to it.
49
- const outputs = outputsOf(deps.db, projectId);
50
- // The end matter is already out of this text: the article stage cut it into
51
- // files of its own, so nothing here can narrate a sources list.
52
- const chunking = project.config.chunking ?? defaultChunking;
53
- const texts = chunkNarration(narrationSource(deps, outputs, projectId), chunking);
54
- if (texts.length === 0) {
55
- throw new Error(nothingToNarrate);
56
- }
57
- const planned = planLegacyNarration(deps, context.stage.id, texts, deps.maxCharacters?.(choice.provider, choice.model) ?? 1000000, (piece) => finished(deps, projectId, piece) !== undefined);
58
- const result = await speakChunks(deps, context, providers, choice, planned);
59
- if (!result.ok)
60
- return "held";
61
- await storeBody(deps, context, choice, result.value, outputs);
62
- if ((await speakSegments(deps, context, providers, choice, outputs)) === "held")
63
- return "held";
64
- deps.log.write("info", "narration.done", {
65
- projectId,
66
- stage: "audio",
67
- detail: `${String(texts.length)} chunks in ${chunking.mode} mode`,
68
- });
69
- return "done";
70
- }
71
- // The narration source: the plain-text article, written by the article stage or pasted in
72
- // by a user whose article stage was set to Provide.
73
- function narrationSource(deps, outputs, projectId) {
74
- const article = outputs.find((output) => output.role === "article_txt");
75
- if (article === undefined) {
76
- throw new Error(nothingToNarrate);
77
- }
78
- return readFileSync(outputPath(deps.paths, projectId, article.path), "utf8");
79
- }
80
- // Chunks are scheduled through the shared provider queue. A chunk a previous run finished is not spoken
81
- // again, and one that fails takes down the whole stage while its siblings finish and keep their
82
- // audio for the next resume.
83
- async function speakChunks(deps, context, providers, choice, pieces) {
84
- const { projectId } = context.stage;
85
- const total = pieces.length;
86
- let done = pieces.filter((piece) => finished(deps, projectId, piece)).length;
87
- report(deps, context, done, total);
88
- const outcomes = await Promise.all(pieces.map(async (piece) => {
89
- const carried = chunkOf(piece);
90
- const kept = finished(deps, projectId, piece);
91
- if (kept !== undefined) {
92
- return { ok: true, file: kept };
93
- }
94
- const file = `${chunkDir}/${piece.id}.mp3`;
95
- setPiece(deps.db, piece.id, "running", piece.payload);
96
- try {
97
- const spoken = await providers.forPiece(piece.id).tts({
98
- provider: choice.provider,
99
- model: choice.model,
100
- voiceId: choice.voice,
101
- text: carried.text,
102
- }, observeNarration(deps.audioPreviews, projectId, piece.id, total === 1 ? "Body" : `Body part ${piece.idx} of ${total}`));
103
- if (!spoken.ok) {
104
- setPiece(deps.db, piece.id, "pending", piece.payload);
105
- return spoken;
106
- }
107
- write(deps, projectId, file, spoken.value.bytes);
108
- // The file is on disk before the row says so: a crash between the two leaves a
109
- // file the reconcile collects, where the other order would leave a `done` chunk
110
- // whose audio never existed and a concat that fails on every retry.
111
- setPiece(deps.db, piece.id, "done", JSON.stringify({ text: carried.text, file }));
112
- done += 1;
113
- report(deps, context, done, total);
114
- return { ok: true, file };
115
- }
116
- catch (error) {
117
- // A cancel is not this chunk failing: an aborted call counts as nothing, and the
118
- // resume runs a `pending` chunk exactly as a failed one.
119
- setPiece(deps.db, piece.id, context.signal.aborted ? "pending" : "failed", piece.payload);
120
- return { ok: false, error };
121
- }
122
- }));
123
- const files = [];
124
- for (const outcome of outcomes) {
125
- if (!outcome.ok) {
126
- if ("reason" in outcome)
127
- return outcome;
128
- throw outcome.error;
129
- }
130
- files.push(outcome.file);
131
- }
132
- return { ok: true, value: files };
133
- }
134
- // One body file, in chunk order, with its measured duration on the row.
135
- async function storeBody(deps, context, choice, files, outputs) {
136
- const { projectId } = context.stage;
137
- if (outputs.some((output) => output.role === "audio_body")) {
138
- // A retry that failed on a segment last time: the body is already joined and measured,
139
- // and joining it again would insert a second row for the same file.
140
- return;
141
- }
142
- const name = outputFileName("audio_body", 1, ".mp3", "audio");
143
- const durationMs = await joinNarration({ bin: deps.ffmpeg, log: deps.log }, {
144
- files: files.map((file) => outputPath(deps.paths, projectId, file)),
145
- listPath: outputPath(deps.paths, projectId, `${chunkDir}/concat.txt`),
146
- output: outputPath(deps.paths, projectId, name),
147
- signal: context.signal,
148
- });
149
- // A cancel protects an output already stored, not one about to be: one landing between
150
- // ffmpeg exiting and the row below leaves nothing recorded.
151
- context.signal.throwIfAborted();
152
- store(deps, projectId, "audio_body", name, durationMs, choice);
153
- counted(deps, "body", durationMs, choice);
154
- }
155
- // Each picked segment's text is one TTS request with the same provider and voice, stored as
156
- // its own audio file with its duration; body chunking does not apply to them. The text is
157
- // whatever the article stage wrote onto the segment piece: an LLM-mode entry's answer, or a
158
- // text-mode entry's rendered body, spoken verbatim.
159
- async function speakSegments(deps, context, providers, choice, outputs) {
160
- const { projectId } = context.stage;
161
- for (const piece of segmentPieces(deps, projectId)) {
162
- const segment = segmentOf(piece);
163
- const role = segment.category === "intro" ? "audio_intro" : "audio_outro";
164
- if (outputs.some((output) => output.role === role)) {
165
- // Spoken by a previous run of this stage; the resume keeps it.
166
- continue;
167
- }
168
- const text = segment.text.trim();
169
- if (text === "") {
170
- // The same rule, applied to a segment: there is nothing to say, and silently
171
- // dropping an intro the user picked would lose it without telling anyone.
172
- throw new Error(`The ${segment.category} is empty, so there is nothing to narrate for it. Write the ${segment.category} or remove it in Edit project, then Try again.`);
173
- }
174
- const spoken = await providers.forPiece(piece.id).tts({
175
- provider: choice.provider,
176
- model: choice.model,
177
- voiceId: choice.voice,
178
- text,
179
- }, observeNarration(deps.audioPreviews, projectId, piece.id, segment.category === "intro" ? "Intro" : "Outro"));
180
- if (!spoken.ok)
181
- return "held";
182
- const name = outputFileName(role, 1, ".mp3", "audio");
183
- write(deps, projectId, name, spoken.value.bytes);
184
- // Measured the same way the body is, because the render adds all three and the gaps to
185
- // get the length of the video.
186
- const durationMs = await probeDurationMs(deps.ffmpeg, outputPath(deps.paths, projectId, name), context.signal, deps.log);
187
- context.signal.throwIfAborted();
188
- store(deps, projectId, role, name, durationMs, choice);
189
- counted(deps, segment.category, durationMs, choice);
190
- }
191
- return "done";
192
- }
193
- // The segment pieces belong to the *article* stage, which is where they are written, so
194
- // they are read under its id and never under this stage's own. They stay there because the
195
- // re-run rules depend on it: a Re-run audio, and the audio re-run an article edit cascades
196
- // into, both clear the audio stage's pieces, and only the article stage ever writes an
197
- // entry text - so an intro kept on this stage's row would be thrown away by the first
198
- // re-narration and never written again.
199
- function segmentPieces(deps, projectId) {
200
- const article = stagesOf(deps.db, projectId).find((stage) => stage.kind === "article");
201
- if (article === undefined) {
202
- // Admission writes all six stage rows with the project, so a project without an article
203
- // stage is a bug rather than a run the user configured.
204
- throw new Error("Slopify hit an internal error (the project has no article stage). Try again; if it happens again, use Download diagnostics in Settings and report it.");
205
- }
206
- return piecesOf(deps.db, article.id, "segment");
207
- }
208
- // Audio seconds come from the measured duration per segment, never from the text's length.
209
- function counted(deps, segment, durationMs, choice) {
210
- deps.count("stage.completed", {
211
- stage: "audio",
212
- segment,
213
- provider: choice.provider,
214
- model: choice.model,
215
- audioSeconds: durationMs / 1000,
216
- });
217
- }
218
- // K of N chunks narrated, written as well as emitted, so a page opened mid-stage reads the
219
- // count off the row rather than waiting for the next chunk.
220
- function report(deps, context, done, total) {
221
- setStageProgress(deps.db, context.stage.id, done, total);
222
- context.emit({
223
- type: "stage.progress",
224
- projectId: context.stage.projectId,
225
- stage: "audio",
226
- current: done,
227
- total,
228
- });
229
- }
230
- // A chunk counts as finished only when its audio is still on disk: the row and the file
231
- // are written one after the other, and the boot reconcile can remove a file whose row
232
- // survived. Answering "done" for a missing file would fail the concat instead.
233
- function finished(deps, projectId, piece) {
234
- const file = chunkOf(piece).file;
235
- if (file === undefined) {
236
- return undefined;
237
- }
238
- return existsSync(outputPath(deps.paths, projectId, file)) ? file : undefined;
239
- }
240
- function write(deps, projectId, path, bytes) {
241
- const target = outputPath(deps.paths, projectId, path);
242
- mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
243
- writeFileSync(target, bytes, { mode: 0o600 });
244
- }
245
- function store(deps, projectId, role, path, durationMs, choice) {
246
- insertOutput(deps.db, {
247
- id: deps.ids.next(),
248
- projectId,
249
- stageKind: "audio",
250
- role,
251
- path,
252
- originalFilename: null,
253
- bytes: statSync(outputPath(deps.paths, projectId, path)).size,
254
- durationMs,
255
- meta: { provider: choice.provider, model: choice.model, voice: choice.voice },
256
- createdAt: deps.clock.now().toISOString(),
257
- });
258
- }
259
- function chunkOf(piece) {
260
- if (piece.payload === null) {
261
- throw new Error("Slopify hit an internal error (a saved narration chunk is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
262
- }
263
- return chunkPayload.parse(JSON.parse(piece.payload));
264
- }
265
- function segmentOf(piece) {
266
- if (piece.payload === null) {
267
- throw new Error("Slopify hit an internal error (a saved intro or outro is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
268
- }
269
- return segmentPayload.parse(JSON.parse(piece.payload));
270
- }
@@ -1,90 +0,0 @@
1
- import { saveRevision } from "../revisions/mutations.js";
2
- import { currentRevisionId } from "../revisions/repo.js";
3
- import { getRevisionView } from "../revisions/view.js";
4
- import { projectStandings } from "./runtime-store.js";
5
- export async function revisionAction(deps, projectId, action) {
6
- const revisionId = currentRevisionId(deps.db, projectId);
7
- const view = revisionId === undefined ? undefined : getRevisionView(deps, projectId, revisionId);
8
- if (view === undefined)
9
- return { ok: false, reason: "no-project" };
10
- if (action.kind === "retry")
11
- return { ok: false, reason: "rebuild-required" };
12
- const config = view.revision.config;
13
- const content = view.revision.content;
14
- const image = action.kind === "delete-image" || action.kind === "regenerate-image"
15
- ? view.outputs.find((row) => row.selected &&
16
- row.output.id === action.outputId &&
17
- (row.output.role === "image" ||
18
- // One of the generated thumbnails is made again on its own, the others kept.
19
- (action.kind === "regenerate-image" &&
20
- row.output.role === "thumbnail" &&
21
- (config.sources.thumbnail === "from_prompt" ||
22
- config.sources.thumbnail === "prompt_by_llm")) ||
23
- // A generated establishing image is made again like any image; it is never deleted
24
- // on its own (Edit project sets it Off), and an uploaded one has nothing to redo.
25
- (action.kind === "regenerate-image" &&
26
- row.output.role === "reference" &&
27
- config.reference?.source === "prompt")))
28
- : undefined;
29
- if ((action.kind === "delete-image" || action.kind === "regenerate-image") && image === undefined)
30
- return { ok: false, reason: "unknown-image" };
31
- const imageKey = image?.workKey.slice(6);
32
- if (action.kind === "article" && action.markdown.trim() === "")
33
- return { ok: false, reason: "empty-article" };
34
- const regenerate = action.kind === "rerun"
35
- ? Object.keys(view.revision.fingerprints).filter((key) => keyStage(key) === action.stage)
36
- : action.kind === "regenerate-image" && image !== undefined
37
- ? [image.workKey]
38
- : undefined;
39
- const nextContent = action.kind === "article"
40
- ? { ...content, articleMarkdown: action.markdown, articleEdited: true }
41
- : action.kind === "delete-image"
42
- ? {
43
- ...content,
44
- imageOrder: content.imageOrder.filter((key) => key !== imageKey),
45
- imageDefinitions: Object.fromEntries(Object.entries(content.imageDefinitions).filter(([key]) => key !== imageKey)),
46
- }
47
- : content;
48
- const nextConfig = action.kind === "delete-image" && nextContent.imageOrder.length === 0
49
- ? {
50
- ...config,
51
- sources: { ...config.sources, images: "off", video: "off" },
52
- ...(config.subtitles?.mode === "burn-in"
53
- ? { subtitles: { ...config.subtitles, mode: "files" } }
54
- : {}),
55
- }
56
- : config;
57
- const saved = await saveRevision(deps, {
58
- projectId,
59
- baseRevisionId: view.revision.id,
60
- idempotencyKey: deps.ids.next(),
61
- edit: {
62
- config: nextConfig,
63
- content: nextContent,
64
- ...(regenerate === undefined ? {} : { regenerate }),
65
- },
66
- });
67
- if (!saved.ok)
68
- return { ok: false, reason: "not-rerunnable" };
69
- projectStandings(deps, projectId);
70
- return {
71
- ok: true,
72
- redone: [],
73
- };
74
- }
75
- function keyStage(key) {
76
- const prefix = key.split(":")[0];
77
- if (prefix === "entry" || prefix === "article")
78
- return "article";
79
- if (prefix === "image" || prefix === "reference")
80
- return "images";
81
- if (prefix === "audio")
82
- return "audio";
83
- if (prefix === "thumbnail")
84
- return "thumbnail";
85
- if (prefix === "research")
86
- return "research";
87
- if (prefix === "document")
88
- return "document";
89
- return "video";
90
- }
@@ -1,224 +0,0 @@
1
- import { z } from "zod";
2
- import { transact } from "../../kernel/db/tx.js";
3
- import { isProviderError } from "../../kernel/ports/model.js";
4
- import { insertPiece, piecesOf, setPiece } from "../../kernel/runner/piece-repo.js";
5
- import { projectById } from "../admission/repo.js";
6
- import { setStageProgress } from "../admission/stage-writes.js";
7
- import { storeText } from "../storage/staging.js";
8
- import { noTokens, plusUsage } from "../telemetry/model.js";
9
- import { researchDocuments } from "./documents.js";
10
- import { chaptersFrom, plannerMessages, subAgentMessages } from "./planner.js";
11
- import { sourcedAnswer, synthesisMessages } from "./synthesis.js";
12
- // A model that cannot ground on the web fails the stage immediately, with no fallback to
13
- // what the model already knows.
14
- export const webResearchUnsupported = "The chosen AI model can't search the web, which research needs. Choose a model with web search in Edit project → Providers, or turn research off, then Try again.";
15
- // What a chapter piece carries between runs: its title, and its notes once a sub-agent
16
- // has answered. That is the whole of the resume.
17
- const chapterPayload = z.object({ title: z.string(), notes: z.string().optional() });
18
- export async function runResearch(deps, context, providers) {
19
- const { projectId } = context.stage;
20
- const project = projectById(deps.db, projectId);
21
- if (project === undefined) {
22
- throw new Error("This project no longer exists; it may have been deleted while it was running.");
23
- }
24
- const choice = project.config.llm;
25
- const articlePrompt = project.config.rendered.article;
26
- if (choice === undefined || articlePrompt === undefined) {
27
- // Admission refuses a run whose research is Generate without both, so
28
- // reaching here is a bug in admission rather than something the user did.
29
- throw new Error("No AI model or article prompt is set for research. Choose a model in Edit project → Providers and check Edit project → Prompts, then Try again.");
30
- }
31
- const brief = { articlePrompt, values: project.config.values };
32
- try {
33
- return await research(deps, context, providers, choice, brief);
34
- }
35
- catch (error) {
36
- // The adapter says the model cannot ground on the web and the wrapper has already
37
- // made that terminal; this is where it becomes the sentence the user reads.
38
- if (isProviderError(error) && error.fault.kind === "unsupported") {
39
- throw new Error(webResearchUnsupported);
40
- }
41
- throw error;
42
- }
43
- }
44
- async function research(deps, context, providers, choice, brief) {
45
- // Tokens are counted per stage, so the planner, every sub-agent and the synthesis add
46
- // into one total. A call that was aborted or failed never answers and adds nothing; a
47
- // provider that reports no usage adds zero, never an estimate.
48
- let tokens = noTokens;
49
- const add = (usage) => {
50
- tokens = plusUsage(tokens, usage);
51
- };
52
- const planned = await plan(deps, context, providers, choice, brief, add);
53
- if (!planned.ok)
54
- return "held";
55
- const researched = await researchChapters(deps, context, providers, choice, brief, planned.value, add);
56
- if (!researched.ok)
57
- return "held";
58
- const findings = researched.value;
59
- const answer = await providers.llm({
60
- provider: choice.provider,
61
- model: choice.model,
62
- ...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
63
- messages: synthesisMessages(brief, findings),
64
- documents: researchDocuments(findings),
65
- previewLabel: "Writing research notes",
66
- check: (given) => sourcedAnswer("the research summary", given.text),
67
- });
68
- if (!answer.ok)
69
- return "held";
70
- add(answer.value.usage);
71
- const { projectId } = context.stage;
72
- // The notes and every instruction sent are stored on the project. Each sub-agent's own output
73
- // stays on its chapter row, which is where the resume reads it.
74
- storeText(deps, {
75
- projectId,
76
- stageKind: "research",
77
- role: "notes",
78
- text: answer.value.text.trim(),
79
- });
80
- storeText(deps, {
81
- projectId,
82
- stageKind: "research",
83
- role: "instructions",
84
- text: instructionsText(brief, findings),
85
- });
86
- deps.count("stage.completed", {
87
- stage: "research",
88
- provider: choice.provider,
89
- model: choice.model,
90
- ...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
91
- ...tokens,
92
- });
93
- deps.log.write("info", "research.done", {
94
- projectId,
95
- stage: "research",
96
- detail: `${String(findings.length)} chapters researched`,
97
- });
98
- return "done";
99
- }
100
- // The chapter list, planned once and kept. A retry after a failure finds the rows the
101
- // first run wrote and does not ask again.
102
- async function plan(deps, context, providers, choice, brief, add) {
103
- const existing = piecesOf(deps.db, context.stage.id, "chapter");
104
- if (existing.length > 0) {
105
- return { ok: true, value: existing };
106
- }
107
- const answer = await providers.llm({
108
- provider: choice.provider,
109
- model: choice.model,
110
- ...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
111
- messages: plannerMessages(brief),
112
- previewLabel: "Planning research",
113
- // An empty answer, or one with no chapter in it, is a failed attempt.
114
- check: (given) => chaptersFrom(given.text).length === 0
115
- ? "The AI model didn't return any research topics. Try again; if it keeps happening, choose a different model in Edit project → Providers."
116
- : undefined,
117
- });
118
- if (!answer.ok)
119
- return answer;
120
- add(answer.value.usage);
121
- // No cap on the count. The list is the prompt's own section guide as often as
122
- // not, and capping it would silently drop a section the article asks for.
123
- const planned = chaptersFrom(answer.value.text).map((title, index) => ({
124
- id: deps.ids.next(),
125
- stageId: context.stage.id,
126
- kind: "chapter",
127
- idx: index + 1,
128
- state: "pending",
129
- payload: JSON.stringify({ title }),
130
- }));
131
- transact(deps.db, () => {
132
- for (const piece of planned) {
133
- insertPiece(deps.db, piece);
134
- }
135
- });
136
- return { ok: true, value: planned };
137
- }
138
- // One sub-agent per chapter, all in parallel. A chapter a previous run finished is not
139
- // researched again, and one that fails takes down the whole stage while its siblings finish and
140
- // keep their output for the next resume.
141
- async function researchChapters(deps, context, providers, choice, brief, chapters, add) {
142
- const outline = chapters.map((piece) => payloadOf(piece).title);
143
- const total = chapters.length;
144
- let done = chapters.filter((piece) => payloadOf(piece).notes !== undefined).length;
145
- report(deps, context, done, total);
146
- const outcomes = await Promise.all(chapters.map(async (piece) => {
147
- const kept = payloadOf(piece);
148
- if (kept.notes !== undefined) {
149
- return { ok: true, finding: { title: kept.title, notes: kept.notes } };
150
- }
151
- setPiece(deps.db, piece.id, "running", piece.payload);
152
- try {
153
- const answer = await providers.forPiece(piece.id).llm({
154
- provider: choice.provider,
155
- model: choice.model,
156
- ...(choice.thinking === undefined ? {} : { thinking: choice.thinking }),
157
- messages: subAgentMessages(brief, kept.title, outline),
158
- previewLabel: kept.title,
159
- // Grounding is asked for explicitly, so a model without it says so
160
- // instead of answering from what it already knows.
161
- webSearch: true,
162
- check: (given) => sourcedAnswer(`the research topic "${kept.title}"`, given.text),
163
- });
164
- if (!answer.ok) {
165
- setPiece(deps.db, piece.id, "pending", piece.payload);
166
- return answer;
167
- }
168
- add(answer.value.usage);
169
- const notes = answer.value.text.trim();
170
- setPiece(deps.db, piece.id, "done", JSON.stringify({ title: kept.title, notes }));
171
- done += 1;
172
- report(deps, context, done, total);
173
- return { ok: true, finding: { title: kept.title, notes } };
174
- }
175
- catch (error) {
176
- // A cancel is not this chapter failing: an aborted call counts as nothing, and
177
- // the resume runs a `pending` chapter exactly as a failed one.
178
- setPiece(deps.db, piece.id, context.signal.aborted ? "pending" : "failed", piece.payload);
179
- return { ok: false, error };
180
- }
181
- }));
182
- const findings = [];
183
- for (const outcome of outcomes) {
184
- if (!outcome.ok) {
185
- if ("reason" in outcome)
186
- return outcome;
187
- throw outcome.error;
188
- }
189
- findings.push(outcome.finding);
190
- }
191
- return { ok: true, value: findings };
192
- }
193
- // K of N chapters researched, written as well as emitted, so a page opened mid-stage reads the
194
- // count off the row rather than waiting for the next chapter.
195
- function report(deps, context, done, total) {
196
- setStageProgress(deps.db, context.stage.id, done, total);
197
- context.emit({
198
- type: "stage.progress",
199
- projectId: context.stage.projectId,
200
- stage: "research",
201
- current: done,
202
- total,
203
- });
204
- }
205
- // Every instruction sent is stored on the project. All three are pure functions of
206
- // the brief and the findings, so a resumed run reproduces the ones it did not send.
207
- function instructionsText(brief, findings) {
208
- const outline = findings.map((finding) => finding.title);
209
- const parts = [section("Planner", plannerMessages(brief))];
210
- for (const title of outline) {
211
- parts.push(section(`Sub-agent: ${title}`, subAgentMessages(brief, title, outline)));
212
- }
213
- parts.push(section("Synthesis", synthesisMessages(brief, findings)));
214
- return `${parts.join("\n\n")}\n`;
215
- }
216
- function section(label, messages) {
217
- return `=== ${label} ===\n\n${messages.map((message) => message.content).join("\n\n")}`;
218
- }
219
- function payloadOf(piece) {
220
- if (piece.payload === null) {
221
- throw new Error("Slopify hit an internal error (a saved research topic is empty). Try again; if it happens again, use Download diagnostics in Settings and report it.");
222
- }
223
- return chapterPayload.parse(JSON.parse(piece.payload));
224
- }