@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
@@ -0,0 +1,333 @@
1
+ import { z } from "zod";
2
+ import { transact } from "../../kernel/db/tx.js";
3
+ import { projectExists } from "../admission/repo.js";
4
+ import { keepThinking } from "../model-upkeep/switch.js";
5
+ import { templateById } from "../project-templates/repo.js";
6
+ import { currentRevisionId } from "../revisions/repo.js";
7
+ import { getRevisionView, isReadyOutput } from "../revisions/view.js";
8
+ import { runCostOf } from "../run-cost/panel.js";
9
+ import { bakeoffOf, bakeoffSections, clonesOf, deleteBakeoffRow, insertBakeoff, } from "./bakeoff-repo.js";
10
+ import { budgetRoom, budgetWords, fitsBudget } from "./budget.js";
11
+ import { removeHiddenProject } from "./hidden.js";
12
+ import { enqueueJob, queuedJob, runningJob } from "./jobs.js";
13
+ import { sectionLabel } from "./model.js";
14
+ import { quoteSample } from "./quote.js";
15
+ import { sampleOf, sampleVersions } from "./repo.js";
16
+ import { exceedsAcknowledged } from "./save.js";
17
+ import { sectionOfWork } from "./sections.js";
18
+ // Compare models: one section's sample made again on one or two other models, each on a hidden
19
+ // copy of the template's sample (`onboarding/copy.ts`), so its cost and speed sit beside the
20
+ // model the template uses now. Only that section's work runs on each copy. Nothing about the
21
+ // template changes: "Use this model" is a normal Save, and the copies are discarded after.
22
+ export const bakeoffModelSchema = z
23
+ .object({
24
+ provider: z.string().trim().min(1).max(100),
25
+ model: z.string().trim().min(1).max(200),
26
+ })
27
+ .strict();
28
+ export const bakeoffQuoteSchema = z
29
+ .object({
30
+ section: z.enum(bakeoffSections),
31
+ // The models to compare with the one the template uses now: one or two.
32
+ models: z.array(bakeoffModelSchema).min(1).max(2),
33
+ })
34
+ .strict();
35
+ export const bakeoffStartSchema = bakeoffQuoteSchema
36
+ .extend({
37
+ // The total high estimate the quote showed, and that steps with no price are accepted.
38
+ acknowledgedHigh: z.number().finite().min(0).max(10_000),
39
+ acknowledgedUnknown: z.boolean().optional(),
40
+ })
41
+ .strict();
42
+ // The stage each section's model works in, which its time is read from.
43
+ export const bakeoffStage = {
44
+ script: "article",
45
+ voice: "audio",
46
+ pictures: "images",
47
+ };
48
+ // The model a section is made with now, or undefined when no model makes it.
49
+ export function sectionModel(document, section) {
50
+ const { form } = document;
51
+ const pick = (choice) => choice.provider === "" || choice.model === ""
52
+ ? undefined
53
+ : { provider: choice.provider, model: choice.model };
54
+ switch (section) {
55
+ case "script":
56
+ return form.sources.article === "generate" ? pick(form.llm) : undefined;
57
+ case "voice":
58
+ return form.sources.audio === "generate" ? pick(form.audio) : undefined;
59
+ case "pictures":
60
+ return form.sources.images === "generate" ? pick(form.images) : undefined;
61
+ }
62
+ }
63
+ // The template's setup with the section made with `model` and nothing else changed.
64
+ export function withModel(document, section, model, catalogue) {
65
+ const { form } = document;
66
+ switch (section) {
67
+ case "script": {
68
+ const { thinking, ...rest } = form.llm;
69
+ return {
70
+ ...document,
71
+ form: {
72
+ ...form,
73
+ llm: {
74
+ ...rest,
75
+ provider: model.provider,
76
+ model: model.model,
77
+ ...keepThinking(catalogue, model.provider, model.model, thinking),
78
+ },
79
+ },
80
+ };
81
+ }
82
+ case "voice":
83
+ return { ...document, form: { ...form, audio: { ...form.audio, model: model.model } } };
84
+ case "pictures":
85
+ return {
86
+ ...document,
87
+ form: { ...form, images: { ...form.images, provider: model.provider, model: model.model } },
88
+ };
89
+ }
90
+ }
91
+ const refuse = (reason, message) => ({
92
+ ok: false,
93
+ reason,
94
+ message,
95
+ });
96
+ const missingTemplate = "This template no longer exists; it may have been deleted. Go to Library → Templates to see your templates.";
97
+ // What the bake-off is made on: the template's newest version, with a current paid sample.
98
+ function basis(deps, templateId, section, models) {
99
+ const template = templateById(deps.db, templateId);
100
+ if (template === undefined)
101
+ return refuse("not-found", missingTemplate);
102
+ const label = sectionLabel[section];
103
+ const current = sectionModel(template.document, section);
104
+ if (current === undefined)
105
+ return refuse("invalid-input", `No model makes ${label} in this template, so there is nothing to compare. Choose a model in ${label} and press Save first.`);
106
+ const sample = sampleOf(deps.db, templateId);
107
+ const made = sampleVersions(deps.db, templateId).find((one) => one.templateVersion === template.version);
108
+ if (sample === undefined || !projectExists(deps.db, sample.projectId) || made?.state !== "done")
109
+ return refuse("not-ready", `Compare models runs on the template's current sample, and version ${String(template.version)} has none yet. Press Save (or Make sample) in the studio, wait for it, then compare.`);
110
+ const seen = new Set();
111
+ for (const model of models) {
112
+ const key = `${model.provider}:${model.model}`;
113
+ if (model.provider === current.provider && model.model === current.model)
114
+ return refuse("invalid-input", `${model.model} is the model ${label} uses now; its figures come from the current sample. Pick another model to compare it with.`);
115
+ if (seen.has(key))
116
+ return refuse("invalid-input", `${model.model} is picked twice. Pick each model once.`);
117
+ seen.add(key);
118
+ if (section === "voice" && model.provider !== current.provider)
119
+ return refuse("invalid-input", "A voice is chosen per provider, so voice models are compared within the template's voice provider. Pick models of that provider, or change the voice in Voice first.");
120
+ }
121
+ return { ok: true, document: template.document, version: template.version, current };
122
+ }
123
+ export async function quoteBakeoff(deps, templateId, input) {
124
+ const base = basis(deps, templateId, input.section, input.models);
125
+ if (!base.ok)
126
+ return base;
127
+ const catalogue = deps.review.catalogue.read();
128
+ const models = [];
129
+ for (const model of input.models) {
130
+ const quoted = await quoteSample(deps, templateId, {
131
+ document: withModel(base.document, input.section, model, catalogue),
132
+ sections: [input.section],
133
+ });
134
+ if (!quoted.ok)
135
+ return quoted;
136
+ models.push({
137
+ ...model,
138
+ high: quoted.value.high,
139
+ unknown: quoted.value.unknown,
140
+ onPlan: quoted.value.onPlan,
141
+ usesPlan: quoted.value.usesPlan,
142
+ problem: quoted.value.problem,
143
+ });
144
+ }
145
+ const high = models.reduce((sum, one) => sum + one.high, 0);
146
+ const usesPlan = models.some((one) => one.usesPlan);
147
+ const budget = budgetRoom(deps.db, deps.clock.now(), deps.timeZone);
148
+ return {
149
+ ok: true,
150
+ value: {
151
+ version: base.version,
152
+ section: input.section,
153
+ current: base.current,
154
+ models,
155
+ high,
156
+ unknown: models.reduce((sum, one) => sum + one.unknown, 0),
157
+ usesPlan,
158
+ budget,
159
+ fits: fitsBudget(budget, high, usesPlan),
160
+ problem: models.find((one) => one.problem !== null)?.problem ?? null,
161
+ },
162
+ };
163
+ }
164
+ // Compare: priced again first; a higher total, an unknown price not accepted or a total past
165
+ // today's budget is refused with the new quote, and nothing is spent.
166
+ export async function startBakeoff(deps, templateId, input) {
167
+ const existing = bakeoffOf(deps.db, templateId);
168
+ if (existing !== undefined && (existing.state === "queued" || existing.state === "making"))
169
+ return refuse("conflict", "A bake-off of this template is still being made. Wait for it to finish, or discard it, then compare again.");
170
+ if (queuedJob(deps.db, templateId) !== undefined ||
171
+ runningJob(deps.db)?.templateId === templateId)
172
+ return refuse("conflict", "A sample of this template is being made. Compare models once it is done: the bake-off runs on it.");
173
+ const quoted = await quoteBakeoff(deps, templateId, input);
174
+ if (!quoted.ok)
175
+ return quoted;
176
+ const quote = quoted.value;
177
+ if (quote.problem !== null)
178
+ return { ...refuse("invalid-input", quote.problem), quote };
179
+ const money = (value) => `$${value.toFixed(2)}`;
180
+ if (exceedsAcknowledged(quote.high, input.acknowledgedHigh))
181
+ return {
182
+ ...refuse("cost-changed", `Comparing now costs about ${money(quote.high)}, more than the ${money(input.acknowledgedHigh)} you saw. Press Compare again to accept the new price.`),
183
+ quote,
184
+ };
185
+ if (quote.unknown > 0 && input.acknowledgedUnknown !== true)
186
+ return {
187
+ ...refuse("cost-changed", `${String(quote.unknown)} ${quote.unknown === 1 ? "step has" : "steps have"} no known price. Press Compare again to accept that.`),
188
+ quote,
189
+ };
190
+ if (!quote.fits)
191
+ return {
192
+ ...refuse("over-budget", `${budgetWords(quote.budget, quote, "Comparing")}, or compare tomorrow.`),
193
+ quote,
194
+ };
195
+ if (existing !== undefined) {
196
+ const cleared = discardBakeoff(deps, templateId);
197
+ if (!cleared.ok)
198
+ return cleared;
199
+ }
200
+ const id = deps.uuid();
201
+ const at = deps.clock.now().toISOString();
202
+ transact(deps.db, () => {
203
+ insertBakeoff(deps.db, {
204
+ id,
205
+ templateId,
206
+ templateVersion: quote.version,
207
+ section: input.section,
208
+ // The person's figure: the quarter-and-five-cents leeway is applied once, at make time.
209
+ ackHigh: input.acknowledgedHigh,
210
+ ackUnknown: quote.unknown > 0,
211
+ createdAt: at,
212
+ }, quote.models.map((model) => ({
213
+ provider: model.provider,
214
+ model: model.model,
215
+ quotedHigh: model.high,
216
+ })));
217
+ enqueueJob(deps.db, {
218
+ templateId,
219
+ templateVersion: quote.version,
220
+ ackHigh: input.acknowledgedHigh,
221
+ kind: "bakeoff",
222
+ requestedAt: at,
223
+ });
224
+ });
225
+ return { ok: true, value: { id, version: quote.version } };
226
+ }
227
+ // Discard: every copy goes, with its files. What the copies were charged today still counts
228
+ // against the sample budget. Refused while a copy still has work going.
229
+ export function discardBakeoff(deps, templateId) {
230
+ const row = bakeoffOf(deps.db, templateId);
231
+ if (row === undefined)
232
+ return { ok: true, value: { removed: 0 } };
233
+ if (row.state === "making" ||
234
+ (row.state === "queued" && runningJob(deps.db)?.templateId === templateId))
235
+ return refuse("conflict", "The bake-off is still being made, and work already going is never stopped. Discard it once it finishes.");
236
+ let removed = 0;
237
+ for (const clone of clonesOf(deps.db, row.id)) {
238
+ if (clone.projectId === null || !projectExists(deps.db, clone.projectId))
239
+ continue;
240
+ const gone = removeHiddenProject(deps, clone.projectId);
241
+ if (!gone.ok)
242
+ return refuse("conflict", gone.reason === "running"
243
+ ? "A model's copy still has work going. Discard the bake-off once it finishes."
244
+ : "A model's copy couldn't be removed from disk. Close any program using Slopify's files, then press Discard again.");
245
+ removed += 1;
246
+ }
247
+ deleteBakeoffRow(deps.db, row.id);
248
+ // A queued bake-off job has nothing left to make.
249
+ if (row.state === "queued")
250
+ deps.db
251
+ .prepare("DELETE FROM template_sample_jobs WHERE template_id=? AND state='queued' AND kind='bakeoff'")
252
+ .run(templateId);
253
+ return { ok: true, value: { removed } };
254
+ }
255
+ const openingChars = 1200;
256
+ function madeOn(deps, projectId, section) {
257
+ const revisionId = currentRevisionId(deps.db, projectId);
258
+ if (revisionId === undefined)
259
+ return { outputs: [], article: null };
260
+ const view = getRevisionView({ ...deps, ids: { next: () => "" }, clock: { now: () => new Date(), sleep: async () => { } } }, projectId, revisionId);
261
+ if (view === undefined)
262
+ return { outputs: [], article: null };
263
+ const outputs = view.outputs.flatMap((row) => isReadyOutput(row) && sectionOfWork(row.workKey) === section
264
+ ? [
265
+ {
266
+ role: row.output.role,
267
+ url: `/files/${encodeURIComponent(projectId)}/revisions/${encodeURIComponent(revisionId)}/${encodeURIComponent(row.recordId)}`,
268
+ name: row.output.path.split("/").pop() ?? row.output.path,
269
+ },
270
+ ]
271
+ : []);
272
+ return {
273
+ outputs: section === "script" ? [] : outputs,
274
+ article: section === "script" && view.articleMarkdown !== null
275
+ ? view.articleMarkdown.slice(0, openingChars)
276
+ : null,
277
+ };
278
+ }
279
+ const secondsOf = (from, to) => from === null || to === null ? null : Math.max(0, (Date.parse(to) - Date.parse(from)) / 1000);
280
+ // The bake-off as the studio shows it: the model in use now (from the template's current
281
+ // sample), then each other model's copy, with what it made, cost and took.
282
+ export function bakeoffView(deps, templateId) {
283
+ const row = bakeoffOf(deps.db, templateId);
284
+ if (row === undefined)
285
+ return null;
286
+ const head = templateById(deps.db, templateId);
287
+ const version = templateById(deps.db, templateId, row.templateVersion);
288
+ const current = version === undefined ? undefined : sectionModel(version.document, row.section);
289
+ const sample = sampleOf(deps.db, templateId);
290
+ const entries = [];
291
+ if (current !== undefined && sample !== undefined && projectExists(deps.db, sample.projectId)) {
292
+ const cost = runCostOf(deps.db, sample.projectId);
293
+ const used = cost.byModel.filter((one) => one.provider === current.provider && one.model === current.model);
294
+ const wall = cost.byStage.find((one) => one.stage === bakeoffStage[row.section])?.wallMs ?? null;
295
+ entries.push({
296
+ ...current,
297
+ slot: null,
298
+ state: "current",
299
+ quotedHigh: null,
300
+ cost: used.length === 0 ? null : used.reduce((sum, one) => sum + one.cost, 0),
301
+ seconds: wall === null ? null : wall / 1000,
302
+ failure: null,
303
+ ...madeOn(deps, sample.projectId, row.section),
304
+ });
305
+ }
306
+ for (const clone of clonesOf(deps.db, row.id)) {
307
+ const exists = clone.projectId !== null && projectExists(deps.db, clone.projectId);
308
+ entries.push({
309
+ provider: clone.provider,
310
+ model: clone.model,
311
+ slot: clone.slot,
312
+ state: clone.state,
313
+ quotedHigh: clone.quotedHigh,
314
+ cost: exists && clone.projectId !== null && clone.state !== "queued"
315
+ ? runCostOf(deps.db, clone.projectId).cost
316
+ : null,
317
+ seconds: secondsOf(clone.startedAt, clone.finishedAt),
318
+ failure: clone.failure,
319
+ ...(exists && clone.projectId !== null && clone.state === "done"
320
+ ? madeOn(deps, clone.projectId, row.section)
321
+ : { outputs: [], article: null }),
322
+ });
323
+ }
324
+ return {
325
+ id: row.id,
326
+ section: row.section,
327
+ version: row.templateVersion,
328
+ stale: head !== undefined && head.version !== row.templateVersion,
329
+ state: row.state,
330
+ sentence: row.sentence,
331
+ entries,
332
+ };
333
+ }
@@ -0,0 +1,103 @@
1
+ import { z } from "zod";
2
+ import { dailySampleBudget, dailySampleBudgetKey } from "../designs/budget.js";
3
+ import { readJsonSetting, writeSetting } from "../settings/repo.js";
4
+ // Settings → Template studio: the most the studio's samples may spend in one day, in dollars,
5
+ // and whether they may use a CLI plan at all. Plan usage has no dollar price, so the budget
6
+ // can't hold it back: samples that run on a CLI plan wait until the person turns that on. By
7
+ // default ($2 a day, plans off) samples spend at most $2 a day and never a plan; at $0 nothing
8
+ // spends money. A sample that would go over either waits, saying so, until the next day, a
9
+ // higher budget or plans allowed. The designed pieces' samples have their own daily budget
10
+ // (`designs/budget.ts`), set here too, and follow the same plan switch.
11
+ export const dailyBudgetKey = "templateStudio.dailyBudget";
12
+ export const samplesUsePlansKey = "templateStudio.samplesUsePlans";
13
+ export const defaultDailyBudget = 2;
14
+ export const dailyBudgetMax = 1000;
15
+ // Each field left out keeps what is stored.
16
+ export const studioSettingsSchema = z
17
+ .object({
18
+ dailyBudget: z.number().finite().min(0).max(dailyBudgetMax).optional(),
19
+ samplesUsePlans: z.boolean().optional(),
20
+ designDailyBudget: z.number().finite().min(0).max(dailyBudgetMax).optional(),
21
+ })
22
+ .strict()
23
+ .readonly();
24
+ export function readStudioSettings(db) {
25
+ return {
26
+ dailyBudget: readJsonSetting(db, dailyBudgetKey, z.number().finite().min(0).max(dailyBudgetMax), defaultDailyBudget),
27
+ samplesUsePlans: readJsonSetting(db, samplesUsePlansKey, z.boolean(), false),
28
+ designDailyBudget: dailySampleBudget(db),
29
+ };
30
+ }
31
+ export function writeStudioSettings(db, settings) {
32
+ const cents = (dollars) => JSON.stringify(Math.round(dollars * 100) / 100);
33
+ if (settings.dailyBudget !== undefined)
34
+ writeSetting(db, dailyBudgetKey, cents(settings.dailyBudget));
35
+ if (settings.designDailyBudget !== undefined)
36
+ writeSetting(db, dailySampleBudgetKey, cents(settings.designDailyBudget));
37
+ if (settings.samplesUsePlans !== undefined)
38
+ writeSetting(db, samplesUsePlansKey, JSON.stringify(settings.samplesUsePlans));
39
+ return readStudioSettings(db);
40
+ }
41
+ // The instant today began on this computer's clock, in `timeZone`.
42
+ export function localMidnight(now, timeZone) {
43
+ const day = Temporal.Instant.fromEpochMilliseconds(now.valueOf())
44
+ .toZonedDateTimeISO(timeZone)
45
+ .startOfDay();
46
+ return new Date(day.epochMilliseconds);
47
+ }
48
+ export function systemTimeZone() {
49
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
50
+ }
51
+ // What the studio's samples were charged since local midnight: the priced calls of every
52
+ // template's sample project and every bake-off copy, billed per use (a CLI plan's calls add
53
+ // nothing), and what such a project spent before it was removed. Other projects' calls never
54
+ // count.
55
+ export function spentToday(db, now, timeZone = systemTimeZone()) {
56
+ const since = localMidnight(now, timeZone).toISOString();
57
+ const row = db
58
+ .prepare(`SELECT COALESCE((SELECT SUM(u.cost) FROM provider_usage u
59
+ WHERE u.project_id IN (SELECT project_id FROM template_hidden_projects)
60
+ AND u.on_plan = 0 AND u.created_at >= ?), 0)
61
+ + COALESCE((SELECT SUM(cost) FROM template_studio_spend WHERE created_at >= ?), 0) AS spent`)
62
+ .get(since, since);
63
+ return z.object({ spent: z.number() }).parse(row).spent;
64
+ }
65
+ // Today's sample calls billed per use whose price no one knows: they add nothing to `spentToday`,
66
+ // so they are counted and said beside it rather than read as free.
67
+ export function unpricedToday(db, now, timeZone = systemTimeZone()) {
68
+ const row = db
69
+ .prepare(`SELECT count(*) AS n FROM provider_usage u
70
+ WHERE u.project_id IN (SELECT project_id FROM template_hidden_projects)
71
+ AND u.on_plan = 0 AND u.cost IS NULL AND u.created_at >= ?`)
72
+ .get(localMidnight(now, timeZone).toISOString());
73
+ return z.object({ n: z.number() }).parse(row).n;
74
+ }
75
+ export function budgetRoom(db, now, timeZone = systemTimeZone()) {
76
+ const { dailyBudget, samplesUsePlans } = readStudioSettings(db);
77
+ const spent = spentToday(db, now, timeZone);
78
+ return {
79
+ budget: dailyBudget,
80
+ spent,
81
+ unpriced: unpricedToday(db, now, timeZone),
82
+ left: Math.max(0, dailyBudget - spent),
83
+ plans: samplesUsePlans,
84
+ };
85
+ }
86
+ // Whether a sample of about `high` dollars, `usesPlan` when a step runs on a CLI plan, may
87
+ // start on what is left of today's budget.
88
+ export function fitsBudget(room, high, usesPlan = false) {
89
+ if (usesPlan && !room.plans)
90
+ return false;
91
+ return high <= room.left + 1e-9;
92
+ }
93
+ const money = (value) => `$${value.toFixed(2)}`;
94
+ // Why a sample that doesn't fit (`fitsBudget`) waits, and what lets it start, as one sentence
95
+ // (no full stop) about `subject`: "This sample", "Comparing".
96
+ export function budgetWords(room, cost, subject) {
97
+ if (cost.usesPlan && !room.plans)
98
+ return `${subject} runs on your CLI plan, and samples may not use CLI plans. Turn on Samples may use my CLI plans in Settings → Template studio`;
99
+ const unpriced = room.unpriced === 0
100
+ ? ""
101
+ : ` (samples spent ${money(room.spent)} today plus ${String(room.unpriced)} ${room.unpriced === 1 ? "call" : "calls"} with no known price)`;
102
+ return `${subject} costs about ${money(cost.high)} and ${money(room.left)} of today's ${money(room.budget)} sample budget is left${unpriced}. Raise the budget in Settings → Template studio`;
103
+ }