@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
@@ -1,3 +1,4 @@
1
+ import { errorText } from "../../kernel/errors.js";
1
2
  import { createPreviewCache } from "./preview-cache.js";
2
3
  export function createHub(deps) {
3
4
  const previews = createPreviewCache();
@@ -20,7 +21,7 @@ export function createHub(deps) {
20
21
  .writeSSE({ event: event.type, data: JSON.stringify(event), id: deps.ids.next() })
21
22
  .catch((error) => {
22
23
  subscriber.drop();
23
- deps.log.write("warn", "sse.write", { detail: `${event.type}: ${messageOf(error)}` });
24
+ deps.log.write("warn", "sse.write", { detail: `${event.type}: ${errorText(error)}` });
24
25
  });
25
26
  };
26
27
  const join = (set, stream, signal, prune) => {
@@ -80,7 +81,7 @@ export function createHub(deps) {
80
81
  // What the AI models are writing right now, so a page opened mid-call shows it.
81
82
  for (const event of previews.snapshotAll()) {
82
83
  const shown = present(event);
83
- if (shown !== undefined)
84
+ if (shown !== undefined && deps.quiet?.(shown.projectId) !== true)
84
85
  send(subscriber, shown);
85
86
  }
86
87
  }
@@ -96,6 +97,8 @@ export function createHub(deps) {
96
97
  return;
97
98
  for (const subscriber of projects.get(projectId) ?? [])
98
99
  send(subscriber, shown);
100
+ if (deps.quiet?.(projectId) === true)
101
+ return;
99
102
  for (const subscriber of globals)
100
103
  send(subscriber, shown);
101
104
  },
@@ -120,6 +123,3 @@ export function observedHub(hub, observe) {
120
123
  },
121
124
  };
122
125
  }
123
- function messageOf(error) {
124
- return error instanceof Error ? error.message : String(error);
125
- }
@@ -17,21 +17,12 @@ import { currentRevisionId } from "../../slices/revisions/repo.js";
17
17
  import { allowsCustomModel } from "../../slices/settings/models.js";
18
18
  import { providerStatuses } from "../../slices/settings/readiness.js";
19
19
  import { outputsOf } from "../../slices/storage/repo.js";
20
- import { onInvalid, problem, titleOf } from "./problem.js";
21
- const idParam = z.object({
22
- id: z
23
- .string()
24
- .min(1)
25
- .max(64)
26
- .regex(/^[0-9A-Za-z_-]+$/),
27
- });
20
+ import { idParam, idString } from "./ids.js";
21
+ import { onInvalid, problem } from "./problem.js";
22
+ import { revisionRequired } from "./refusal.js";
28
23
  const stageParam = idParam.extend({ kind: z.enum(stageKinds) });
29
24
  const imageParam = idParam.extend({
30
- outputId: z
31
- .string()
32
- .min(1)
33
- .max(64)
34
- .regex(/^[0-9A-Za-z_-]+$/),
25
+ outputId: idString,
35
26
  });
36
27
  // The return type is inferred so Hono keeps the route types the SPA's client is
37
28
  // generated from; see stagingRoutes.
@@ -109,7 +100,6 @@ export function actionRoutes(deps) {
109
100
  };
110
101
  return problem(c, {
111
102
  status: codes[result.reason],
112
- title: titleOf(codes[result.reason]),
113
103
  detail: messages[result.reason],
114
104
  extensions: {
115
105
  reason: result.reason,
@@ -122,7 +112,6 @@ export function actionRoutes(deps) {
122
112
  if (deps.rebuild === undefined)
123
113
  return problem(c, {
124
114
  status: 503,
125
- title: titleOf(503),
126
115
  detail: "Resume and retry are not ready yet because Slopify is still starting. Wait a moment, reload the page and try again.",
127
116
  });
128
117
  const result = recoveryResultSchema.parse(await recoverProject(deps.rebuild, id, { ...input, action }));
@@ -159,7 +148,6 @@ export function actionRoutes(deps) {
159
148
  ].join(" ");
160
149
  return problem(c, {
161
150
  status,
162
- title: titleOf(status),
163
151
  detail,
164
152
  extensions: {
165
153
  reason: result.reason,
@@ -222,7 +210,6 @@ export function actionRoutes(deps) {
222
210
  if (keys.length === 0)
223
211
  return problem(c, {
224
212
  status: 409,
225
- title: titleOf(409),
226
213
  detail: "Nothing in this section was refused by a content filter, so there is no prompt to soften. Reload the page; if the section still shows an error, use Try again.",
227
214
  extensions: { reason: "nothing-refused" },
228
215
  });
@@ -243,11 +230,3 @@ export function actionRoutes(deps) {
243
230
  .delete("/:id/images/:outputId", zValidator("param", imageParam, onInvalid), revisionRequired)
244
231
  .post("/:id/images/:outputId/regenerate", zValidator("param", imageParam, onInvalid), revisionRequired));
245
232
  }
246
- function revisionRequired(c) {
247
- return problem(c, {
248
- status: 409,
249
- title: titleOf(409),
250
- detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use Choose what to remake (in the project's More menu).",
251
- extensions: { reason: "revision-required" },
252
- });
253
- }
@@ -2,9 +2,11 @@
2
2
  // No network request can set them (the Node server passes its own `{ incoming, outgoing }`),
3
3
  // which is how a route knows an agent asked, not the person.
4
4
  export const agentBindings = { actor: "agent" };
5
+ // The in-app assistant's calls, through its own MCP endpoint (`/mcp/assistant`).
6
+ export const assistantBindings = { actor: "assistant" };
5
7
  export function actorOf(c) {
6
8
  const env = c.env;
7
- return typeof env === "object" && env !== null && "actor" in env && env.actor === "agent"
8
- ? "agent"
9
- : "person";
9
+ if (typeof env !== "object" || env === null || !("actor" in env))
10
+ return "person";
11
+ return env.actor === "agent" || env.actor === "assistant" ? env.actor : "person";
10
12
  }
@@ -4,9 +4,12 @@ import { Hono } from "hono";
4
4
  import { streamSSE } from "hono/streaming";
5
5
  import { mcpPath } from "../../slices/agents/model.js";
6
6
  import { isSampleProject } from "../../slices/onboarding/sample.js";
7
+ import { sampleOwner } from "../../slices/template-studio/repo.js";
8
+ import { assistantMcpPath, assistantMcpRoutes, runAllowedCall } from "../mcp/assistant.js";
7
9
  import { mcpRoutes } from "../mcp/http.js";
8
10
  import { actionRoutes } from "./actions.js";
9
- import { agentBindings } from "./actor.js";
11
+ import { agentBindings, assistantBindings } from "./actor.js";
12
+ import { assistantRoutes } from "./assistant.js";
10
13
  import { audioPreviewRoutes } from "./audio-preview.js";
11
14
  import { auditionRoutes } from "./auditions.js";
12
15
  import { autostartRoutes } from "./autostart.js";
@@ -14,6 +17,8 @@ import { backupRoutes } from "./backups.js";
14
17
  import { channelMemoryRoutes } from "./channel-memory.js";
15
18
  import { channelRoutes } from "./channels.js";
16
19
  import { checkpointRoutes } from "./checkpoints.js";
20
+ import { designTemplateRoutes } from "./design-templates.js";
21
+ import { designRoutes } from "./designs.js";
17
22
  import { diagnosticsRoutes } from "./diagnostics.js";
18
23
  import { documentThemeRoutes } from "./document-themes.js";
19
24
  import { draftRoutes } from "./drafts.js";
@@ -22,24 +27,31 @@ import { externalReviewRoutes } from "./external-reviews.js";
22
27
  import { fileRoutes } from "./files.js";
23
28
  import { fontsRoutes } from "./fonts.js";
24
29
  import { homeRoutes, uploadedRoutes } from "./home.js";
30
+ import { keywordRoutes } from "./keywords.js";
31
+ import { modelCheckRoutes, providerCheckRoutes } from "./model-checks.js";
25
32
  import { narrationPeakRoutes } from "./narration-peaks.js";
26
33
  import { onboardingRoutes } from "./onboarding.js";
27
34
  import { openFolderRoutes } from "./open-folder.js";
28
35
  import { outputRoutes } from "./outputs.js";
29
36
  import { patchNotesRoutes } from "./patch-notes.js";
30
37
  import { planningRoutes } from "./planning.js";
31
- import { problem, problemFromError, titleOf } from "./problem.js";
38
+ import { problem, problemFromError } from "./problem.js";
32
39
  import { projectTemplateRoutes } from "./project-templates.js";
33
40
  import { projectRoutes } from "./projects.js";
34
41
  import { promptRoutes } from "./prompts.js";
35
42
  import { pronunciationRoutes } from "./pronunciations.js";
36
43
  import { providerRoutes } from "./providers.js";
44
+ import { remoteAccessRoutes } from "./remote-access.js";
45
+ import { remoteGuard } from "./remote-guard.js";
46
+ import { requestGuard } from "./request-guard.js";
37
47
  import { reviewRoutes } from "./reviews.js";
38
48
  import { revisionFileRoutes, revisionFolderRoutes } from "./revision-files.js";
39
49
  import { revisionRoutes } from "./revisions.js";
40
50
  import { runCostRoutes } from "./run-cost.js";
51
+ import { viaFunnel } from "./same-page.js";
41
52
  import { calendarRoutes, scheduleRoutes } from "./schedules.js";
42
53
  import { settingsRoutes } from "./settings.js";
54
+ import { socialRoutes } from "./social.js";
43
55
  import { stagingRoutes } from "./staging.js";
44
56
  import { startRecorder } from "./starts.js";
45
57
  import { storageRoutes } from "./storage.js";
@@ -48,6 +60,7 @@ import { studioRoutes } from "./studio.js";
48
60
  import { stylePreviewRoutes } from "./style-preview.js";
49
61
  import { subtitleRoutes } from "./subtitles.js";
50
62
  import { telemetryRoutes } from "./telemetry.js";
63
+ import { templateStudioRoutes } from "./template-studio.js";
51
64
  import { trashRoutes } from "./trash.js";
52
65
  import { tutorialRoutes } from "./tutorial.js";
53
66
  import { tutorialPagesRoutes } from "./tutorials.js";
@@ -57,7 +70,7 @@ import { whatsNewRoutes } from "./whats-new.js";
57
70
  import { youtubeEditRoutes } from "./youtube-edits.js";
58
71
  const notBuilt = "The Slopify UI is not built yet. The API is running: try /api/health.\n" +
59
72
  "Build the SPA with `npm run build` at the repository root.\n";
60
- function apiRoutes(deps, startedAt) {
73
+ function apiRoutes(deps, startedAt, runAllowed) {
61
74
  return (new Hono()
62
75
  .get("/health", (c) => c.json({
63
76
  status: "ok",
@@ -72,9 +85,12 @@ function apiRoutes(deps, startedAt) {
72
85
  .route("/drafts", draftRoutes(deps.drafts))
73
86
  .route("/diagnostics", diagnosticsRoutes(deps))
74
87
  .route("/project-templates", projectTemplateRoutes(deps.drafts))
88
+ .route("/template-studio", templateStudioRoutes(deps))
75
89
  .route("/schedules", scheduleRoutes(deps.schedules))
76
90
  .route("/channels", channelRoutes(deps))
77
91
  .route("/channels", channelMemoryRoutes(deps))
92
+ .route("/designs", designRoutes(deps.designs))
93
+ .route("/design-templates", designTemplateRoutes(deps.designTemplates))
78
94
  .route("/calendar", calendarRoutes(deps.schedules))
79
95
  .route("/projects", planningRoutes(deps))
80
96
  .route("/projects", projectRoutes(deps))
@@ -98,6 +114,7 @@ function apiRoutes(deps, startedAt) {
98
114
  .route("/projects", youtubeEditRoutes(deps))
99
115
  .route("/fonts", fontsRoutes(deps))
100
116
  .route("/prompts", promptRoutes(deps))
117
+ .route("/keywords", keywordRoutes(deps))
101
118
  .route("/entries", entryRoutes(deps))
102
119
  .route("/document-themes", documentThemeRoutes(deps))
103
120
  .route("/pronunciations", pronunciationRoutes(deps))
@@ -105,19 +122,51 @@ function apiRoutes(deps, startedAt) {
105
122
  .route("/telemetry", telemetryRoutes(deps))
106
123
  .route("/usage", usageRoutes(deps))
107
124
  .route("/settings/autostart", autostartRoutes(deps.autostart))
125
+ .route("/settings/remote-access", remoteAccessRoutes(deps.remoteAccess))
108
126
  .route("/settings", settingsRoutes(deps))
109
127
  .route("/studio", studioRoutes(deps))
128
+ .route("/social", socialRoutes(deps))
110
129
  .route("/style-preview", stylePreviewRoutes(deps))
111
130
  .route("/tutorial", tutorialRoutes(deps))
112
131
  .route("/whats-new", whatsNewRoutes(deps))
113
132
  .route("/patch-notes", patchNotesRoutes(deps))
114
133
  .route("/tutorials", tutorialPagesRoutes(deps))
134
+ .route("/providers", providerCheckRoutes(deps))
115
135
  .route("/providers", providerRoutes(deps))
116
- .route("/onboarding", onboardingRoutes(deps)));
136
+ .route("/model-checks", modelCheckRoutes(deps))
137
+ .route("/onboarding", onboardingRoutes(deps))
138
+ .route("/assistant", assistantRoutes(deps, runAllowed)));
117
139
  }
118
140
  export function createApp(deps) {
119
141
  const startedAt = deps.clock.now().getTime();
142
+ // An Allow's call goes through the API in-process as the assistant, like its MCP calls.
143
+ const runAllowed = (call) => runAllowedCall({
144
+ db: deps.db,
145
+ ids: deps.ids,
146
+ clock: deps.clock,
147
+ log: deps.log,
148
+ api: async (request) => app.request(request, undefined, assistantBindings),
149
+ }, call);
120
150
  const app = new Hono()
151
+ // Slopify has no login, so it never answers the public internet. Tailscale marks every
152
+ // request that came through Funnel; Slopify never turns Funnel on, and this holds even if
153
+ // someone else does.
154
+ .use("*", async (c, next) => {
155
+ if (!viaFunnel(c.req.raw.headers))
156
+ return next();
157
+ return problem(c, {
158
+ status: 403,
159
+ detail: "Slopify doesn't answer the public internet: this request came through Tailscale Funnel. On the computer Slopify runs on, run tailscale funnel status to see which port has Funnel, turn it off with tailscale funnel --https=<that port> off, and open Slopify on your other devices through Settings → General → Remote access instead.",
160
+ });
161
+ })
162
+ // Only Slopify's own names, and changes only from its own page (`request-guard.ts`).
163
+ .use("*", requestGuard({
164
+ listenHost: deps.host,
165
+ remoteHost: deps.remoteAccess?.remoteHost,
166
+ extraHosts: deps.allowedHosts ?? [],
167
+ }))
168
+ // Over remote access: only the account that turned it on, and no host setup.
169
+ .use("*", remoteGuard(deps.remoteAccess))
121
170
  .use("*", async (c, next) => {
122
171
  // A provisional candidate must not trigger the stale-tab Reload dialog.
123
172
  if (!deps.updater?.locked())
@@ -130,14 +179,12 @@ export function createApp(deps) {
130
179
  if (deps.installationPending?.() && new URL(c.req.url).pathname !== "/api/update/activate")
131
180
  return problem(c, {
132
181
  status: 503,
133
- title: titleOf(503),
134
182
  detail: "Slopify is still finishing its Docker setup. Wait for the launcher to finish, then try again.",
135
183
  });
136
184
  const releaseRequest = deps.mutations?.begin();
137
185
  if (deps.mutations !== undefined && releaseRequest === undefined)
138
186
  return problem(c, {
139
187
  status: 503,
140
- title: titleOf(503),
141
188
  detail: "Slopify is shutting down, so changes cannot be saved right now. Start it again, reload the page and try again.",
142
189
  });
143
190
  const updateRoute = [
@@ -151,7 +198,6 @@ export function createApp(deps) {
151
198
  releaseRequest?.();
152
199
  return problem(c, {
153
200
  status: 409,
154
- title: titleOf(409),
155
201
  detail: "Slopify is updating. Wait for it to restart, reload the page and try again.",
156
202
  });
157
203
  }
@@ -176,15 +222,28 @@ export function createApp(deps) {
176
222
  return next();
177
223
  return problem(c, {
178
224
  status: 409,
179
- title: titleOf(409),
180
225
  detail: "This is the sample project, which is read-only so trying things on it never spends anything. Press Make my own copy at the top of the sample's page, then edit and rebuild the copy.",
181
226
  extensions: { reason: "sample-read-only" },
182
227
  });
228
+ })
229
+ // A template's studio sample is changed only through its studio (`slices/template-studio`),
230
+ // whose runner reaches the project's slices directly: no project route may change it.
231
+ .use("/api/projects/:id/*", async (c, next) => {
232
+ if (["GET", "HEAD", "OPTIONS"].includes(c.req.method))
233
+ return next();
234
+ const owner = sampleOwner(deps.db, c.req.param("id"));
235
+ if (owner === undefined)
236
+ return next();
237
+ return problem(c, {
238
+ status: 409,
239
+ detail: `This is the studio sample of ${owner.name}. Change the template in its studio instead: Library → Templates → ${owner.name} → Open studio.`,
240
+ extensions: { reason: "template-sample" },
241
+ });
183
242
  })
184
243
  .use("/api/*", startRecorder(deps))
185
244
  .onError((error, c) => problemFromError(c, error, deps))
186
245
  .notFound(missing)
187
- .route("/api", apiRoutes(deps, startedAt))
246
+ .route("/api", apiRoutes(deps, startedAt, runAllowed))
188
247
  .get("/api/events/global", (c) => streamSSE(c, (stream) => deps.hub.subscribeGlobal(stream, c.req.raw.signal)))
189
248
  .get("/api/events/projects/:id", (c) => streamSSE(c, (stream) => deps.hub.subscribe(c.req.param("id"), stream, c.req.raw.signal)))
190
249
  // Files are served by URL, not through the API.
@@ -193,6 +252,23 @@ export function createApp(deps) {
193
252
  // The API answers for its whole prefix, so an unknown endpoint is a problem+json 404
194
253
  // rather than the SPA's index.html with a 200.
195
254
  .all("/api/*", missing);
255
+ const mcpDeps = {
256
+ db: deps.db,
257
+ paths: deps.paths,
258
+ ids: deps.ids,
259
+ clock: deps.clock,
260
+ log: deps.log,
261
+ version: deps.version,
262
+ host: deps.host,
263
+ ...(deps.measureAudio === undefined ? {} : { measureAudio: deps.measureAudio }),
264
+ };
265
+ // The in-app assistant reaches it the same way, as the assistant, with its turn's token.
266
+ // Mounted first: `/mcp` must never answer for it.
267
+ app.route(assistantMcpPath, assistantMcpRoutes({
268
+ ...mcpDeps,
269
+ turns: deps.assistant?.tokens ?? deps.assistantTurns,
270
+ api: async (request) => app.request(request, undefined, assistantBindings),
271
+ }));
196
272
  // Agents reach the same API in-process, as the agent (`actor.ts`): every tool validates,
197
273
  // refuses and answers exactly like the page's own request.
198
274
  app.route(mcpPath, mcpRoutes({
@@ -225,7 +301,6 @@ const sampleReadOnlyExempt = /\/(revisions\/prepare|rebuild\/preview|open-folder
225
301
  function missing(c) {
226
302
  return problem(c, {
227
303
  status: 404,
228
- title: titleOf(404),
229
304
  detail: `${c.req.method} ${c.req.path} is not a route of this API.`,
230
305
  });
231
306
  }
@@ -0,0 +1,127 @@
1
+ import { zValidator } from "@hono/zod-validator";
2
+ import { Hono } from "hono";
3
+ import { z } from "zod";
4
+ import { assistantCliPreferences, assistantScreenSchema, assistantTextMax, } from "../../slices/assistant/model.js";
5
+ import { setCliPreference } from "../../slices/assistant/repo.js";
6
+ import { actorOf } from "./actor.js";
7
+ import { idParam } from "./ids.js";
8
+ import { onInvalid, problem } from "./problem.js";
9
+ import { hostOnlySentence } from "./remote-guard.js";
10
+ import { viaProxy } from "./same-page.js";
11
+ // The assistant panel's chat. The person's own: an agent or the assistant itself is refused
12
+ // here, so nothing but a press in the page answers an ask.
13
+ const messageBody = z
14
+ .object({
15
+ text: z
16
+ .string()
17
+ .trim()
18
+ .min(1, "Write a message first.")
19
+ .max(assistantTextMax, `Keep a message to ${String(assistantTextMax)} characters.`),
20
+ screen: assistantScreenSchema.optional(),
21
+ })
22
+ .strict();
23
+ const answerBody = z.object({ answer: z.enum(["allow", "deny"]) }).strict();
24
+ const cliBody = z.object({ preference: z.enum(assistantCliPreferences) }).strict();
25
+ const notWired = "This copy of Slopify was started without the assistant. Restart Slopify, then open the assistant again.";
26
+ function personOnly(c) {
27
+ return actorOf(c) === "person"
28
+ ? undefined
29
+ : problem(c, {
30
+ status: 403,
31
+ detail: "The assistant's chat is the person's: only a press in Slopify's page sends a message or answers an ask.",
32
+ });
33
+ }
34
+ export function assistantRoutes(deps,
35
+ // On Allow, the asked call is made by Slopify itself (`edge/mcp/assistant.ts`).
36
+ runAllowed) {
37
+ const assistant = deps.assistant;
38
+ const wired = (c) => personOnly(c) ?? assistant ?? problem(c, { status: 503, detail: notWired });
39
+ return new Hono()
40
+ .get("/", async (c) => {
41
+ const refused = personOnly(c);
42
+ if (refused !== undefined)
43
+ return refused;
44
+ if (assistant === undefined)
45
+ return c.json({
46
+ available: false,
47
+ cli: null,
48
+ reason: notWired,
49
+ fix: null,
50
+ preference: "auto",
51
+ session: null,
52
+ });
53
+ const { choice, preference } = await assistant.status();
54
+ return c.json({
55
+ available: choice.ok,
56
+ cli: choice.ok ? choice.cli : null,
57
+ reason: choice.ok ? null : choice.reason,
58
+ fix: choice.ok ? null : choice.fix,
59
+ preference,
60
+ session: assistant.session() ?? null,
61
+ });
62
+ })
63
+ .post("/sessions", (c) => {
64
+ const service = wired(c);
65
+ if (service instanceof Response)
66
+ return service;
67
+ const id = service.newSession();
68
+ return c.json({ session: service.session(id) }, 201);
69
+ })
70
+ .post("/sessions/:id/messages", zValidator("param", idParam, onInvalid), zValidator("json", messageBody, onInvalid), (c) => {
71
+ const service = wired(c);
72
+ if (service instanceof Response)
73
+ return service;
74
+ const { text, screen } = c.req.valid("json");
75
+ const sent = service.send(c.req.valid("param").id, text, screen);
76
+ if (sent.ok)
77
+ return c.json({ turnId: sent.turnId }, 202);
78
+ return problem(c, {
79
+ status: sent.reason === "unknown-session" ? 404 : 409,
80
+ detail: sent.reason === "unknown-session"
81
+ ? "This chat no longer exists. Press New chat and send your message again."
82
+ : sent.reason === "busy"
83
+ ? "The assistant is still answering in this chat. Wait for it, or press Stop, then send your message."
84
+ : "Two assistant chats are answering already. Wait for one to finish, or press Stop in it, then send your message.",
85
+ extensions: { reason: sent.reason },
86
+ });
87
+ })
88
+ .post("/sessions/:id/stop", zValidator("param", idParam, onInvalid), (c) => {
89
+ const service = wired(c);
90
+ if (service instanceof Response)
91
+ return service;
92
+ return c.json({ stopped: service.stop(c.req.valid("param").id) });
93
+ })
94
+ .post("/asks/:id", zValidator("param", idParam, onInvalid), zValidator("json", answerBody, onInvalid), (c) => {
95
+ // An Allow starts paid or removing work: it is pressed on this computer, or on the
96
+ // person's own device through remote access, never by another account (`remote-guard.ts`).
97
+ if (c.req.valid("json").answer === "allow" && viaProxy(c.req.raw.headers))
98
+ return problem(c, {
99
+ status: 403,
100
+ detail: hostOnlySentence("The assistant's Allow is pressed"),
101
+ extensions: { reason: "host-only" },
102
+ });
103
+ const service = wired(c);
104
+ if (service instanceof Response)
105
+ return service;
106
+ const answered = service.answer(c.req.valid("param").id, c.req.valid("json").answer, runAllowed);
107
+ if (answered.ok)
108
+ return c.json({ ask: answered.ask });
109
+ return problem(c, {
110
+ status: answered.reason === "unknown" ? 404 : 409,
111
+ detail: answered.reason === "unknown"
112
+ ? "This ask no longer exists. Ask the assistant again."
113
+ : answered.reason === "expired"
114
+ ? "This ask waited more than 15 minutes, so it expired before your answer. Ask the assistant again, so it can check the cost again."
115
+ : "This ask was already answered.",
116
+ extensions: { reason: answered.reason },
117
+ });
118
+ })
119
+ .put("/cli", zValidator("json", cliBody, onInvalid), (c) => {
120
+ const refused = personOnly(c);
121
+ if (refused !== undefined)
122
+ return refused;
123
+ const { preference } = c.req.valid("json");
124
+ setCliPreference(deps.db, preference);
125
+ return c.json({ preference });
126
+ });
127
+ }
@@ -4,18 +4,12 @@ import { z } from "zod";
4
4
  import { currentRevisionId } from "../../slices/revisions/repo.js";
5
5
  import { projectTitle } from "../../slices/storage/repo.js";
6
6
  import { currentWorkOrigin } from "../events/visibility.js";
7
- import { onInvalid, problem, titleOf } from "./problem.js";
8
- const projectParam = z.object({
9
- projectId: z
10
- .string()
11
- .min(1)
12
- .max(64)
13
- .regex(/^[0-9A-Za-z_-]+$/),
14
- });
15
- const previewParam = projectParam.extend({ previewId: z.uuid() });
7
+ import { projectIdParam } from "./ids.js";
8
+ import { onInvalid, problem } from "./problem.js";
9
+ const previewParam = projectIdParam.extend({ previewId: z.uuid() });
16
10
  export function audioPreviewRoutes(deps) {
17
11
  return new Hono()
18
- .get("/:projectId/audio-preview", zValidator("param", projectParam, onInvalid), (c) => {
12
+ .get("/:projectId/audio-preview", zValidator("param", projectIdParam, onInvalid), (c) => {
19
13
  const { projectId } = c.req.valid("param");
20
14
  if (projectTitle(deps.db, projectId) === undefined)
21
15
  return missing(c);
@@ -48,7 +42,6 @@ export function audioPreviewRoutes(deps) {
48
42
  function missing(c) {
49
43
  return problem(c, {
50
44
  status: 404,
51
- title: titleOf(404),
52
45
  detail: "This live audio preview has ended. Play the finished narration from the project's audio outputs instead.",
53
46
  });
54
47
  }
@@ -10,7 +10,7 @@ const line = z.object({
10
10
  model: z.string().min(1).max(200),
11
11
  text: z.string().trim().min(1).max(auditionLineMax),
12
12
  });
13
- const quoteBody = z.object({
13
+ export const quoteBody = z.object({
14
14
  lines: z
15
15
  .array(line.extend({ speaker: z.string().max(100) }))
16
16
  .min(1)
@@ -2,7 +2,7 @@ import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { z } from "zod";
4
4
  import { AutostartRefusal } from "../autostart/service.js";
5
- import { onInvalid, problem, titleOf } from "./problem.js";
5
+ import { onInvalid, problem } from "./problem.js";
6
6
  const body = z.object({ enabled: z.boolean() });
7
7
  // Settings → General's "Start Slopify when I log in", and the first-run screen's offer of it.
8
8
  // In Docker the view says whether Docker starts at login, and turning it on or off is refused
@@ -12,26 +12,26 @@ export function autostartRoutes(service) {
12
12
  return (new Hono()
13
13
  .get("/", async (c) => {
14
14
  if (service === undefined)
15
- return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
15
+ return problem(c, { status: 404, detail: unavailable });
16
16
  c.header("Cache-Control", "no-store");
17
17
  return c.json(await service.view());
18
18
  })
19
19
  .put("/", zValidator("json", body, onInvalid), async (c) => {
20
20
  if (service === undefined)
21
- return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
21
+ return problem(c, { status: 404, detail: unavailable });
22
22
  try {
23
23
  return c.json(await service.set(c.req.valid("json").enabled));
24
24
  }
25
25
  catch (error) {
26
26
  if (!(error instanceof AutostartRefusal))
27
27
  throw error;
28
- return problem(c, { status: 409, title: titleOf(409), detail: error.message });
28
+ return problem(c, { status: 409, detail: error.message });
29
29
  }
30
30
  })
31
31
  // The first-run screen's No thanks: never offer it there again, change nothing.
32
32
  .post("/answer", async (c) => {
33
33
  if (service === undefined)
34
- return problem(c, { status: 404, title: titleOf(404), detail: unavailable });
34
+ return problem(c, { status: 404, detail: unavailable });
35
35
  return c.json(await service.answer());
36
36
  }));
37
37
  }
@@ -2,7 +2,7 @@ import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
3
  import { HTTPException } from "hono/http-exception";
4
4
  import { backupConfigInputSchema } from "../../slices/backups/model.js";
5
- import { onInvalid, problem, titleOf } from "./problem.js";
5
+ import { onInvalid, problem } from "./problem.js";
6
6
  // Settings → Backups: the schedule, the folder, the last result and Back up now. Writing an
7
7
  // archive takes minutes, so Back up now answers at once and the screen polls this view.
8
8
  export function backupRoutes(service) {
@@ -21,7 +21,6 @@ export function backupRoutes(service) {
21
21
  if (!saved.ok)
22
22
  return problem(c, {
23
23
  status: 400,
24
- title: titleOf(400),
25
24
  detail: saved.detail,
26
25
  extensions: { field: "folder" },
27
26
  });
@@ -32,7 +31,6 @@ export function backupRoutes(service) {
32
31
  if (!started.ok)
33
32
  return problem(c, {
34
33
  status: started.status,
35
- title: titleOf(started.status),
36
34
  detail: started.detail,
37
35
  });
38
36
  return c.json(await backups().view(), 202);
@@ -5,7 +5,8 @@ import { bodyLimit } from "hono/body-limit";
5
5
  import { z } from "zod";
6
6
  import { clearChannelVideos, deleteChannelVideo, importChannelVideos, listChannelVideos, previewChannelVideos, } from "../../slices/channels/videos.js";
7
7
  import { channelEpisodes, deleteMemory, editMemory, setEpisodeMemory, } from "../../slices/episodes/service.js";
8
- import { onInvalid, problem, titleOf } from "./problem.js";
8
+ import { onInvalid } from "./problem.js";
9
+ import { jsonBody, refusal } from "./refusal.js";
9
10
  const idParam = z.object({ id: z.uuid() });
10
11
  const memoryParam = idParam.extend({ memoryId: z.string().min(1).max(100) });
11
12
  const videoParam = idParam.extend({ videoId: z.uuid() });
@@ -22,12 +23,12 @@ export function channelMemoryRoutes(deps) {
22
23
  return result.ok ? c.json(result.value) : refused(c, result, "episode");
23
24
  })
24
25
  .put("/:id/episodes/setting", zValidator("param", idParam, onInvalid), async (c) => {
25
- const result = setEpisodeMemory(service, c.req.valid("param").id, await body(c));
26
+ const result = setEpisodeMemory(service, c.req.valid("param").id, await jsonBody(c));
26
27
  return result.ok ? c.json(result.value) : refused(c, result, "episode");
27
28
  })
28
29
  .put("/:id/episodes/:memoryId", zValidator("param", memoryParam, onInvalid), async (c) => {
29
30
  const { id, memoryId } = c.req.valid("param");
30
- const result = editMemory(service, id, memoryId, await body(c));
31
+ const result = editMemory(service, id, memoryId, await jsonBody(c));
31
32
  return result.ok ? c.json(result.value) : refused(c, result, "episode");
32
33
  })
33
34
  .delete("/:id/episodes/:memoryId", zValidator("param", memoryParam, onInvalid), (c) => {
@@ -40,11 +41,11 @@ export function channelMemoryRoutes(deps) {
40
41
  return result.ok ? c.json({ videos: result.value }) : refused(c, result, "video");
41
42
  })
42
43
  .post("/:id/videos/preview", zValidator("param", idParam, onInvalid), importLimit(), async (c) => {
43
- const result = previewChannelVideos(service, c.req.valid("param").id, await body(c));
44
+ const result = previewChannelVideos(service, c.req.valid("param").id, await jsonBody(c));
44
45
  return result.ok ? c.json(result.value) : refused(c, result, "video");
45
46
  })
46
47
  .post("/:id/videos", zValidator("param", idParam, onInvalid), importLimit(), async (c) => {
47
- const result = importChannelVideos(service, c.req.valid("param").id, await body(c));
48
+ const result = importChannelVideos(service, c.req.valid("param").id, await jsonBody(c));
48
49
  return result.ok ? c.json(result.value, 201) : refused(c, result, "video");
49
50
  })
50
51
  .delete("/:id/videos", zValidator("param", idParam, onInvalid), (c) => {
@@ -67,26 +68,12 @@ function importLimit() {
67
68
  }, "video"),
68
69
  });
69
70
  }
70
- async function body(c) {
71
- try {
72
- return await c.req.json();
73
- }
74
- catch {
75
- return undefined;
76
- }
77
- }
78
71
  function refused(c, result, about) {
79
- const status = result.reason === "not-found" ? 404 : 400;
80
72
  const detail = result.message ??
81
73
  (result.reason === "not-found"
82
74
  ? about === "episode"
83
75
  ? "This channel or episode summary no longer exists; it may have been deleted in another tab. Reload the channel's Episodes tab."
84
76
  : "This channel or video title no longer exists; it may have been deleted in another tab. Reload the channel's Existing videos tab."
85
77
  : "This request isn't valid. Reload the page and try again.");
86
- return problem(c, {
87
- status,
88
- title: titleOf(status),
89
- detail,
90
- extensions: { reason: result.reason },
91
- });
78
+ return refusal(c, result.reason, { statuses: { "not-found": 404 }, otherwise: 400, detail });
92
79
  }