@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
@@ -33,6 +33,26 @@ export function getRevisionView(deps, projectId, revisionId) {
33
33
  articleMarkdown: resolvedArticleMarkdown(deps, revision, outputs),
34
34
  };
35
35
  }
36
+ export function isReadyOutput(row, match = {}) {
37
+ return (row.selected &&
38
+ row.available &&
39
+ row.state === "ready" &&
40
+ (match.role === undefined || row.output.role === match.role) &&
41
+ (match.workKey === undefined || row.workKey === match.workKey));
42
+ }
43
+ export function readyOutput(view, match = {}) {
44
+ return view.outputs.find((row) => isReadyOutput(row, match));
45
+ }
46
+ // A piece a step can read: selected, its file (if any) on disk and done, under `key` when given.
47
+ export function isDonePiece(row, key) {
48
+ return (row.selected &&
49
+ row.available &&
50
+ row.piece.state === "done" &&
51
+ (key === undefined || row.key === key));
52
+ }
53
+ export function donePiece(view, key) {
54
+ return view.pieces.find((row) => isDonePiece(row, key));
55
+ }
36
56
  export function resolvedArticleMarkdown(deps, revision, outputs) {
37
57
  if (revision.content.articleEdited === true && revision.content.articleMarkdown !== undefined)
38
58
  return revision.content.articleMarkdown;
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { causedBy } from "../../kernel/errors.js";
3
+ import { notTemplateSample } from "../template-studio/repo.js";
3
4
  // Living within a CLI's plan limits. When a CLI says its allowance is used up, the time it
4
5
  // returns is stored per account; every call to that CLI - from any project, and after a
5
6
  // restart - waits for it instead of failing. Other providers are untouched, so a project
@@ -99,13 +100,15 @@ export function createLimitGate(deps) {
99
100
  // At start, a stage that was waiting on a plan limit when the app stopped was marked
100
101
  // interrupted like any other (main.ts markInterruptedStages). Those projects are resumed so
101
102
  // the wait carries on by itself - their calls meet the stored reset and wait again. A project
102
- // paused or cancelled meanwhile is left as it is.
103
+ // paused or cancelled meanwhile is left as it is. A template studio sample is the studio's own
104
+ // recovery's to resume, which checks the sample budget and its plans switch first.
103
105
  export async function resumeAfterRestart(db, resume, log) {
104
106
  const projects = db
105
107
  .prepare(`SELECT DISTINCT w.project_id AS id FROM plan_limit_waiters w
106
108
  JOIN stages s ON s.project_id = w.project_id AND s.kind = w.stage
107
109
  WHERE s.state = 'failed' AND s.failure_reason = 'interrupted'
108
- AND NOT EXISTS (SELECT 1 FROM project_controls c WHERE c.project_id = w.project_id AND c.paused = 1)`)
110
+ AND NOT EXISTS (SELECT 1 FROM project_controls c WHERE c.project_id = w.project_id AND c.paused = 1)
111
+ AND ${notTemplateSample("w", "project_id")}`)
109
112
  .all()
110
113
  .map((row) => z.string().parse(row.id));
111
114
  db.prepare("DELETE FROM plan_limit_waiters").run();
@@ -9,15 +9,17 @@ export function createUsageMeter(deps) {
9
9
  record: (call) => {
10
10
  const priced = priceCall(call, deps.catalogue());
11
11
  const at = deps.clock.now().toISOString();
12
+ const usageId = deps.ids.next();
12
13
  deps.db
13
14
  .prepare(`INSERT INTO provider_usage (id, project_id, stage, kind, provider, model, tokens_in, tokens_out, tokens_cached, characters, images, seconds, size, quality, wall_ms, on_plan, cost, api_model, api_cost, price_json, created_at)
14
15
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
15
- .run(deps.ids.next(), call.projectId, call.stage, call.kind, call.provider, call.model, call.tokensIn ?? null, call.tokensOut ?? null, call.cachedTokens ?? null, call.characters ?? null, call.images ?? null, call.seconds ?? null, call.size ?? null, call.quality ?? null, Math.round(call.wallMs), priced.onPlan ? 1 : 0, priced.cost, priced.apiModel, priced.apiCost, priced.price === null ? null : JSON.stringify(priced.price), at);
16
+ .run(usageId, call.projectId, call.stage, call.kind, call.provider, call.model, call.tokensIn ?? null, call.tokensOut ?? null, call.cachedTokens ?? null, call.characters ?? null, call.images ?? null, call.seconds ?? null, call.size ?? null, call.quality ?? null, Math.round(call.wallMs), priced.onPlan ? 1 : 0, priced.cost, priced.apiModel, priced.apiCost, priced.price === null ? null : JSON.stringify(priced.price), at);
17
+ // The reading names its call, so the Run cost tab knows when the call ran and its tokens.
16
18
  const account = planAccountOf(call.provider);
17
19
  if (account !== undefined && call.limits !== undefined)
18
20
  deps.db
19
21
  .prepare("INSERT INTO plan_limit_readings (id, project_id, account, reading_json, created_at) VALUES (?, ?, ?, ?, ?)")
20
- .run(deps.ids.next(), call.projectId, account, JSON.stringify(call.limits), at);
22
+ .run(deps.ids.next(), call.projectId, account, JSON.stringify({ ...call.limits, usageId }), at);
21
23
  },
22
24
  };
23
25
  }
@@ -40,8 +42,13 @@ export function createStandaloneMeter(deps) {
40
42
  }
41
43
  // A schedule counts under its template's channel; one whose template is gone, like a channel
42
44
  // this install doesn't have, under the default channel.
45
+ // A design counts under its own channel; one deleted since, under the default channel.
43
46
  function channelOf(db, owner) {
44
- return owner.kind === "schedule"
45
- ? resolveChannelId(db, scheduleChannel(db, owner.id)?.id)
46
- : resolveChannelId(db, owner.id);
47
+ if (owner.kind === "schedule")
48
+ return resolveChannelId(db, scheduleChannel(db, owner.id)?.id);
49
+ if (owner.kind === "design") {
50
+ const row = db.prepare("SELECT channel_id FROM designs WHERE id=?").get(owner.id);
51
+ return resolveChannelId(db, row === undefined ? undefined : String(row.channel_id));
52
+ }
53
+ return resolveChannelId(db, owner.id);
47
54
  }
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { stageKinds } from "../../kernel/pipeline.js";
3
- import { planAccountNames, planAccountOf, planAccounts, } from "../../kernel/ports/plan-limits.js";
3
+ import { planAccountNames, planAccounts, } from "../../kernel/ports/plan-limits.js";
4
+ import { isSampleStandIn } from "../settings/model.js";
5
+ import { planUses } from "./plan-readings.js";
4
6
  import { projectTiming } from "./timing.js";
5
7
  const usageRow = z.object({
6
8
  stage: z.enum(stageKinds),
@@ -19,15 +21,6 @@ const usageRow = z.object({
19
21
  api_cost: z.number().nullable(),
20
22
  price_json: z.string().nullable(),
21
23
  });
22
- const windowSchema = z.object({
23
- kind: z.enum(["five_hour", "weekly", "other"]),
24
- usedPercent: z.number(),
25
- resetsAt: z.string().nullable(),
26
- });
27
- const readingSchema = z.object({
28
- before: z.array(windowSchema).optional(),
29
- after: z.array(windowSchema).optional(),
30
- });
31
24
  const emptyTotals = {
32
25
  tokensIn: 0,
33
26
  tokensOut: 0,
@@ -45,11 +38,13 @@ const emptyLine = {
45
38
  };
46
39
  function add(into, row) {
47
40
  const onPlan = row.on_plan === 1;
41
+ // The bundled samples' calls were stored with no price; their stand-ins are free.
42
+ const cost = row.cost ?? (isSampleStandIn(row.provider) ? 0 : null);
48
43
  return {
49
44
  ...into,
50
45
  calls: into.calls + 1,
51
- cost: into.cost + (row.cost ?? 0),
52
- unpriced: into.unpriced + (row.cost === null ? 1 : 0),
46
+ cost: into.cost + (cost ?? 0),
47
+ unpriced: into.unpriced + (cost === null ? 1 : 0),
53
48
  apiEquivalent: onPlan ? (into.apiEquivalent ?? 0) + (row.api_cost ?? 0) : into.apiEquivalent,
54
49
  apiUnpriced: into.apiUnpriced + (onPlan && row.api_cost === null ? 1 : 0),
55
50
  tokensIn: into.tokensIn + (row.tokens_in ?? 0),
@@ -95,7 +90,7 @@ export function runCostOf(db, projectId, now = Date.now()) {
95
90
  for (const [stage, wallMs] of walls)
96
91
  if (!stages.has(stage))
97
92
  stages.set(stage, { stage, wallMs, ...emptyLine, ...emptyTotals });
98
- const plans = planUses(db, projectId, rows);
93
+ const plans = planUses(db, projectId, rows.map((row) => row.provider));
99
94
  return {
100
95
  currency: "USD",
101
96
  ...total,
@@ -128,67 +123,6 @@ function catalogueOf(json) {
128
123
  const parsed = z.object({ catalogue: z.string() }).safeParse(JSON.parse(json));
129
124
  return parsed.success ? parsed.data.catalogue : null;
130
125
  }
131
- // Two readings belong to the same window when their resets agree to within a few minutes;
132
- // a reset moving on means the window rolled over between them.
133
- const sameWindowMs = 5 * 60_000;
134
- export function windowUse(readings) {
135
- const byKind = new Map();
136
- for (const reading of readings)
137
- for (const window of reading)
138
- byKind.set(window.kind, [...(byKind.get(window.kind) ?? []), window]);
139
- const uses = [];
140
- for (const [kind, seen] of byKind) {
141
- let used = 0;
142
- for (let at = 1; at < seen.length; at += 1) {
143
- const previous = seen[at - 1];
144
- const current = seen[at];
145
- if (previous === undefined || current === undefined)
146
- continue;
147
- const same = previous.resetsAt === current.resetsAt ||
148
- (previous.resetsAt !== null &&
149
- current.resetsAt !== null &&
150
- Math.abs(Date.parse(previous.resetsAt) - Date.parse(current.resetsAt)) < sameWindowMs);
151
- used += same ? Math.max(0, current.usedPercent - previous.usedPercent) : current.usedPercent;
152
- }
153
- const last = seen.at(-1);
154
- if (last !== undefined)
155
- uses.push({ kind, usedPercent: used, nowPercent: last.usedPercent, resetsAt: last.resetsAt });
156
- }
157
- const order = ["five_hour", "weekly", "other"];
158
- return uses.toSorted((left, right) => order.indexOf(left.kind) - order.indexOf(right.kind));
159
- }
160
- function planUses(db, projectId, rows) {
161
- const readings = db
162
- .prepare("SELECT account, reading_json FROM plan_limit_readings WHERE project_id = ? ORDER BY created_at, rowid")
163
- .all(projectId)
164
- .map((row) => z.object({ account: z.string(), reading_json: z.string() }).parse(row));
165
- return planAccounts.flatMap((account) => {
166
- const calls = rows.filter((row) => planAccountOf(row.provider) === account).length;
167
- if (calls === 0)
168
- return [];
169
- const sequence = [];
170
- for (const row of readings) {
171
- if (row.account !== account)
172
- continue;
173
- const parsed = readingSchema.safeParse(JSON.parse(row.reading_json));
174
- if (!parsed.success)
175
- continue;
176
- if (parsed.data.before?.length)
177
- sequence.push(parsed.data.before);
178
- if (parsed.data.after?.length)
179
- sequence.push(parsed.data.after);
180
- }
181
- return [
182
- {
183
- account,
184
- name: planAccountNames[account],
185
- calls,
186
- reported: sequence.length > 0,
187
- windows: windowUse(sequence),
188
- },
189
- ];
190
- });
191
- }
192
126
  export function limitWaitsOf(db, projectId) {
193
127
  return limitWaitRows(db, projectId).map((row) => row.wait);
194
128
  }
@@ -0,0 +1,127 @@
1
+ import { z } from "zod";
2
+ import { planAccountNames, planAccountOf, planAccounts, } from "../../kernel/ports/plan-limits.js";
3
+ import { beforeGapMs, windowUse, } from "./plan-use.js";
4
+ const windowSchema = z.object({
5
+ kind: z.enum(["five_hour", "weekly", "other"]),
6
+ usedPercent: z.number(),
7
+ resetsAt: z.string().nullable(),
8
+ });
9
+ // `usageId` names the call's provider_usage row; readings stored before it was kept lack it.
10
+ export const readingSchema = z.object({
11
+ before: z.array(windowSchema).optional(),
12
+ after: z.array(windowSchema).optional(),
13
+ usageId: z.string().optional(),
14
+ });
15
+ const callRow = z.object({
16
+ id: z.string(),
17
+ provider: z.string(),
18
+ tokens_in: z.number().nullable(),
19
+ tokens_out: z.number().nullable(),
20
+ wall_ms: z.number(),
21
+ created_at: z.string(),
22
+ });
23
+ const readingRow = z.object({
24
+ id: z.string(),
25
+ account: z.string().nullable(),
26
+ reading_json: z.string().nullable(),
27
+ created_at: z.string(),
28
+ });
29
+ function callOf(row) {
30
+ const endMs = Date.parse(row.created_at);
31
+ return {
32
+ id: row.id,
33
+ startMs: endMs - row.wall_ms,
34
+ endMs,
35
+ tokens: row.tokens_in === null && row.tokens_out === null
36
+ ? null
37
+ : (row.tokens_in ?? 0) + (row.tokens_out ?? 0),
38
+ };
39
+ }
40
+ function parseReading(json) {
41
+ if (json === null)
42
+ return undefined;
43
+ const parsed = readingSchema.safeParse(JSON.parse(json));
44
+ return parsed.success ? parsed.data : undefined;
45
+ }
46
+ function readingOf(stored, usageId, createdAt, calls) {
47
+ const call = usageId === undefined ? undefined : calls.get(usageId);
48
+ const at = Date.parse(createdAt);
49
+ return {
50
+ usageId: call === undefined ? null : call.id,
51
+ startMs: call?.startMs ?? at,
52
+ endMs: call?.endMs ?? at,
53
+ tokens: call?.tokens ?? null,
54
+ before: stored.before,
55
+ after: stored.after,
56
+ };
57
+ }
58
+ export function planUses(db, projectId, providers) {
59
+ const ownCalls = db
60
+ .prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at FROM provider_usage WHERE project_id = ?")
61
+ .all(projectId)
62
+ .map((row) => callRow.parse(row));
63
+ const ownReadings = db
64
+ .prepare("SELECT id, account, reading_json, created_at FROM plan_limit_readings WHERE project_id = ? ORDER BY created_at, rowid")
65
+ .all(projectId)
66
+ .map((row) => readingRow.parse(row));
67
+ return planAccounts.flatMap((account) => {
68
+ const calls = providers.filter((provider) => planAccountOf(provider) === account).length;
69
+ if (calls === 0)
70
+ return [];
71
+ const mine = ownReadings.filter((row) => row.account === account);
72
+ const activity = accountActivity(db, account, ownCalls, projectId);
73
+ const own = mine.flatMap((row) => {
74
+ const stored = parseReading(row.reading_json);
75
+ if (stored === undefined || (!stored.before?.length && !stored.after?.length))
76
+ return [];
77
+ return [readingOf(stored, stored.usageId, row.created_at, activity.byId)];
78
+ });
79
+ return [
80
+ {
81
+ account,
82
+ name: planAccountNames[account],
83
+ calls,
84
+ reported: own.length > 0,
85
+ windows: windowUse(own, {
86
+ calls: [...activity.byId.values()],
87
+ readings: activity.readings,
88
+ }),
89
+ },
90
+ ];
91
+ });
92
+ }
93
+ // Every call on the account from when this project's first call began (less the gap an
94
+ // after-only call may look back), from any project or schedule.
95
+ function accountActivity(db, account, ownCalls, projectId) {
96
+ const starts = ownCalls.map((row) => callOf(row).startMs);
97
+ const byId = new Map();
98
+ if (starts.length === 0)
99
+ return { byId, readings: [] };
100
+ const since = new Date(Math.min(...starts) - beforeGapMs).toISOString();
101
+ const projectCalls = db
102
+ .prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at FROM provider_usage WHERE created_at >= ?")
103
+ .all(since)
104
+ .map((row) => callRow.parse(row));
105
+ const standalone = db
106
+ .prepare("SELECT id, provider, tokens_in, tokens_out, wall_ms, created_at, account, reading_json FROM standalone_usage WHERE created_at >= ?")
107
+ .all(since);
108
+ for (const row of [...projectCalls, ...standalone.map((raw) => callRow.parse(raw))])
109
+ if (planAccountOf(row.provider) === account)
110
+ byId.set(row.id, callOf(row));
111
+ const readings = [];
112
+ for (const raw of db
113
+ .prepare("SELECT id, account, reading_json, created_at FROM plan_limit_readings WHERE account = ? AND project_id != ? AND created_at >= ?")
114
+ .all(account, projectId, since)) {
115
+ const row = readingRow.parse(raw);
116
+ const stored = parseReading(row.reading_json);
117
+ if (stored !== undefined)
118
+ readings.push(readingOf(stored, stored.usageId, row.created_at, byId));
119
+ }
120
+ for (const raw of standalone) {
121
+ const row = readingRow.parse(raw);
122
+ const stored = row.account === account ? parseReading(row.reading_json) : undefined;
123
+ if (stored !== undefined)
124
+ readings.push(readingOf(stored, row.id, row.created_at, byId));
125
+ }
126
+ return { byId, readings };
127
+ }
@@ -0,0 +1,169 @@
1
+ // How much of a CLI plan's windows a project's calls took. A plan's readings are account-wide:
2
+ // they move with every use of the account, this project's or not. So a call is charged only
3
+ // its own before→after rise, and only when nothing else on the account ran during it; calls
4
+ // that overlapped split their rise by tokens; anything else is said to be shared, not guessed.
5
+ // "measured": every call's rise was its own. "split": some overlapped and split their rise by
6
+ // tokens. "shared": some calls' share could not be told apart from other use. "approximate":
7
+ // stored before each call's own readings were kept, so only the account's rise is known.
8
+ export const windowBases = ["measured", "split", "shared", "approximate"];
9
+ // An after-only call (Claude Code reports its windows once) takes the account's reading just
10
+ // before it as its "before"; one older than this says nothing about the call alone.
11
+ export const beforeGapMs = 15 * 60_000;
12
+ // A stated reset that moves by less than this is drift, not a new window.
13
+ const resetDriftMs = 60 * 60_000;
14
+ const kindOrder = ["five_hour", "weekly", "other"];
15
+ const windowOf = (windows, kind) => windows?.find((window) => window.kind === kind);
16
+ export function windowUse(own, account = { calls: [], readings: [] }) {
17
+ const kinds = new Set();
18
+ for (const reading of own)
19
+ for (const window of [...(reading.before ?? []), ...(reading.after ?? [])])
20
+ kinds.add(window.kind);
21
+ return kindOrder.flatMap((kind) => {
22
+ if (!kinds.has(kind))
23
+ return [];
24
+ const use = kindUse(kind, own, account);
25
+ return use === undefined ? [] : [use];
26
+ });
27
+ }
28
+ function kindUse(kind, own, account) {
29
+ const mine = own.filter((reading) => windowOf(reading.before, kind) !== undefined || windowOf(reading.after, kind) !== undefined);
30
+ const legacy = mine.some((reading) => reading.usageId === null);
31
+ const calls = new Map();
32
+ for (const call of account.calls)
33
+ calls.set(call.id, call);
34
+ for (const reading of own)
35
+ if (reading.usageId !== null)
36
+ calls.set(reading.usageId, {
37
+ id: reading.usageId,
38
+ startMs: reading.startMs,
39
+ endMs: reading.endMs,
40
+ tokens: reading.tokens,
41
+ });
42
+ const everyReading = [...own, ...account.readings];
43
+ const points = [];
44
+ let used = 0;
45
+ let measured = 0;
46
+ let split = 0;
47
+ let shared = 0;
48
+ for (const reading of mine) {
49
+ const before = windowOf(reading.before, kind);
50
+ const after = windowOf(reading.after, kind);
51
+ if (before !== undefined)
52
+ points.push({ atMs: reading.startMs, window: before });
53
+ if (after !== undefined)
54
+ points.push({ atMs: reading.endMs, window: after });
55
+ if (legacy || reading.usageId === null)
56
+ continue;
57
+ const start = before === undefined ? pointBefore(kind, reading, everyReading) : undefined;
58
+ if (start !== undefined)
59
+ points.push(start);
60
+ const from = before ?? start?.window;
61
+ const spanStart = before === undefined ? start?.atMs : reading.startMs;
62
+ if (from === undefined || after === undefined || spanStart === undefined) {
63
+ shared += 1;
64
+ continue;
65
+ }
66
+ const rise = rolledOver(from, after, reading.endMs)
67
+ ? after.usedPercent
68
+ : after.usedPercent - from.usedPercent;
69
+ const beside = [...calls.values()].filter((call) => call.id !== reading.usageId && call.endMs > spanStart && call.startMs < reading.endMs);
70
+ if (beside.length === 0) {
71
+ used += rise;
72
+ measured += 1;
73
+ continue;
74
+ }
75
+ const tokens = reading.tokens ?? 0;
76
+ const others = beside.map((call) => call.tokens);
77
+ if (tokens <= 0 || others.some((count) => count === null)) {
78
+ shared += 1;
79
+ continue;
80
+ }
81
+ const total = others.reduce((sum, count) => sum + (count ?? 0), tokens);
82
+ used += (rise * tokens) / total;
83
+ split += 1;
84
+ }
85
+ const sorted = points.toSorted((left, right) => left.atMs - right.atMs);
86
+ const first = sorted[0];
87
+ const last = sorted.at(-1);
88
+ if (first === undefined || last === undefined)
89
+ return undefined;
90
+ const { rise: accountPercent, segments } = accountRise(sorted);
91
+ const share = legacy
92
+ ? accountPercent
93
+ : measured + split === 0
94
+ ? null
95
+ : Math.min(Math.max(0, used), accountPercent);
96
+ return {
97
+ kind,
98
+ usedPercent: share,
99
+ basis: legacy ? "approximate" : shared > 0 ? "shared" : split > 0 ? "split" : "measured",
100
+ sharedCalls: legacy ? 0 : shared,
101
+ fromPercent: first.window.usedPercent,
102
+ accountPercent,
103
+ rolledOver: segments > 1,
104
+ nowPercent: last.window.usedPercent,
105
+ resetsAt: last.window.resetsAt,
106
+ };
107
+ }
108
+ // The latest reading on the account at or before the call began, from any call but itself.
109
+ function pointBefore(kind, call, readings) {
110
+ let found;
111
+ for (const reading of readings) {
112
+ if (reading.usageId !== null && reading.usageId === call.usageId)
113
+ continue;
114
+ const candidates = [];
115
+ const before = windowOf(reading.before, kind);
116
+ const after = windowOf(reading.after, kind);
117
+ if (before !== undefined)
118
+ candidates.push({ atMs: reading.startMs, window: before });
119
+ if (after !== undefined)
120
+ candidates.push({ atMs: reading.endMs, window: after });
121
+ for (const point of candidates)
122
+ if (point.atMs <= call.startMs &&
123
+ point.atMs >= call.startMs - beforeGapMs &&
124
+ (found === undefined || point.atMs > found.atMs))
125
+ found = point;
126
+ }
127
+ return found;
128
+ }
129
+ // Within one call the window reset when the reading dropped or the stated reset passed.
130
+ function rolledOver(before, after, endMs) {
131
+ if (after.usedPercent < before.usedPercent)
132
+ return true;
133
+ return before.resetsAt !== null && Date.parse(before.resetsAt) <= endMs;
134
+ }
135
+ // Across many calls' readings a drop alone is not a reset: parallel calls report out of
136
+ // order. A new window needs the stated reset to have passed, or a drop with the reset moved
137
+ // on by more than drift. The rise is counted per window from its readings' range, at most 100.
138
+ function accountRise(points) {
139
+ let segments = 1;
140
+ let rise = 0;
141
+ let base = points[0]?.window.usedPercent ?? 0;
142
+ let peak = base;
143
+ for (let at = 1; at < points.length; at += 1) {
144
+ const previous = points[at - 1];
145
+ const current = points[at];
146
+ if (previous === undefined || current === undefined)
147
+ continue;
148
+ if (newWindow(previous, current)) {
149
+ rise += Math.max(0, peak - base);
150
+ segments += 1;
151
+ base = 0;
152
+ peak = current.window.usedPercent;
153
+ continue;
154
+ }
155
+ peak = Math.max(peak, current.window.usedPercent);
156
+ }
157
+ rise += Math.max(0, peak - base);
158
+ return { rise: Math.min(100, rise), segments };
159
+ }
160
+ function newWindow(previous, current) {
161
+ const passed = previous.window.resetsAt !== null && Date.parse(previous.window.resetsAt) <= current.atMs;
162
+ if (passed)
163
+ return true;
164
+ if (current.window.usedPercent >= previous.window.usedPercent)
165
+ return false;
166
+ return (previous.window.resetsAt !== null &&
167
+ current.window.resetsAt !== null &&
168
+ Date.parse(current.window.resetsAt) - Date.parse(previous.window.resetsAt) > resetDriftMs);
169
+ }
@@ -1,4 +1,4 @@
1
- import { isLocalCliProvider, systemVoiceProvider } from "../settings/model.js";
1
+ import { isLocalCliProvider, isSampleStandIn, systemVoiceProvider } from "../settings/model.js";
2
2
  // Per million tokens; cached input at its own rate when the catalogue gives one.
3
3
  export function tokenCost(rates, tokensIn, tokensOut, cachedTokens = 0) {
4
4
  if (rates.inputPerMillionTokens === undefined || rates.outputPerMillionTokens === undefined)
@@ -57,8 +57,8 @@ export function priceCall(call, catalogue) {
57
57
  price: api === undefined ? null : { catalogue: date, model: api.id, ...api.pricing },
58
58
  };
59
59
  }
60
- // The computer's own voice costs nothing.
61
- if (call.provider === systemVoiceProvider)
60
+ // The computer's own voice and the samples' stand-ins cost nothing.
61
+ if (call.provider === systemVoiceProvider || isSampleStandIn(call.provider))
62
62
  return { onPlan: false, cost: 0, apiModel: null, apiCost: null, price: null };
63
63
  const family = call.kind === "video" ? "image" : call.kind;
64
64
  const entry = catalogue[family].find((model) => model.provider === call.provider && model.id === call.model);
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { planAccountNames, planAccounts, } from "../../kernel/ports/plan-limits.js";
3
3
  import { defaultChannelId } from "../channels/model.js";
4
4
  import { projectChannels } from "../channels/repo.js";
5
+ import { notTemplateSample, templateSampleProjects } from "../template-studio/repo.js";
5
6
  const usageRow = z.object({
6
7
  project_id: z.string(),
7
8
  on_plan: z.number(),
@@ -42,13 +43,30 @@ export function weekSummary(db, since, channelId) {
42
43
  if (row.on_plan === 1)
43
44
  apiEquivalent = (apiEquivalent ?? 0) + (row.api_cost ?? 0);
44
45
  };
46
+ let studioCost = 0;
47
+ const hidden = templateSampleProjects(db);
45
48
  for (const raw of db
46
49
  .prepare("SELECT project_id, on_plan, cost, api_cost FROM provider_usage WHERE created_at >= ?")
47
50
  .all(since)) {
48
51
  const row = usageRow.parse(raw);
49
- if (projectInScope(row.project_id))
50
- count(row);
52
+ if (hidden.has(row.project_id)) {
53
+ if (channelId !== undefined)
54
+ continue;
55
+ studioCost += row.cost ?? 0;
56
+ }
57
+ else if (!projectInScope(row.project_id))
58
+ continue;
59
+ count(row);
51
60
  }
61
+ // A sample or bake-off copy removed since keeps what it was charged here.
62
+ if (channelId === undefined)
63
+ for (const raw of db
64
+ .prepare("SELECT cost FROM template_studio_spend WHERE created_at >= ?")
65
+ .all(since)) {
66
+ const spent = z.object({ cost: z.number() }).parse(raw).cost;
67
+ studioCost += spent;
68
+ count({ channel_id: "", on_plan: 0, cost: spent, api_cost: null });
69
+ }
52
70
  for (const raw of db
53
71
  .prepare("SELECT channel_id, on_plan, cost, api_cost FROM standalone_usage WHERE created_at >= ?")
54
72
  .all(since)) {
@@ -59,11 +77,21 @@ export function weekSummary(db, since, channelId) {
59
77
  count(row);
60
78
  }
61
79
  // A video is made when its file lands; a remade video counts again, as it was made again.
80
+ // A template's studio sample is no video of the person's (its spend still counts above).
62
81
  const videos = db
63
- .prepare("SELECT project_id FROM outputs WHERE role = 'video' AND created_at >= ?")
82
+ .prepare(`SELECT project_id FROM outputs WHERE role = 'video' AND created_at >= ? AND ${notTemplateSample("outputs", "project_id")}`)
64
83
  .all(since)
65
84
  .filter((row) => projectInScope(String(row.project_id))).length;
66
- return { since, videos, calls, cost, unpriced, apiEquivalent, plans: planStandings(db) };
85
+ return {
86
+ since,
87
+ videos,
88
+ calls,
89
+ cost,
90
+ unpriced,
91
+ studioCost,
92
+ apiEquivalent,
93
+ plans: planStandings(db),
94
+ };
67
95
  }
68
96
  function knownChannels(db) {
69
97
  return new Set(db
@@ -7,9 +7,11 @@ import { sampleIds } from "../onboarding/model.js";
7
7
  import { readSampleRecord } from "../onboarding/state.js";
8
8
  import { projectHolds } from "../reviews/holds.js";
9
9
  import { limitWaitsByProject, listingWait } from "../run-cost/panel.js";
10
+ import { notTemplateSample } from "../template-studio/repo.js";
10
11
  import { setAsideProjects, uploadedAndSettled, uploadedProjects } from "../uploads/repo.js";
11
12
  import { settledStatus } from "../uploads/rules.js";
12
13
  import { nextOccurrence } from "./calendar.js";
14
+ import { upcomingDesignRuns } from "./design-agenda.js";
13
15
  import { preparedProject } from "./prepare.js";
14
16
  import { scheduleRows } from "./repo.js";
15
17
  import { calendarMaxDays, } from "./schema.js";
@@ -49,6 +51,8 @@ function upcomingRuns(deps, schedule, now, from, to) {
49
51
  return [];
50
52
  if (schedule.status !== "active" && schedule.status !== "paused")
51
53
  return [];
54
+ if (schedule.cadence.kind === "by-date")
55
+ return upcomingDesignRuns(deps.db, schedule, now, from, to);
52
56
  const paused = schedule.status === "paused";
53
57
  // A paused schedule resumes from the next occurrence after the moment Resume is pressed.
54
58
  let at = schedule.nextRunAt === null
@@ -58,6 +62,7 @@ function upcomingRuns(deps, schedule, now, from, to) {
58
62
  : new Date(schedule.nextRunAt);
59
63
  const template = deps.template(schedule.templateId);
60
64
  const templateName = template?.name ?? null;
65
+ const notReady = deps.notReady?.(schedule.templateId) !== undefined;
61
66
  const form = template?.document.form;
62
67
  const { items } = schedule;
63
68
  const generation = schedule.topicGeneration.mode;
@@ -91,10 +96,13 @@ function upcomingRuns(deps, schedule, now, from, to) {
91
96
  ? "held"
92
97
  : "generated",
93
98
  renderedTitle: title,
99
+ templateKind: "project",
100
+ timezone: schedule.timezone,
94
101
  // Prepared ahead (`prepare.ts`): the project the day continues.
95
102
  prepared: title === null || queuedTopic === undefined
96
103
  ? null
97
104
  : (preparedProject(deps.db, schedule.id, title)?.projectId ?? null),
105
+ notReady,
98
106
  });
99
107
  if (schedule.cadence.kind === "once")
100
108
  break;
@@ -186,7 +194,8 @@ function projectsIn(deps, from, to) {
186
194
  COALESCE(project_controls.paused,0) AS paused,max(stages.finished_at) AS finished_at
187
195
  FROM projects LEFT JOIN stages ON stages.project_id=projects.id
188
196
  LEFT JOIN project_controls ON project_controls.project_id=projects.id
189
- WHERE projects.created_at<? AND ${liveProject()} GROUP BY projects.id
197
+ WHERE projects.created_at<? AND ${liveProject()} AND ${notTemplateSample()}
198
+ GROUP BY projects.id
190
199
  HAVING COALESCE(project_controls.paused,0)=1 OR count(stages.id)=0
191
200
  OR sum(stages.state IN ('pending','running'))>0
192
201
  OR COALESCE(max(stages.finished_at),projects.created_at)>=?
@@ -244,7 +253,7 @@ function queued(deps) {
244
253
  return [];
245
254
  const row = deps.db
246
255
  .prepare(`SELECT projects.title, batches.created_at FROM projects, batches
247
- WHERE projects.id=? AND batches.id=? AND ${liveProject()}`)
256
+ WHERE projects.id=? AND batches.id=? AND ${liveProject()} AND ${notTemplateSample()}`)
248
257
  .get(entry.projectId, entry.batchId);
249
258
  if (row === undefined)
250
259
  return [];