@gentbajko/slopify 3.25.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,388 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ import { redact } from "../../kernel/log.js";
6
+ import { cliName } from "../explain.js";
7
+ import { cliLoginError } from "./cli-login-error.js";
8
+ import { codexBinary, hardenedCodexBase } from "./codex.js";
9
+ import { cliInput, endedWithout, stopCliRun, stuckCli } from "./run-cli.js";
10
+ import { lines } from "./sse-lines.js";
11
+ // The in-app assistant's CLI turn: Claude Code or Codex with no tool of its own (no shell, no
12
+ // files, no web), none of the person's settings, hooks, plugins, skills, memory or MCP
13
+ // servers, and one MCP server: Slopify's `/mcp/assistant`, opened with the turn's token.
14
+ //
15
+ // The token is never an argument: argv is readable by every process on the computer. It is in
16
+ // the CLI's environment only, and the CLIs put it in the header themselves:
17
+ // - Claude Code 2.1.302 expands ${VAR} in an --mcp-config file's headers (checked on this
18
+ // machine: a stand-in server saw "Bearer <the token>" from `"Bearer
19
+ // ${SLOPIFY_ASSISTANT_TOKEN}"`). The file names the variable, not the token.
20
+ // - Codex 0.155.1 reads `mcp_servers.<name>.bearer_token_env_var` (codex-rs config
21
+ // `McpServerTransportConfig::StreamableHttp`; also checked: the server saw the bearer).
22
+ export const assistantServerName = "slopify";
23
+ export const assistantTokenVariable = "SLOPIFY_ASSISTANT_TOKEN";
24
+ const slopifyTool = `mcp__${assistantServerName}__`;
25
+ export function claudeAssistantArgs(options) {
26
+ return [
27
+ "-p",
28
+ "--output-format",
29
+ "stream-json",
30
+ "--verbose",
31
+ "--include-partial-messages",
32
+ // `claude --help` 2.1.302: --restricted drops the code-running tools and WebFetch and
33
+ // ignores the user, project and local settings files; --setting-sources "" loads none of
34
+ // them either. --bare would discard the OAuth sign-in the assistant runs on.
35
+ "--restricted",
36
+ "--setting-sources",
37
+ "",
38
+ "--disable-slash-commands",
39
+ "--no-chrome",
40
+ "--no-session-persistence",
41
+ "--settings",
42
+ JSON.stringify({
43
+ disableAllHooks: true,
44
+ autoMemoryEnabled: false,
45
+ claudeMdExcludes: ["**"],
46
+ enabledPlugins: {},
47
+ }),
48
+ "--system-prompt",
49
+ options.system,
50
+ "--strict-mcp-config",
51
+ "--mcp-config",
52
+ options.mcpConfig,
53
+ // No built-in tool at all; only Slopify's server is allowed, and the web tools are named
54
+ // as refused in case a later version brings them back by default.
55
+ "--tools",
56
+ "",
57
+ "--allowedTools",
58
+ `mcp__${assistantServerName}`,
59
+ "--disallowedTools",
60
+ "WebSearch,WebFetch,Bash",
61
+ "--permission-mode",
62
+ "dontAsk",
63
+ ...(options.model === undefined || options.model === "" ? [] : ["--model", options.model]),
64
+ ];
65
+ }
66
+ export function claudeAssistantConfig(url) {
67
+ return JSON.stringify({
68
+ mcpServers: {
69
+ [assistantServerName]: {
70
+ type: "http",
71
+ url,
72
+ headers: { Authorization: `Bearer \${${assistantTokenVariable}}` },
73
+ },
74
+ },
75
+ });
76
+ }
77
+ export function codexAssistantArgs(options) {
78
+ const server = `mcp_servers.${assistantServerName}`;
79
+ return [
80
+ ...hardenedCodexBase(options.directory),
81
+ "-c",
82
+ `instructions=${JSON.stringify(options.system)}`,
83
+ "-c",
84
+ 'web_search="disabled"',
85
+ "-c",
86
+ "tool_output_token_limit=32768",
87
+ "-c",
88
+ `${server}.url=${JSON.stringify(options.url)}`,
89
+ "-c",
90
+ `${server}.bearer_token_env_var=${JSON.stringify(assistantTokenVariable)}`,
91
+ "-c",
92
+ `${server}.required=true`,
93
+ "-c",
94
+ `${server}.default_tools_approval_mode="auto"`,
95
+ ...(options.model === undefined || options.model === "" ? [] : ["-m", options.model]),
96
+ "--",
97
+ "-",
98
+ ];
99
+ }
100
+ // What the CLI is started with: enough to find itself, its sign-in and the network, and
101
+ // nothing else of Slopify's environment. A provider key there would make the CLI bill that key
102
+ // instead of the person's plan, and hand it a secret it never needs.
103
+ const passedNames = new Set([
104
+ "PATH",
105
+ "HOME",
106
+ "USER",
107
+ "LOGNAME",
108
+ "SHELL",
109
+ "LANG",
110
+ "LANGUAGE",
111
+ "TZ",
112
+ "TMPDIR",
113
+ "TEMP",
114
+ "TMP",
115
+ "TERM",
116
+ // Where each CLI keeps its sign-in and settings, when moved from the default.
117
+ "CLAUDE_CONFIG_DIR",
118
+ "CODEX_HOME",
119
+ // A network that needs a proxy or its own certificates.
120
+ "HTTP_PROXY",
121
+ "HTTPS_PROXY",
122
+ "NO_PROXY",
123
+ "http_proxy",
124
+ "https_proxy",
125
+ "no_proxy",
126
+ "NODE_EXTRA_CA_CERTS",
127
+ "SSL_CERT_FILE",
128
+ "SSL_CERT_DIR",
129
+ // Windows: the CLI can't start, or find its folders, without these.
130
+ "SYSTEMROOT",
131
+ "SystemRoot",
132
+ "WINDIR",
133
+ "COMSPEC",
134
+ "PATHEXT",
135
+ "USERPROFILE",
136
+ "APPDATA",
137
+ "LOCALAPPDATA",
138
+ "HOMEDRIVE",
139
+ "HOMEPATH",
140
+ "PROGRAMDATA",
141
+ "ProgramData",
142
+ "PROGRAMFILES",
143
+ "ProgramFiles",
144
+ "Path",
145
+ ]);
146
+ export function assistantEnv(sourceEnv) {
147
+ const env = {};
148
+ for (const [name, value] of Object.entries(sourceEnv))
149
+ if (value !== undefined && (passedNames.has(name) || /^(LC|XDG)_[A-Z_]+$/.test(name)))
150
+ env[name] = value;
151
+ return env;
152
+ }
153
+ // A private folder (0700) that is the CLI's working directory, so nothing of the person's
154
+ // projects is its cwd, holding the 0600 config file; removed when the turn ends.
155
+ export function assistantWorkspace(url, token, sourceEnv) {
156
+ const directory = mkdtempSync(join(tmpdir(), "slopify-assistant-"));
157
+ const remove = () => rmSync(directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
158
+ try {
159
+ const config = join(directory, "mcp.json");
160
+ writeFileSync(config, claudeAssistantConfig(url), { mode: 0o600, flag: "wx" });
161
+ const env = assistantEnv(sourceEnv);
162
+ env.PWD = directory;
163
+ env[assistantTokenVariable] = token;
164
+ return { directory, config, env, remove };
165
+ }
166
+ catch (error) {
167
+ remove();
168
+ throw error;
169
+ }
170
+ }
171
+ const typed = z.object({ type: z.string() }).loose();
172
+ const claudeInit = z
173
+ .object({
174
+ subtype: z.literal("init"),
175
+ tools: z.array(z.string()).optional(),
176
+ mcp_servers: z.array(z.object({ name: z.string(), status: z.string() }).loose()).optional(),
177
+ })
178
+ .loose();
179
+ const claudeAssistant = z
180
+ .object({
181
+ message: z.object({
182
+ content: z.array(z
183
+ .object({ type: z.string(), text: z.string().optional(), name: z.string().optional() })
184
+ .loose()),
185
+ }),
186
+ })
187
+ .loose();
188
+ const claudePartial = z
189
+ .object({
190
+ event: z.object({
191
+ type: z.literal("content_block_delta"),
192
+ delta: z.object({ type: z.literal("text_delta"), text: z.string() }),
193
+ }),
194
+ })
195
+ .loose();
196
+ const claudeResult = z
197
+ .object({ subtype: z.string(), is_error: z.boolean().optional(), result: z.string().optional() })
198
+ .loose();
199
+ const codexItem = z
200
+ .object({
201
+ item: z
202
+ .object({
203
+ id: z.string().optional(),
204
+ type: z.string(),
205
+ text: z.string().optional(),
206
+ server: z.string().optional(),
207
+ tool: z.string().optional(),
208
+ })
209
+ .loose(),
210
+ })
211
+ .loose();
212
+ const codexFailed = z.object({ error: z.object({ message: z.string() }) }).loose();
213
+ const codexError = z.object({ message: z.string() }).loose();
214
+ // Codex items that are no part of a Slopify chat: a shell command, a file edit, a web
215
+ // search, another agent.
216
+ const codexForeign = new Set([
217
+ "command_execution",
218
+ "file_change",
219
+ "web_search",
220
+ "collab_tool_call",
221
+ ]);
222
+ function failed(cli, said) {
223
+ const login = cliLoginError(cli, said);
224
+ if (login !== undefined)
225
+ return { type: "failed", message: login.message };
226
+ return {
227
+ type: "failed",
228
+ message: `The ${cliName(cli === "claude-code" ? "claude" : "codex")} stopped with an error: ${redact(said).slice(0, 500)}. Send your message again; if it keeps happening, check the CLI in Settings → Providers.`,
229
+ };
230
+ }
231
+ // One JSONL line of the CLI's stream as what the chat shows. A stateful reader: Codex reports
232
+ // a message's text so far, and only what is new is passed on.
233
+ export function assistantStream(cli) {
234
+ const typedSoFar = new Map();
235
+ return (line) => {
236
+ if (line.trim() === "")
237
+ return [];
238
+ let value;
239
+ try {
240
+ value = JSON.parse(line);
241
+ }
242
+ catch {
243
+ return [];
244
+ }
245
+ const event = typed.safeParse(value);
246
+ if (!event.success)
247
+ return [];
248
+ return cli === "claude-code"
249
+ ? claudeEvents(event.data.type, value)
250
+ : codexEvents(event.data.type, value);
251
+ };
252
+ function claudeEvents(type, value) {
253
+ if (type === "system") {
254
+ const init = claudeInit.safeParse(value);
255
+ if (!init.success)
256
+ return [];
257
+ const foreign = (init.data.tools ?? []).filter((name) => !name.startsWith(slopifyTool));
258
+ if (foreign.length > 0)
259
+ return [{ type: "foreign", name: foreign.join(", ") }];
260
+ const server = init.data.mcp_servers?.find((one) => one.name === assistantServerName);
261
+ if (server !== undefined && (server.status === "failed" || server.status === "needs-auth"))
262
+ return [
263
+ {
264
+ type: "failed",
265
+ message: "Claude Code couldn't reach Slopify's assistant tools, so it answered nothing. Send your message again; if it keeps happening, restart Slopify.",
266
+ },
267
+ ];
268
+ return [];
269
+ }
270
+ if (type === "stream_event") {
271
+ const partial = claudePartial.safeParse(value);
272
+ return partial.success && partial.data.event.delta.text !== ""
273
+ ? [{ type: "partial", text: partial.data.event.delta.text }]
274
+ : [];
275
+ }
276
+ if (type === "assistant") {
277
+ const message = claudeAssistant.safeParse(value);
278
+ if (!message.success)
279
+ return [];
280
+ return message.data.message.content.flatMap((block) => {
281
+ if (block.type === "text" && block.text !== undefined && block.text !== "")
282
+ return [{ type: "text", text: block.text }];
283
+ if (block.type !== "tool_use" || block.name === undefined)
284
+ return [];
285
+ return block.name.startsWith(slopifyTool)
286
+ ? [{ type: "tool", name: block.name.slice(slopifyTool.length) }]
287
+ : [{ type: "foreign", name: block.name }];
288
+ });
289
+ }
290
+ if (type === "result") {
291
+ const result = claudeResult.safeParse(value);
292
+ if (!result.success)
293
+ return [];
294
+ return result.data.is_error === true || result.data.subtype !== "success"
295
+ ? [failed("claude-code", result.data.result ?? result.data.subtype)]
296
+ : [{ type: "done" }];
297
+ }
298
+ return [];
299
+ }
300
+ function codexEvents(type, value) {
301
+ if (type === "item.started" || type === "item.updated" || type === "item.completed") {
302
+ const parsed = codexItem.safeParse(value);
303
+ if (!parsed.success)
304
+ return [];
305
+ const { item } = parsed.data;
306
+ if (codexForeign.has(item.type))
307
+ return [{ type: "foreign", name: item.type }];
308
+ if (item.type === "mcp_tool_call") {
309
+ if (item.server !== assistantServerName)
310
+ return [{ type: "foreign", name: `${item.server ?? "?"}.${item.tool ?? "?"}` }];
311
+ return type === "item.started" && item.tool !== undefined
312
+ ? [{ type: "tool", name: item.tool }]
313
+ : [];
314
+ }
315
+ if (item.type !== "agent_message")
316
+ return [];
317
+ const id = item.id ?? "";
318
+ const seen = typedSoFar.get(id) ?? "";
319
+ const text = item.text ?? "";
320
+ if (type === "item.completed") {
321
+ typedSoFar.delete(id);
322
+ return text === "" ? [] : [{ type: "text", text }];
323
+ }
324
+ if (text.length > seen.length && text.startsWith(seen)) {
325
+ typedSoFar.set(id, text);
326
+ return [{ type: "partial", text: text.slice(seen.length) }];
327
+ }
328
+ return [];
329
+ }
330
+ if (type === "turn.completed")
331
+ return [{ type: "done" }];
332
+ if (type === "turn.failed") {
333
+ const parsed = codexFailed.safeParse(value);
334
+ return [failed("codex", parsed.success ? parsed.data.error.message : "the turn failed")];
335
+ }
336
+ if (type === "error") {
337
+ const parsed = codexError.safeParse(value);
338
+ return [failed("codex", parsed.success ? parsed.data.message : "unknown error")];
339
+ }
340
+ return [];
341
+ }
342
+ }
343
+ // The turn itself: a private workspace, the CLI started in it with the prompt on stdin, its
344
+ // stream read into the chat's events, and everything stopped and removed at the end.
345
+ export function assistantCliRunner(run, sourceEnv = process.env) {
346
+ return async function* turn(request) {
347
+ const space = assistantWorkspace(request.url, request.token, sourceEnv);
348
+ let child;
349
+ let ended;
350
+ try {
351
+ child = run(request.binary, request.cli === "claude-code"
352
+ ? claudeAssistantArgs({ mcpConfig: space.config, system: request.system })
353
+ : codexAssistantArgs({
354
+ directory: space.directory,
355
+ url: request.url,
356
+ system: request.system,
357
+ }), request.signal, { cwd: space.directory, env: space.env, stdin: cliInput(request.prompt) });
358
+ const read = assistantStream(request.cli);
359
+ for await (const line of lines(child.stdout, request.signal))
360
+ for (const event of read(line)) {
361
+ yield event;
362
+ if (event.type === "done" || event.type === "failed" || event.type === "foreign")
363
+ return;
364
+ }
365
+ }
366
+ finally {
367
+ // Also when the reader stops early (Stop, the time limit): a CLI left running would
368
+ // keep spending the person's plan.
369
+ try {
370
+ if (child !== undefined)
371
+ ended = await stopCliRun(child);
372
+ }
373
+ finally {
374
+ space.remove();
375
+ }
376
+ }
377
+ if (request.signal.aborted || child === undefined)
378
+ return;
379
+ // The stream ended with neither an answer nor a failure.
380
+ const binary = request.cli === "claude-code" ? "claude" : codexBinary;
381
+ const said = child.stderr();
382
+ yield {
383
+ type: "failed",
384
+ message: cliLoginError(request.cli, said)?.message ??
385
+ (ended === undefined ? stuckCli(binary) : endedWithout(binary, ended, said)),
386
+ };
387
+ };
388
+ }
@@ -0,0 +1,83 @@
1
+ import { spawn } from "node:child_process";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ export async function withChildSession(spec, work) {
6
+ const directory = await mkdtemp(join(tmpdir(), spec.directoryPrefix));
7
+ const child = spawn(spec.file, [...spec.args], {
8
+ cwd: directory,
9
+ env: spec.env(directory),
10
+ stdio: ["pipe", "pipe", "pipe"],
11
+ windowsHide: true,
12
+ detached: process.platform !== "win32",
13
+ });
14
+ child.on("error", () => { });
15
+ child.stdin.on("error", () => { });
16
+ child.stderr.resume();
17
+ const closed = new Promise((resolve) => child.once("close", () => resolve()));
18
+ const terminate = (signal) => {
19
+ if (process.platform !== "win32" && child.pid !== undefined) {
20
+ try {
21
+ process.kill(-child.pid, signal);
22
+ return;
23
+ }
24
+ catch {
25
+ // A child that already exited has no process group to signal.
26
+ }
27
+ }
28
+ child.kill(signal);
29
+ };
30
+ let forceTimer;
31
+ const timer = setTimeout(() => {
32
+ if (spec.killAfterTermMs === undefined) {
33
+ terminate("SIGKILL");
34
+ return;
35
+ }
36
+ terminate("SIGTERM");
37
+ forceTimer = setTimeout(() => terminate("SIGKILL"), spec.killAfterTermMs);
38
+ }, spec.timeoutMs);
39
+ try {
40
+ return await work({
41
+ child,
42
+ send: (message) => {
43
+ child.stdin.write(`${JSON.stringify(message)}\n`);
44
+ },
45
+ });
46
+ }
47
+ finally {
48
+ clearTimeout(timer);
49
+ if (forceTimer !== undefined)
50
+ clearTimeout(forceTimer);
51
+ terminate("SIGTERM");
52
+ if (!(await closesWithin(closed, spec.graceMs))) {
53
+ terminate("SIGKILL");
54
+ if (spec.settleMs !== undefined)
55
+ await closesWithin(closed, spec.settleMs);
56
+ }
57
+ await rm(directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
58
+ }
59
+ }
60
+ async function closesWithin(closed, ms) {
61
+ let timer;
62
+ try {
63
+ return await Promise.race([
64
+ closed.then(() => true),
65
+ new Promise((resolve) => {
66
+ timer = setTimeout(() => resolve(false), ms);
67
+ }),
68
+ ]);
69
+ }
70
+ finally {
71
+ clearTimeout(timer);
72
+ }
73
+ }
74
+ // The child's output, ended with an error once more than `maxBytes` has arrived.
75
+ export async function* cappedOutput(chunks, maxBytes, oversize) {
76
+ let bytes = 0;
77
+ for await (const chunk of chunks) {
78
+ bytes += chunk.byteLength;
79
+ if (bytes > maxBytes)
80
+ throw oversize();
81
+ yield chunk;
82
+ }
83
+ }
@@ -1,10 +1,8 @@
1
- import { spawn } from "node:child_process";
2
1
  import { randomUUID } from "node:crypto";
3
- import { mkdtemp, rm } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { join } from "node:path";
6
2
  import { z } from "zod";
7
3
  import { cliCommand } from "../../kernel/cli-command.js";
4
+ import { cappedOutput, withChildSession } from "./child-session.js";
5
+ import { lines } from "./sse-lines.js";
8
6
  const maxOutputBytes = 1024 * 1024;
9
7
  const modelId = z
10
8
  .string()
@@ -109,104 +107,59 @@ function claudeModelName(target, description, fallback) {
109
107
  // The installed CLI has no model-list command; initialize is a no-prompt
110
108
  // stream-JSON control exchange. Treat protocol drift as unavailable metadata.
111
109
  export async function nodeClaudeCodeModels(binary, timeoutMs = 15_000) {
112
- const directory = await mkdtemp(join(tmpdir(), "slopify-claude-models-"));
113
110
  const requestId = randomUUID();
114
111
  const command = cliCommand(binary);
115
- const child = spawn(command.file, [
116
- ...command.args,
117
- "-p",
118
- "--input-format",
119
- "stream-json",
120
- "--output-format",
121
- "stream-json",
122
- "--verbose",
123
- "--safe-mode",
124
- "--tools",
125
- "",
126
- "--strict-mcp-config",
127
- "--no-session-persistence",
128
- ], {
129
- cwd: directory,
130
- env: discoveryEnv(),
131
- stdio: ["pipe", "pipe", "pipe"],
132
- windowsHide: true,
133
- detached: process.platform !== "win32",
134
- });
135
- child.on("error", () => { });
136
- child.stdin.on("error", () => { });
137
- child.stderr.resume();
138
- const closed = new Promise((resolve) => child.once("close", () => resolve()));
139
- const terminate = (force) => {
140
- const signal = force ? "SIGKILL" : "SIGTERM";
141
- if (process.platform !== "win32" && child.pid !== undefined) {
142
- try {
143
- process.kill(-child.pid, signal);
144
- return;
145
- }
146
- catch {
147
- // A child that already exited has no process group to signal.
148
- }
149
- }
150
- child.kill(signal);
151
- };
152
- let forceTimer;
153
- const timer = setTimeout(() => {
154
- terminate(false);
155
- forceTimer = setTimeout(() => terminate(true), 500);
156
- }, timeoutMs);
157
- try {
158
- child.stdin.write(`${JSON.stringify({
159
- request_id: requestId,
160
- type: "control_request",
161
- request: { subtype: "initialize" },
162
- })}\n`);
163
- let output = "";
164
- let bytes = 0;
165
- for await (const piece of child.stdout) {
166
- bytes += piece.length;
167
- if (bytes > maxOutputBytes)
168
- throw unavailable();
169
- output += piece.toString();
170
- let newline = output.indexOf("\n");
171
- while (newline >= 0) {
172
- const line = output.slice(0, newline).trim();
173
- output = output.slice(newline + 1);
174
- if (line !== "") {
175
- let event;
176
- try {
177
- event = JSON.parse(line);
178
- }
179
- catch {
180
- throw unavailable();
181
- }
182
- if (typeof event === "object" &&
183
- event !== null &&
184
- "type" in event &&
185
- event.type === "control_response") {
186
- return choices(event, requestId);
187
- }
112
+ return withChildSession({
113
+ file: command.file,
114
+ args: [
115
+ ...command.args,
116
+ "-p",
117
+ "--input-format",
118
+ "stream-json",
119
+ "--output-format",
120
+ "stream-json",
121
+ "--verbose",
122
+ "--safe-mode",
123
+ "--tools",
124
+ "",
125
+ "--strict-mcp-config",
126
+ "--no-session-persistence",
127
+ ],
128
+ env: () => discoveryEnv(),
129
+ directoryPrefix: "slopify-claude-models-",
130
+ timeoutMs,
131
+ killAfterTermMs: 500,
132
+ graceMs: 500,
133
+ settleMs: 500,
134
+ }, async ({ child, send }) => {
135
+ try {
136
+ send({
137
+ request_id: requestId,
138
+ type: "control_request",
139
+ request: { subtype: "initialize" },
140
+ });
141
+ for await (const text of lines(cappedOutput(child.stdout, maxOutputBytes, unavailable))) {
142
+ const line = text.trim();
143
+ if (line === "")
144
+ continue;
145
+ let event;
146
+ try {
147
+ event = JSON.parse(line);
148
+ }
149
+ catch {
150
+ throw unavailable();
151
+ }
152
+ if (typeof event === "object" &&
153
+ event !== null &&
154
+ "type" in event &&
155
+ event.type === "control_response") {
156
+ return choices(event, requestId);
188
157
  }
189
- newline = output.indexOf("\n");
190
158
  }
159
+ throw unavailable();
191
160
  }
192
- throw unavailable();
193
- }
194
- catch {
195
- throw unavailable();
196
- }
197
- finally {
198
- clearTimeout(timer);
199
- if (forceTimer !== undefined)
200
- clearTimeout(forceTimer);
201
- terminate(false);
202
- const grace = await Promise.race([
203
- closed.then(() => true),
204
- new Promise((resolve) => setTimeout(() => resolve(false), 500)),
205
- ]);
206
- if (!grace) {
207
- terminate(true);
208
- await Promise.race([closed, new Promise((resolve) => setTimeout(resolve, 500))]);
161
+ catch {
162
+ throw unavailable();
209
163
  }
210
- await rm(directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
211
- }
164
+ });
212
165
  }
@@ -142,7 +142,11 @@ export function claudeCodeLlm(deps) {
142
142
  const read = new Set();
143
143
  let run;
144
144
  let ended;
145
+ // A call of several model turns reports its windows once per turn: the first is kept as
146
+ // the call's "before", the last as its "after".
147
+ let firstLimits;
145
148
  let limits;
149
+ let limitEvents = 0;
146
150
  try {
147
151
  images = imageWorkspace(req.images);
148
152
  const folder = images?.directory ?? documents?.directory;
@@ -191,8 +195,11 @@ export function claudeCodeLlm(deps) {
191
195
  // A shape this version does not know is not a failure: the plan windows are
192
196
  // reported beside the answer, never needed for it.
193
197
  const parsed = rateLimitEvent.safeParse(event.value);
194
- if (parsed.success)
198
+ if (parsed.success) {
199
+ firstLimits ??= parsed.data.rate_limit_info;
195
200
  limits = parsed.data.rate_limit_info;
201
+ limitEvents += 1;
202
+ }
196
203
  continue;
197
204
  }
198
205
  if (event.type === "assistant" && !documents) {
@@ -231,6 +238,7 @@ export function claudeCodeLlm(deps) {
231
238
  });
232
239
  throw providerError({
233
240
  kind: kindOf(result.api_error_status ?? null),
241
+ ...(result.api_error_status === 404 ? { access: "not-found" } : {}),
234
242
  message: cliReported(binary, redact(result.result ?? result.subtype), nextStep(binary, result.api_error_status ?? null)),
235
243
  });
236
244
  }
@@ -240,11 +248,14 @@ export function claudeCodeLlm(deps) {
240
248
  if ((documents || images) && result.result)
241
249
  yield { type: "delta", text: result.result };
242
250
  const windows = limits === undefined ? [] : claudeWindows(limits);
251
+ const start = limitEvents > 1 && firstLimits !== undefined ? claudeWindows(firstLimits) : [];
243
252
  yield {
244
253
  type: "done",
245
254
  usage: usageOf(result.modelUsage),
246
255
  finishReason: result.stop_reason ?? null,
247
- ...(windows.length === 0 ? {} : { limits: { after: windows } }),
256
+ ...(windows.length === 0
257
+ ? {}
258
+ : { limits: { ...(start.length === 0 ? {} : { before: start }), after: windows } }),
248
259
  };
249
260
  return;
250
261
  }