@gentbajko/slopify 3.25.8 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (604) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/3.25.9.md +5 -0
  195. package/dist/patch-notes/4.0.0.md +51 -0
  196. package/dist/patch-notes/index.json +12 -0
  197. package/dist/slices/admission/heads.js +2 -1
  198. package/dist/slices/admission/repo.js +2 -1
  199. package/dist/slices/admission/rules.js +43 -8
  200. package/dist/slices/admission/schema.js +8 -0
  201. package/dist/slices/admission/stage-writes.js +0 -3
  202. package/dist/slices/admission/start.js +48 -31
  203. package/dist/slices/admission/substitute.js +7 -4
  204. package/dist/slices/agents/attention-posts.js +31 -0
  205. package/dist/slices/agents/attention-templates.js +33 -0
  206. package/dist/slices/agents/attention.js +10 -1
  207. package/dist/slices/agents/connection.js +2 -6
  208. package/dist/slices/agents/model.js +6 -3
  209. package/dist/slices/agents/repo.js +15 -7
  210. package/dist/slices/article/continuation.js +0 -63
  211. package/dist/slices/article/plain.js +3 -2
  212. package/dist/slices/article/segments.js +2 -62
  213. package/dist/slices/assistant/access.js +1 -0
  214. package/dist/slices/assistant/asks.js +80 -0
  215. package/dist/slices/assistant/cost.js +54 -0
  216. package/dist/slices/assistant/gate.js +222 -0
  217. package/dist/slices/assistant/model.js +79 -0
  218. package/dist/slices/assistant/names.js +23 -0
  219. package/dist/slices/assistant/prompt.js +74 -0
  220. package/dist/slices/assistant/repo.js +74 -0
  221. package/dist/slices/assistant/tokens.js +18 -0
  222. package/dist/slices/assistant/turn.js +342 -0
  223. package/dist/slices/autopilot/buffer.js +76 -0
  224. package/dist/slices/autopilot/model.js +14 -0
  225. package/dist/slices/autopilot/notify.js +27 -0
  226. package/dist/slices/autopilot/repo.js +34 -0
  227. package/dist/slices/autopilot/signals.js +194 -0
  228. package/dist/slices/autopilot/warnings.js +85 -0
  229. package/dist/slices/autopilot/weekly.js +100 -0
  230. package/dist/slices/backups/files.js +2 -6
  231. package/dist/slices/backups/repo.js +3 -15
  232. package/dist/slices/backups/service.js +4 -3
  233. package/dist/slices/channels/cast-images.js +2 -1
  234. package/dist/slices/channels/model.js +0 -1
  235. package/dist/slices/channels/rebrand.js +0 -3
  236. package/dist/slices/channels/repo.js +4 -0
  237. package/dist/slices/channels/schema.js +6 -0
  238. package/dist/slices/channels/service.js +10 -0
  239. package/dist/slices/channels/videos.js +2 -14
  240. package/dist/slices/checkpoints/index.js +0 -1
  241. package/dist/slices/control/providers.js +0 -26
  242. package/dist/slices/designs/artwork.js +66 -0
  243. package/dist/slices/designs/budget.js +23 -0
  244. package/dist/slices/designs/calls.js +124 -0
  245. package/dist/slices/designs/check-context.js +38 -0
  246. package/dist/slices/designs/check-text.js +43 -0
  247. package/dist/slices/designs/fields.js +88 -0
  248. package/dist/slices/designs/frames.js +34 -0
  249. package/dist/slices/designs/gate.js +128 -0
  250. package/dist/slices/designs/layout.js +293 -0
  251. package/dist/slices/designs/model.js +35 -0
  252. package/dist/slices/designs/pdf-job.js +23 -0
  253. package/dist/slices/designs/pdf-pages.js +30 -0
  254. package/dist/slices/designs/pdf-runner.js +68 -0
  255. package/dist/slices/designs/pdf-worker.js +24 -0
  256. package/dist/slices/designs/picture-size.js +79 -0
  257. package/dist/slices/designs/pictures.js +55 -0
  258. package/dist/slices/designs/quote.js +24 -0
  259. package/dist/slices/designs/render-args.js +36 -0
  260. package/dist/slices/designs/render-input.js +59 -0
  261. package/dist/slices/designs/render.js +215 -0
  262. package/dist/slices/designs/repo.js +101 -0
  263. package/dist/slices/designs/requests.js +80 -0
  264. package/dist/slices/designs/runner.js +239 -0
  265. package/dist/slices/designs/sample-repo.js +91 -0
  266. package/dist/slices/designs/sample.js +222 -0
  267. package/dist/slices/designs/schema.js +87 -0
  268. package/dist/slices/designs/sections.js +44 -0
  269. package/dist/slices/designs/service.js +233 -0
  270. package/dist/slices/designs/steps-repo.js +102 -0
  271. package/dist/slices/designs/studio-kind.js +39 -0
  272. package/dist/slices/designs/template-checks.js +44 -0
  273. package/dist/slices/designs/template-make.js +118 -0
  274. package/dist/slices/designs/templates.js +218 -0
  275. package/dist/slices/document/fonts.js +2 -1
  276. package/dist/slices/episodes/summarize.js +5 -1
  277. package/dist/slices/estimate/index.js +45 -7
  278. package/dist/slices/fonts/catalog.js +3 -2
  279. package/dist/slices/fonts/discovery.js +3 -2
  280. package/dist/slices/fonts/files.js +2 -6
  281. package/dist/slices/fonts/index.js +0 -1
  282. package/dist/slices/fonts/sfnt.js +45 -10
  283. package/dist/slices/images/appearance.js +6 -15
  284. package/dist/slices/images/scenes.js +100 -27
  285. package/dist/slices/keywords/digest.js +84 -0
  286. package/dist/slices/keywords/facts.js +92 -0
  287. package/dist/slices/keywords/fill.js +80 -0
  288. package/dist/slices/keywords/model.js +75 -0
  289. package/dist/slices/keywords/rules.js +187 -0
  290. package/dist/slices/keywords/schema.js +28 -0
  291. package/dist/slices/keywords/specs.js +260 -0
  292. package/dist/slices/keywords/values.js +74 -0
  293. package/dist/slices/library/lint.js +29 -12
  294. package/dist/slices/library/model.js +2 -0
  295. package/dist/slices/library/photorealistic.js +4 -12
  296. package/dist/slices/library/rename.js +9 -7
  297. package/dist/slices/library/save.js +14 -10
  298. package/dist/slices/library/slots.js +37 -11
  299. package/dist/slices/library/transfer.js +2 -1
  300. package/dist/slices/library/used-by.js +25 -10
  301. package/dist/slices/loudness/loudnorm.js +51 -11
  302. package/dist/slices/model-checks/cost.js +61 -0
  303. package/dist/slices/model-checks/listing.js +124 -0
  304. package/dist/slices/model-checks/model.js +20 -0
  305. package/dist/slices/model-checks/record.js +32 -0
  306. package/dist/slices/model-checks/repo.js +120 -0
  307. package/dist/slices/model-checks/rules.js +112 -0
  308. package/dist/slices/model-checks/run.js +143 -0
  309. package/dist/slices/model-checks/service.js +198 -0
  310. package/dist/slices/model-upkeep/switch.js +70 -25
  311. package/dist/slices/model-upkeep/usage.js +26 -11
  312. package/dist/slices/narration/breaths.js +2 -1
  313. package/dist/slices/narration/clean-pieces.js +2 -1
  314. package/dist/slices/narration/concat.js +2 -1
  315. package/dist/slices/narration/peaks.js +3 -4
  316. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  317. package/dist/slices/narration/pronunciation.js +3 -2
  318. package/dist/slices/narration/steering.js +4 -3
  319. package/dist/slices/notifications/notifier.js +4 -3
  320. package/dist/slices/notifications/rules.js +5 -2
  321. package/dist/slices/notifications/settings.js +4 -12
  322. package/dist/slices/notifications/subject.js +2 -1
  323. package/dist/slices/onboarding/copy.js +2 -1
  324. package/dist/slices/onboarding/first-run.js +3 -2
  325. package/dist/slices/onboarding/full-video.js +3 -2
  326. package/dist/slices/onboarding/install.js +4 -3
  327. package/dist/slices/onboarding/model.js +0 -16
  328. package/dist/slices/onboarding/packs.js +22 -22
  329. package/dist/slices/onboarding/quick-short.js +2 -1
  330. package/dist/slices/onboarding/sample.js +0 -1
  331. package/dist/slices/onboarding/state.js +2 -11
  332. package/dist/slices/onboarding/template.js +2 -1
  333. package/dist/slices/patch-notes/library.js +3 -2
  334. package/dist/slices/play-drafts/convert.js +4 -0
  335. package/dist/slices/play-drafts/review-inputs.js +8 -2
  336. package/dist/slices/play-drafts/review.js +5 -4
  337. package/dist/slices/play-drafts/schema.js +4 -0
  338. package/dist/slices/project-templates/from-project.js +9 -2
  339. package/dist/slices/project-templates/repo.js +6 -1
  340. package/dist/slices/project-templates/setup.js +8 -0
  341. package/dist/slices/project-templates/stand-ins.js +22 -0
  342. package/dist/slices/rebuild/activity.js +2 -0
  343. package/dist/slices/rebuild/admission-repo.js +3 -2
  344. package/dist/slices/rebuild/narration-history.js +4 -5
  345. package/dist/slices/rebuild/narration-retry.js +2 -1
  346. package/dist/slices/rebuild/preview-details.js +27 -9
  347. package/dist/slices/rebuild/preview-plan.js +3 -8
  348. package/dist/slices/rebuild/recipe-build.js +8 -3
  349. package/dist/slices/rebuild/recipe-describe.js +3 -3
  350. package/dist/slices/rebuild/recipe-digest.js +58 -0
  351. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  352. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  353. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  354. package/dist/slices/rebuild/recipe-model.js +4 -0
  355. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  356. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  357. package/dist/slices/rebuild/recipe-reference.js +8 -4
  358. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  359. package/dist/slices/rebuild/recipe-save.js +6 -3
  360. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  361. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  362. package/dist/slices/rebuild/recipe-social.js +46 -0
  363. package/dist/slices/rebuild/recipe-text.js +64 -20
  364. package/dist/slices/rebuild/recipe-validation.js +98 -28
  365. package/dist/slices/rebuild/recipe-visual.js +55 -16
  366. package/dist/slices/rebuild/recipe-work.js +15 -3
  367. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  368. package/dist/slices/rebuild/recovery-selection.js +3 -1
  369. package/dist/slices/rebuild/recovery.js +3 -2
  370. package/dist/slices/rebuild/repo.js +4 -3
  371. package/dist/slices/rebuild/review-promise.js +25 -0
  372. package/dist/slices/rebuild/review-redo.js +2 -1
  373. package/dist/slices/rebuild/runtime-admission.js +4 -3
  374. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  375. package/dist/slices/rebuild/runtime-animate.js +9 -8
  376. package/dist/slices/rebuild/runtime-article.js +3 -2
  377. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  378. package/dist/slices/rebuild/runtime-document.js +8 -11
  379. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  380. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  381. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  382. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  383. package/dist/slices/rebuild/runtime-export.js +34 -12
  384. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  385. package/dist/slices/rebuild/runtime-image.js +4 -9
  386. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  387. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  388. package/dist/slices/rebuild/runtime-lines.js +2 -11
  389. package/dist/slices/rebuild/runtime-local.js +14 -20
  390. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  391. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  392. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  393. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  394. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  395. package/dist/slices/rebuild/runtime-plan.js +31 -5
  396. package/dist/slices/rebuild/runtime-provider.js +60 -19
  397. package/dist/slices/rebuild/runtime-publication.js +10 -9
  398. package/dist/slices/rebuild/runtime-review.js +33 -28
  399. package/dist/slices/rebuild/runtime-run.js +4 -1
  400. package/dist/slices/rebuild/runtime-shorts.js +34 -50
  401. package/dist/slices/rebuild/runtime-store.js +3 -8
  402. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  403. package/dist/slices/rebuild/runtime-voices.js +8 -15
  404. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  405. package/dist/slices/rebuild/transition-repo.js +6 -5
  406. package/dist/slices/rebuild/work-records.js +6 -0
  407. package/dist/slices/reruns/cascade.js +1 -2
  408. package/dist/slices/reruns/index.js +6 -9
  409. package/dist/slices/reviews/external-model.js +3 -0
  410. package/dist/slices/reviews/external-repo.js +2 -1
  411. package/dist/slices/reviews/external-rows.js +2 -3
  412. package/dist/slices/reviews/model.js +26 -8
  413. package/dist/slices/reviews/repo.js +2 -1
  414. package/dist/slices/reviews/rules.js +1 -0
  415. package/dist/slices/reviews/view.js +5 -2
  416. package/dist/slices/revisions/downloads.js +4 -5
  417. package/dist/slices/revisions/mutation-assets.js +7 -4
  418. package/dist/slices/revisions/mutation-cues.js +2 -5
  419. package/dist/slices/revisions/mutations.js +0 -1
  420. package/dist/slices/revisions/repo.js +3 -1
  421. package/dist/slices/revisions/schema.js +1 -0
  422. package/dist/slices/revisions/view.js +20 -0
  423. package/dist/slices/run-cost/limits.js +5 -2
  424. package/dist/slices/run-cost/meter.js +12 -5
  425. package/dist/slices/run-cost/panel.js +8 -74
  426. package/dist/slices/run-cost/plan-readings.js +127 -0
  427. package/dist/slices/run-cost/plan-use.js +169 -0
  428. package/dist/slices/run-cost/pricing.js +3 -3
  429. package/dist/slices/run-cost/week.js +32 -4
  430. package/dist/slices/schedules/agenda.js +11 -2
  431. package/dist/slices/schedules/by-date.js +42 -0
  432. package/dist/slices/schedules/calendar.js +5 -0
  433. package/dist/slices/schedules/design-agenda.js +36 -0
  434. package/dist/slices/schedules/design-check.js +41 -0
  435. package/dist/slices/schedules/design-run.js +126 -0
  436. package/dist/slices/schedules/next-run.js +62 -0
  437. package/dist/slices/schedules/prepare.js +18 -2
  438. package/dist/slices/schedules/repo.js +24 -16
  439. package/dist/slices/schedules/rescore.js +35 -0
  440. package/dist/slices/schedules/scheduler.js +13 -3
  441. package/dist/slices/schedules/schema.js +54 -3
  442. package/dist/slices/schedules/score.js +107 -0
  443. package/dist/slices/schedules/service.js +57 -24
  444. package/dist/slices/schedules/topic-list.js +15 -0
  445. package/dist/slices/schedules/topic-scores.js +104 -0
  446. package/dist/slices/schedules/topics.js +48 -36
  447. package/dist/slices/settings/cli-paths.js +4 -6
  448. package/dist/slices/settings/first-run.js +2 -11
  449. package/dist/slices/settings/model-reach.js +3 -17
  450. package/dist/slices/settings/model.js +10 -0
  451. package/dist/slices/settings/playback.js +33 -22
  452. package/dist/slices/settings/probe-read.js +16 -0
  453. package/dist/slices/settings/repo.js +33 -0
  454. package/dist/slices/settings/tutorial.js +2 -2
  455. package/dist/slices/settings/voices.js +4 -12
  456. package/dist/slices/shorts/pick.js +2 -15
  457. package/dist/slices/shorts/prompts.js +2 -13
  458. package/dist/slices/shorts/render.js +3 -0
  459. package/dist/slices/social/channel.js +44 -0
  460. package/dist/slices/social/defaults.js +17 -0
  461. package/dist/slices/social/due.js +36 -0
  462. package/dist/slices/social/extension.js +18 -0
  463. package/dist/slices/social/fit.js +115 -0
  464. package/dist/slices/social/limits.js +186 -0
  465. package/dist/slices/social/links.js +21 -0
  466. package/dist/slices/social/media.js +131 -0
  467. package/dist/slices/social/model.js +75 -0
  468. package/dist/slices/social/notice.js +19 -0
  469. package/dist/slices/social/pack.js +176 -0
  470. package/dist/slices/social/post-prompt.js +55 -0
  471. package/dist/slices/social/queue.js +31 -0
  472. package/dist/slices/social/repo.js +133 -0
  473. package/dist/slices/social/reshape.js +97 -0
  474. package/dist/slices/social/rules.js +5 -0
  475. package/dist/slices/social/schema.js +70 -0
  476. package/dist/slices/social/serve.js +51 -0
  477. package/dist/slices/social/service.js +128 -0
  478. package/dist/slices/social/settings.js +21 -0
  479. package/dist/slices/social/sources.js +114 -0
  480. package/dist/slices/social/state.js +110 -0
  481. package/dist/slices/social/text.js +149 -0
  482. package/dist/slices/storage/arrange.js +3 -1
  483. package/dist/slices/storage/assets.js +8 -10
  484. package/dist/slices/storage/backup-designs.js +74 -0
  485. package/dist/slices/storage/backup-export.js +8 -3
  486. package/dist/slices/storage/backup-format.js +10 -0
  487. package/dist/slices/storage/backup-import.js +20 -2
  488. package/dist/slices/storage/delete-project.js +6 -5
  489. package/dist/slices/storage/files-location.js +6 -8
  490. package/dist/slices/storage/layout.js +13 -2
  491. package/dist/slices/storage/model.js +3 -0
  492. package/dist/slices/storage/portable.js +16 -11
  493. package/dist/slices/storage/schema.js +2 -0
  494. package/dist/slices/storage/staging-refs.js +2 -1
  495. package/dist/slices/storage/staging.js +6 -9
  496. package/dist/slices/storage/trim.js +10 -3
  497. package/dist/slices/studio/calendar.js +4 -1
  498. package/dist/slices/studio/pick.js +2 -11
  499. package/dist/slices/studio/plan.js +6 -15
  500. package/dist/slices/studio/queue.js +3 -14
  501. package/dist/slices/studio/releases.js +26 -0
  502. package/dist/slices/studio/settings.js +14 -51
  503. package/dist/slices/studio/videos.js +12 -0
  504. package/dist/slices/style-preview/render.js +34 -13
  505. package/dist/slices/style-preview/schema.js +13 -0
  506. package/dist/slices/style-preview/service.js +2 -1
  507. package/dist/slices/style-preview/settings.js +33 -5
  508. package/dist/slices/telemetry/collector-client.js +2 -4
  509. package/dist/slices/telemetry/flush.js +2 -4
  510. package/dist/slices/telemetry/model.js +0 -7
  511. package/dist/slices/telemetry/record.js +10 -3
  512. package/dist/slices/template-studio/anatomy-view.js +118 -0
  513. package/dist/slices/template-studio/anatomy.js +73 -0
  514. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  515. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  516. package/dist/slices/template-studio/bakeoff.js +333 -0
  517. package/dist/slices/template-studio/budget.js +103 -0
  518. package/dist/slices/template-studio/checks.js +273 -0
  519. package/dist/slices/template-studio/document-sample.js +56 -0
  520. package/dist/slices/template-studio/edit.js +15 -0
  521. package/dist/slices/template-studio/for-all.js +161 -0
  522. package/dist/slices/template-studio/free-video.js +31 -0
  523. package/dist/slices/template-studio/free.js +176 -0
  524. package/dist/slices/template-studio/gate.js +40 -0
  525. package/dist/slices/template-studio/hidden.js +20 -0
  526. package/dist/slices/template-studio/jobs.js +79 -0
  527. package/dist/slices/template-studio/loudness.js +111 -0
  528. package/dist/slices/template-studio/make.js +40 -0
  529. package/dist/slices/template-studio/model.js +54 -0
  530. package/dist/slices/template-studio/pack-import.js +253 -0
  531. package/dist/slices/template-studio/pack-samples.js +60 -0
  532. package/dist/slices/template-studio/pack.js +205 -0
  533. package/dist/slices/template-studio/paid-view.js +66 -0
  534. package/dist/slices/template-studio/paid.js +348 -0
  535. package/dist/slices/template-studio/quote.js +148 -0
  536. package/dist/slices/template-studio/ready.js +104 -0
  537. package/dist/slices/template-studio/repo.js +134 -0
  538. package/dist/slices/template-studio/reviews-path.js +58 -0
  539. package/dist/slices/template-studio/runner.js +84 -0
  540. package/dist/slices/template-studio/save.js +236 -0
  541. package/dist/slices/template-studio/say-it-as.js +248 -0
  542. package/dist/slices/template-studio/schedule-gate.js +34 -0
  543. package/dist/slices/template-studio/schedule-price.js +30 -0
  544. package/dist/slices/template-studio/schedule-sample.js +87 -0
  545. package/dist/slices/template-studio/schema.js +218 -0
  546. package/dist/slices/template-studio/scope.js +124 -0
  547. package/dist/slices/template-studio/sections.js +176 -0
  548. package/dist/slices/template-studio/service.js +173 -0
  549. package/dist/slices/template-studio/status.js +174 -0
  550. package/dist/slices/template-studio/topic.js +223 -0
  551. package/dist/slices/trash/designs.js +76 -0
  552. package/dist/slices/trash/model.js +13 -4
  553. package/dist/slices/trash/service.js +52 -2
  554. package/dist/slices/tutorials/library.js +3 -2
  555. package/dist/slices/video/ass-text.js +17 -0
  556. package/dist/slices/video/audio-export.js +2 -64
  557. package/dist/slices/video/encoding.js +160 -0
  558. package/dist/slices/video/ffmpeg.js +23 -21
  559. package/dist/slices/video/figure-card.js +1 -15
  560. package/dist/slices/video/slideshow.js +43 -18
  561. package/dist/slices/voices/palette.js +0 -3
  562. package/dist/slices/youtube/answer.js +2 -15
  563. package/dist/slices/youtube/edits-repo.js +2 -11
  564. package/dist/tutorials/Agents-MCP.md +2 -1
  565. package/dist/tutorials/Assistant.md +42 -0
  566. package/dist/tutorials/Channels.md +2 -0
  567. package/dist/tutorials/Home-and-Projects.md +19 -0
  568. package/dist/tutorials/Install.md +1 -0
  569. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  570. package/dist/tutorials/Models.md +33 -1
  571. package/dist/tutorials/Notifications.md +6 -0
  572. package/dist/tutorials/Play-Images.md +4 -0
  573. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  574. package/dist/tutorials/Prompts.md +10 -6
  575. package/dist/tutorials/Remote-Access.md +63 -0
  576. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  577. package/dist/tutorials/Schedules.md +21 -1
  578. package/dist/tutorials/Settings-Reference.md +50 -8
  579. package/dist/tutorials/Template-Studio.md +125 -0
  580. package/dist/tutorials/Templates.md +3 -0
  581. package/dist/tutorials/_Sidebar.md +5 -0
  582. package/dist/updater/work-in-progress.js +42 -12
  583. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  584. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  585. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  586. package/dist/web/index.html +2 -2
  587. package/package.json +5 -1
  588. package/dist/slices/article/provided-entries.js +0 -66
  589. package/dist/slices/article/run.js +0 -145
  590. package/dist/slices/images/run.js +0 -234
  591. package/dist/slices/narration/legacy-plan.js +0 -58
  592. package/dist/slices/narration/run.js +0 -270
  593. package/dist/slices/rebuild/runtime-actions.js +0 -90
  594. package/dist/slices/research/run.js +0 -224
  595. package/dist/slices/subtitles/prepare.js +0 -194
  596. package/dist/slices/subtitles/transcript.js +0 -34
  597. package/dist/slices/thumbnail/run.js +0 -219
  598. package/dist/slices/video/audio-inputs.js +0 -33
  599. package/dist/slices/video/reuse-audio.js +0 -62
  600. package/dist/slices/video/run.js +0 -78
  601. package/dist/slices/video/write-export.js +0 -158
  602. package/dist/slices/video/write-subtitles.js +0 -59
  603. package/dist/web/assets/index-BadsoORk.css +0 -1
  604. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -0,0 +1,30 @@
1
+ import { statfs } from "node:fs/promises";
2
+ import { z } from "zod";
3
+ // The autopilot's hourly look at what may stop the schedules: the OpenRouter key's spending
4
+ // limit (`GET /api/v1/key`, the same read Settings → Providers' Test makes) and the free disk.
5
+ // Read-only: neither starts paid work.
6
+ const keyAnswer = z.object({
7
+ data: z.object({ limit: z.number().nullable(), limit_remaining: z.number().nullable() }).loose(),
8
+ });
9
+ // The key's limit and what is left of it; null for a key with no limit. Throws, with the
10
+ // status only, when OpenRouter refuses or answers something else: the caller skips the check.
11
+ export async function openRouterKeyLimit(fetchImpl, key, signal) {
12
+ const response = await fetchImpl("https://openrouter.ai/api/v1/key", {
13
+ headers: { Authorization: `Bearer ${key}` },
14
+ signal,
15
+ });
16
+ if (!response.ok)
17
+ throw new Error(`OpenRouter answered ${String(response.status)}`);
18
+ const parsed = keyAnswer.safeParse(await response.json());
19
+ if (!parsed.success)
20
+ throw new Error("OpenRouter's answer wasn't the key's limits");
21
+ const { limit, limit_remaining: remaining } = parsed.data.data;
22
+ return limit === null || remaining === null ? null : { limit, remaining };
23
+ }
24
+ export async function diskSpaceAt(path) {
25
+ const stats = await statfs(path);
26
+ return {
27
+ freeBytes: Number(stats.bavail) * Number(stats.bsize),
28
+ totalBytes: Number(stats.blocks) * Number(stats.bsize),
29
+ };
30
+ }
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import { Readable } from "node:stream";
6
6
  import { pipeline } from "node:stream/promises";
7
7
  import { setTimeout as delay } from "node:timers/promises";
8
+ import { hasErrnoCode } from "../../kernel/errors.js";
8
9
  import { defaultEnglishSubtitleModel, } from "../../kernel/ports/subtitles.js";
9
10
  // Xenova/wav2vec2-base-960h at one pinned revision, which carries the same model at three
10
11
  // precisions. Each keeps its own file name so the cache can hold any of them at once; int8
@@ -144,7 +145,7 @@ async function downloadModel(input, deps, target) {
144
145
  catch (error) {
145
146
  if (await verified(target, deps.model, input.signal))
146
147
  return target;
147
- if (!isCode(error, "EEXIST") && !isCode(error, "EPERM"))
148
+ if (!hasErrnoCode(error, "EEXIST") && !hasErrnoCode(error, "EPERM"))
148
149
  throw error;
149
150
  await rm(target, { force: true });
150
151
  await rename(part, target);
@@ -187,11 +188,8 @@ async function verified(path, model, signal) {
187
188
  return hash.digest("hex") === model.sha256;
188
189
  }
189
190
  catch (error) {
190
- if (isCode(error, "ENOENT"))
191
+ if (hasErrnoCode(error, "ENOENT"))
191
192
  return false;
192
193
  throw error;
193
194
  }
194
195
  }
195
- function isCode(error, code) {
196
- return error instanceof Error && "code" in error && error.code === code;
197
- }
@@ -1,6 +1,5 @@
1
- import { englishMismatch, englishSpec } from "./spec.js";
1
+ import { englishSpec } from "./spec.js";
2
2
  export const frameSeconds = 0.02;
3
- export const mismatch = englishMismatch;
4
3
  export function alignWindow(logits, frames, words, complete, spec = englishSpec) {
5
4
  const labels = spec.labels;
6
5
  if (frames < 1 || frames > 1100 || logits.length !== frames * labels)
@@ -1,6 +1,7 @@
1
1
  import { open, readFile, rm, stat } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { setTimeout } from "node:timers/promises";
4
+ import { hasErrnoCode } from "../../kernel/errors.js";
4
5
  // Models run one at a time per data directory, including requests from separate app
5
6
  // processes. A canceled waiter never removes another request's lock.
6
7
  export async function claimWorker(cacheDir, signal) {
@@ -26,7 +27,7 @@ export async function claimWorker(cacheDir, signal) {
26
27
  };
27
28
  }
28
29
  catch (error) {
29
- if (!(error instanceof Error && "code" in error && error.code === "EEXIST"))
30
+ if (!hasErrnoCode(error, "EEXIST"))
30
31
  throw error;
31
32
  }
32
33
  try {
@@ -41,7 +42,7 @@ export async function claimWorker(cacheDir, signal) {
41
42
  process.kill(owner, 0);
42
43
  }
43
44
  catch (error) {
44
- if (error instanceof Error && "code" in error && error.code === "ESRCH") {
45
+ if (hasErrnoCode(error, "ESRCH")) {
45
46
  await rm(path, { force: true });
46
47
  continue;
47
48
  }
@@ -50,7 +51,7 @@ export async function claimWorker(cacheDir, signal) {
50
51
  }
51
52
  }
52
53
  catch (error) {
53
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
54
+ if (!hasErrnoCode(error, "ENOENT"))
54
55
  throw error;
55
56
  }
56
57
  await setTimeout(250, undefined, { signal });
@@ -121,9 +121,6 @@ export const multilingualGates = {
121
121
  anchorLetters: 20,
122
122
  anchorConfidence: 0.75,
123
123
  };
124
- export function multilingualLanguages() {
125
- return Object.keys(alphabets);
126
- }
127
124
  // The model and language view the aligner uses for a project language. English keeps its own
128
125
  // model and every constant it had.
129
126
  export function alignmentSpecFor(language) {
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readFile, rm } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
+ import { hasErrnoCode } from "../../kernel/errors.js";
3
4
  import { defaultEnglishSubtitleModel, } from "../../kernel/ports/subtitles.js";
4
5
  import { englishModels, prepareModel } from "./cache.js";
5
6
  import { claimWorker } from "./lock.js";
@@ -31,7 +32,7 @@ async function clearOwnLock(cacheDir) {
31
32
  owner = await readFile(path, "utf8");
32
33
  }
33
34
  catch (error) {
34
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
35
+ if (hasErrnoCode(error, "ENOENT"))
35
36
  return;
36
37
  throw error;
37
38
  }
@@ -1,4 +1,5 @@
1
1
  import { open, readFile, stat } from "node:fs/promises";
2
+ import { errorText } from "../../kernel/errors.js";
2
3
  import { SubtitleMismatch } from "../../kernel/ports/subtitles.js";
3
4
  import { alignmentSpecFor } from "./multilingual.js";
4
5
  import { workerInput } from "./protocol.js";
@@ -15,7 +16,7 @@ process.once("message", (raw) => {
15
16
  }
16
17
  run(input.data).then((words) => send({ type: "done", words }), (error) => send({
17
18
  type: "error",
18
- message: error instanceof Error ? error.message : String(error),
19
+ message: errorText(error),
19
20
  ...(error instanceof SubtitleMismatch
20
21
  ? { mismatch: { at: error.at, expected: error.expected, heard: error.heard } }
21
22
  : {}),
@@ -58,9 +58,6 @@ export function refusedImage(provider, detail) {
58
58
  export function voiceFix(voiceId) {
59
59
  return `Check that voice "${voiceId}" still exists in Settings → Voices and that each narration part fits the model's length limit (Chunking, in the Providers section of Edit project), then use Try again.`;
60
60
  }
61
- export function internalError(what) {
62
- return `Slopify hit an internal error (${what}). Use Try again; if it happens again, use Download diagnostics in Settings and report it.`;
63
- }
64
61
  const cliNames = {
65
62
  claude: "Claude Code CLI",
66
63
  "claude-code": "Claude Code CLI",
@@ -4,6 +4,7 @@ import { createRequire } from "node:module";
4
4
  import { join } from "node:path";
5
5
  import { promisify } from "node:util";
6
6
  import { z } from "zod";
7
+ import { errorText } from "../kernel/errors.js";
7
8
  import { redact } from "../kernel/log.js";
8
9
  // ffmpeg-static exports a path even when npm skipped its install script. Recover using
9
10
  // that dependency's own downloader, including its source notice and license, without
@@ -73,6 +74,23 @@ async function verify(bin) {
73
74
  throw new Error(`ffmpeg at ${bin} could not be started. Reinstall ffmpeg or check your antivirus has not quarantined it, then restart Slopify. ${remedy} ${detail(error)}`);
74
75
  }
75
76
  }
77
+ // One short ffmpeg run to its end, answering its stdout; a failure carries the last thing it
78
+ // said on stderr. For questions about an ffmpeg (its encoders, a test encode), never renders.
79
+ export async function ffmpegOutput(bin, args) {
80
+ try {
81
+ const { stdout } = await promisify(execFile)(bin, [...args], {
82
+ windowsHide: true,
83
+ timeout: 30_000,
84
+ maxBuffer: 4 * 1024 * 1024,
85
+ });
86
+ return stdout;
87
+ }
88
+ catch (error) {
89
+ const stderr = z.object({ stderr: z.string() }).safeParse(error);
90
+ const said = stderr.success ? stderr.data.stderr.trim().split("\n").slice(-2).join(" / ") : "";
91
+ throw new Error(said === "" ? detail(error) : redact(said));
92
+ }
93
+ }
76
94
  function detail(error) {
77
- return redact(error instanceof Error ? error.message : String(error));
95
+ return redact(errorText(error));
78
96
  }
@@ -1,10 +1,11 @@
1
1
  import { redact } from "../../kernel/log.js";
2
2
  import { bridgeLimits, decodeHostImageReport, hostFaultSchema, hostFrameSchema, hostHealthSchema, hostImageReportHeader, hostImageSchema, hostLlmSchema, hostModelsSchema, hostOpenedSchema, hostOpenFolderSchema, hostStatusSchema, } from "../../kernel/ports/host-cli.js";
3
3
  import { agentImageTimeoutMs } from "../../kernel/ports/image.js";
4
- import { isProviderError, providerError } from "../../kernel/ports/model.js";
4
+ import { isProviderError, providerError, } from "../../kernel/ports/model.js";
5
5
  import { sniffImage } from "../image/bytes.js";
6
6
  import { claudePlanLimit } from "../llm/claude-code.js";
7
7
  import { codexPlanLimit } from "../llm/codex-limits.js";
8
+ import { accessField } from "../model-access.js";
8
9
  import { hostRequest, hostUnavailable, readHostBytes, } from "./transport.js";
9
10
  const tooBigForHost = "Slopify could not send this request to the host helper because it is larger than, or shaped differently from, what the helper accepts. Make the inputs shorter in Edit project, then use Try again; if it happens again, use Download diagnostics in Settings and report it.";
10
11
  export function createHostCliClient(options) {
@@ -16,7 +17,7 @@ export function createHostCliClient(options) {
16
17
  try {
17
18
  const parsed = hostFaultSchema.safeParse(JSON.parse((await readHostBytes(response, bridgeLimits.status)).toString("utf8")));
18
19
  if (parsed.success)
19
- throw providerError({ kind: parsed.data.kind, message: redact(parsed.data.message) });
20
+ throw bridgedFault(parsed.data.kind, parsed.data.message);
20
21
  }
21
22
  catch (error) {
22
23
  if (isProviderError(error))
@@ -163,6 +164,16 @@ export function createHostCliClient(options) {
163
164
  base64: Buffer.from(req.reference.bytes).toString("base64"),
164
165
  },
165
166
  }),
167
+ ...(req.edit === undefined
168
+ ? {}
169
+ : {
170
+ edit: {
171
+ source: {
172
+ mime: req.edit.source.mime,
173
+ base64: Buffer.from(req.edit.source.bytes).toString("base64"),
174
+ },
175
+ },
176
+ }),
166
177
  ...(req.cast === undefined
167
178
  ? {}
168
179
  : {
@@ -216,6 +227,11 @@ export function createHostCliClient(options) {
216
227
  },
217
228
  };
218
229
  }
230
+ // A fault from the host helper carries its kind and the host adapter's words only; whether it
231
+ // was about the model's access is read again from those words, as the plan limit is below.
232
+ export function bridgedFault(kind, message) {
233
+ return providerError({ kind, message: redact(message), ...accessField(null, message) });
234
+ }
219
235
  // A used-up plan crosses the bridge as a rate-limit fault carrying the CLI's own words, which
220
236
  // the host-side adapter already quoted; they are read again here so a Docker run waits for the
221
237
  // reset like a local one instead of failing.
@@ -256,7 +272,7 @@ async function* readEvents(response) {
256
272
  throw hostUnavailable(true);
257
273
  const event = hostFrameSchema.parse(JSON.parse(line));
258
274
  if (event.type === "error")
259
- throw providerError({ kind: event.kind, message: redact(event.message) });
275
+ throw bridgedFault(event.kind, event.message);
260
276
  if (event.type === "done")
261
277
  terminal = event;
262
278
  else
@@ -0,0 +1,53 @@
1
+ import { providerError } from "../kernel/ports/model.js";
2
+ import { missingKey } from "./explain.js";
3
+ import { retryAfter } from "./retry-after.js";
4
+ // The plumbing every HTTP provider adapter repeats around a failed call. What the provider
5
+ // said and which sentence it earns stay in each adapter; only the shape is shared.
6
+ export function safeJson(text) {
7
+ try {
8
+ return JSON.parse(text);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ // The body of a failed response, or nothing when it cannot be read.
15
+ export async function failureText(response) {
16
+ return await response.text().catch(() => "");
17
+ }
18
+ // Only the adapter sees the vendor's status code, so only the adapter names the kind.
19
+ export function kindOfStatus(status) {
20
+ if (status === 401 || status === 403) {
21
+ return "auth";
22
+ }
23
+ if (status === 429) {
24
+ return "rate_limit";
25
+ }
26
+ // ceiling: everything else is `other` and is retried, so a request the provider will never
27
+ // accept fails the same way four times over. A terminal "this will never work" kind has to
28
+ // reach the port's error contract first, which is not an adapter's to widen.
29
+ // The provider's own server failed; the same request may well succeed later.
30
+ if (status >= 500)
31
+ return "dropped";
32
+ return "other";
33
+ }
34
+ // `missing_key`, not `auth`, because that rule makes it terminal: an absent key never becomes
35
+ // a request.
36
+ export function requireKey(key, provider) {
37
+ if (key === undefined || key === "") {
38
+ throw providerError({ kind: "missing_key", message: missingKey(provider) });
39
+ }
40
+ return key;
41
+ }
42
+ // A 429's Retry-After replaces the fixed backoff; `fallbackRetryMs` is the provider's own
43
+ // hint, used when the header is absent. `access` is what the failure says about the chosen
44
+ // model (see model-access.ts); it never changes the kind.
45
+ export function httpProviderError(response, kind, message, fallbackRetryMs, access) {
46
+ const retryAfterMs = retryAfter(response.headers.get("retry-after")) ?? fallbackRetryMs;
47
+ return providerError({
48
+ kind,
49
+ message,
50
+ ...(access === undefined ? {} : { access }),
51
+ ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
52
+ });
53
+ }
@@ -11,8 +11,9 @@ import { codexLimitError, readingOf } from "../llm/codex.js";
11
11
  import { codexPlanLimit } from "../llm/codex-limits.js";
12
12
  import { cliEvent, cliShaped, endedKind, endedWithout, stopCliRun, } from "../llm/run-cli.js";
13
13
  import { lines } from "../llm/sse-lines.js";
14
+ import { accessField } from "../model-access.js";
14
15
  import { codexGeneratedImage, codexImageCount } from "./codex-output.js";
15
- import { referencePictures } from "./reference.js";
16
+ import { attachedPictures, editNote } from "./reference.js";
16
17
  // "Codex default" puts no model and no effort on the command line, so the CLI's own defaults
17
18
  // draw the image; it is what every project saved before the choice existed runs. Every other
18
19
  // model id is one of the Codex CLI's own models - the list the Codex text provider shows -
@@ -46,7 +47,7 @@ const alwaysDisabled = [
46
47
  function reviews(req) {
47
48
  return (req.model !== codexImageModel.id ||
48
49
  req.thinking !== undefined ||
49
- referencePictures(req).length > 0);
50
+ attachedPictures(req).length > 0);
50
51
  }
51
52
  // The binary's image tool (`ImagegenArgs`: prompt, referenced_image_paths,
52
53
  // num_last_images_to_include) takes each referenced image as an absolute path it reads
@@ -57,10 +58,12 @@ export function codexReferencePath(directory, image) {
57
58
  // Every picture the request carries, each at its own path: the establishing image keeps the
58
59
  // name it always had, the cast pictures follow it as reference-2, reference-3 and so on.
59
60
  export function codexReferencePaths(directory, req) {
60
- return referencePictures(req).map((picture, index) => ({
61
+ return attachedPictures(req).map((picture, index) => ({
61
62
  picture,
62
63
  path: index === 0
63
- ? codexReferencePath(directory, picture.image)
64
+ ? req.edit === undefined
65
+ ? codexReferencePath(directory, picture.image)
66
+ : join(directory, picture.image.mime === "image/jpeg" ? "source.jpg" : "source.png")
64
67
  : join(directory, `reference-${String(index + 1)}${picture.image.mime === "image/jpeg" ? ".jpg" : ".png"}`),
65
68
  }));
66
69
  }
@@ -78,7 +81,34 @@ function describeMember(req, name) {
78
81
  const description = req.cast?.find((member) => member.name === name)?.description.trim() ?? "";
79
82
  return description === "" ? "" : ` (${description})`;
80
83
  }
84
+ // An edit's instructions: the first path is the picture to change, the rest are the cast.
85
+ function editInstructions(req, directory) {
86
+ const paths = codexReferencePaths(directory, req);
87
+ return [
88
+ `The picture to change is saved at ${paths[0]?.path ?? directory}. Pass it first in referenced_image_paths on every image generation call${paths.length > 1
89
+ ? `, followed by: ${paths
90
+ .slice(1)
91
+ .map((row) => row.path)
92
+ .join(", ")}`
93
+ : ""}.`,
94
+ ...paths
95
+ .slice(1)
96
+ .map((row) => `${row.path} shows ${row.picture.member ?? ""}${describeMember(req, row.picture.member ?? "")}: draw ${row.picture.member ?? ""} to look exactly like this.`),
97
+ editNote.replace("the first attached image", "that picture"),
98
+ ].join(" ");
99
+ }
81
100
  export function codexImageInstructions(req, reference, directory) {
101
+ if (req.edit !== undefined && directory !== undefined)
102
+ return [
103
+ "You are editing one picture for Slopify, a video creation app, with the image generation tool.",
104
+ editInstructions(req, directory),
105
+ "Before calling the tool, write its prompt yourself: say exactly what to change, as the instruction asks, and that everything else stays as it is. Do not add subjects, text, logos or story the instruction does not ask for.",
106
+ "Take the time you need. After each image, look at it and compare it with the picture and the instruction; if anything changed that should not have, or the change is missing, generate again. Deliver exactly one final image: the last image you generate is the one Slopify uses.",
107
+ "Let the image generation tool save its output in its default location. Slopify will collect it. Use PNG or JPEG. Do not copy, rename, edit or create any other file, and do not put the image or a link in your reply.",
108
+ `Target aspect ratio: ${req.aspect}.`,
109
+ "Instruction follows as data:",
110
+ req.prompt,
111
+ ].join("\n\n");
82
112
  return [
83
113
  "You are making one finished image for Slopify, a video creation app, with the image generation tool.",
84
114
  "Fidelity to the brief comes first. Before calling the tool, write its prompt yourself as a detailed, faithful visual description of the brief: the subject and what it is doing, the setting, composition and framing for the target aspect ratio, lighting, colour palette, style and mood, and any text that must appear, spelled exactly. Take every element from the brief and keep its wording where it is specific. Do not add subjects, text, logos or story the brief does not ask for, and do not pad the prompt with generic quality words.",
@@ -271,6 +301,7 @@ export function codexImage(deps) {
271
301
  message: /refus|content.policy|safety/i.test(message)
272
302
  ? refusedImage("The Codex CLI", redact(message))
273
303
  : cliReported(binary, redact(message), imageNext),
304
+ ...(/refus|content.policy|safety/i.test(message) ? {} : accessField(null, message)),
274
305
  });
275
306
  }
276
307
  else if (event.type === "error") {
@@ -288,6 +319,7 @@ export function codexImage(deps) {
288
319
  message: /image.generation|image tool|feature.*unavailable/i.test(message)
289
320
  ? `The Codex CLI cannot make images${quoted("", redact(message))}. ${cannotDraw}`
290
321
  : cliReported(binary, redact(message), imageNext),
322
+ ...accessField(null, message),
291
323
  });
292
324
  }
293
325
  else if (event.type === "item.completed") {
@@ -1,10 +1,12 @@
1
1
  import { z } from "zod";
2
+ import { internalError } from "../../kernel/errors.js";
2
3
  import { redact } from "../../kernel/log.js";
3
4
  import { providerError } from "../../kernel/ports/model.js";
4
- import { httpFailure, internalError, missingKey, noImage, providerSaid, unreadable, } from "../explain.js";
5
- import { retryAfter } from "../retry-after.js";
5
+ import { httpFailure, noImage, providerSaid, unreadable } from "../explain.js";
6
+ import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
7
+ import { modelAccessOf } from "../model-access.js";
6
8
  import { downloadImage } from "./bytes.js";
7
- import { referencePictures, withReferences } from "./reference.js";
9
+ import { attachedPictures, promptFor } from "./reference.js";
8
10
  import { dataUri, downloadVideo } from "./video.js";
9
11
  // The HTTP gateway adapter for fal.ai: `fetch` and the downloader beside this file, no SDK.
10
12
  // `@fal-ai/client` would buy queue polling, which only the video clips need and which is three
@@ -60,6 +62,7 @@ const editModels = [
60
62
  function editEndpoint(model) {
61
63
  return editModels.includes(model) ? `${model}/edit` : undefined;
62
64
  }
65
+ export const falEditRefusal = "This fal.ai model can't edit a picture. Choose FLUX.2 or Nano Banana 2 on fal.ai, or Codex CLI, OpenAI or Google, under Made with … · change.";
63
66
  // Image-to-video runs on fal's queue, not the synchronous host: a clip takes one to five
64
67
  // minutes, and fal documents the queue as the way to call anything that slow. A request is
65
68
  // submitted, its status polled until it completes, and its answer fetched from the links the
@@ -104,8 +107,10 @@ export function falImage(deps) {
104
107
  // `image_urls`; only the models that have one are marked as taking a reference.
105
108
  // Cast pictures ride the same list; a model without an edit endpoint gets the cast in
106
109
  // words instead, since only the establishing image is something the user turned on.
107
- const pictures = referencePictures(req);
110
+ const pictures = attachedPictures(req);
108
111
  const edit = pictures.length === 0 ? undefined : editEndpoint(req.model);
112
+ if (req.edit !== undefined && edit === undefined)
113
+ throw providerError({ kind: "unsupported", message: falEditRefusal });
109
114
  if (req.reference !== undefined && edit === undefined)
110
115
  throw providerError({
111
116
  kind: "unsupported",
@@ -116,7 +121,7 @@ export function falImage(deps) {
116
121
  signal: req.signal,
117
122
  headers: { Authorization: `Key ${keyOf(deps)}`, "Content-Type": "application/json" },
118
123
  body: JSON.stringify({
119
- prompt: withReferences(req.prompt, req, edit === undefined ? 0 : pictures.length),
124
+ prompt: promptFor(req.prompt, req, edit === undefined ? 0 : pictures.length),
120
125
  ...(edit === undefined
121
126
  ? {}
122
127
  : { image_urls: pictures.map((picture) => dataUri(picture.image)) }),
@@ -129,7 +134,7 @@ export function falImage(deps) {
129
134
  }),
130
135
  });
131
136
  if (!response.ok) {
132
- throw await failure(response);
137
+ throw await failure(response, "image request", true);
133
138
  }
134
139
  const answer = parse(await response.text());
135
140
  refused(answer, req.prompt);
@@ -162,7 +167,7 @@ export function falImage(deps) {
162
167
  }),
163
168
  });
164
169
  if (!submitted.ok)
165
- throw await failure(submitted, "video clip request");
170
+ throw await failure(submitted, "video clip request", true);
166
171
  const links = read(queued, await submitted.text());
167
172
  for (;;) {
168
173
  const status = await deps.fetch(links.status_url, { signal: req.signal, headers });
@@ -198,12 +203,7 @@ function read(schema, text) {
198
203
  return parsed.data;
199
204
  }
200
205
  function keyOf(deps) {
201
- // `missing_key`, not `auth`, because that rule makes it terminal.
202
- const key = deps.key();
203
- if (key === undefined || key === "") {
204
- throw providerError({ kind: "missing_key", message: missingKey("fal.ai") });
205
- }
206
- return key;
206
+ return requireKey(deps.key(), "fal.ai");
207
207
  }
208
208
  // A content-policy refusal is final, named here and made terminal by the wrapper. fal declines
209
209
  // inside a 200 - the safety checker flags the image and hands back a blank one. With no
@@ -217,37 +217,12 @@ function refused(answer, prompt) {
217
217
  });
218
218
  }
219
219
  }
220
- // Only the adapter sees the vendor's status code, so only the adapter names the kind.
221
- function kindOf(status) {
222
- if (status === 401 || status === 403) {
223
- return "auth";
224
- }
225
- if (status === 429) {
226
- return "rate_limit";
227
- }
228
- // ceiling: everything else is `other` and is retried, so a 422 naming an input this model
229
- // does not take fails the same way four times over. A terminal "this will never work" kind
230
- // has to reach the port's error contract first, which is not this adapter's to widen.
231
- // The provider's own server failed; the same request may well succeed later.
232
- if (status >= 500)
233
- return "dropped";
234
- return "other";
235
- }
236
- async function failure(response, subject = "image request") {
237
- const text = await response.text().catch(() => "");
220
+ // `atModel`: the request went to the model's own address, so a 404 or 403 is about the model.
221
+ async function failure(response, subject = "image request", atModel = false) {
222
+ const text = await failureText(response);
238
223
  // The provider's own words, through the redactor - an error body may quote the key back.
239
224
  const message = redact(detailOf(text) || response.statusText);
240
- const retryAfterMs = retryAfter(response.headers.get("retry-after"));
241
- return providerError({
242
- kind: kindOf(response.status),
243
- message: httpFailure({
244
- provider: "fal.ai",
245
- status: response.status,
246
- detail: message,
247
- subject,
248
- }),
249
- ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
250
- });
225
+ return httpProviderError(response, kindOfStatus(response.status), httpFailure({ provider: "fal.ai", status: response.status, detail: message, subject }), undefined, modelAccessOf(response.status, text, atModel));
251
226
  }
252
227
  function detailOf(text) {
253
228
  const parsed = errorBody.safeParse(safeJson(text));
@@ -270,11 +245,3 @@ function parse(text) {
270
245
  }
271
246
  return parsed.data;
272
247
  }
273
- function safeJson(text) {
274
- try {
275
- return JSON.parse(text);
276
- }
277
- catch {
278
- return undefined;
279
- }
280
- }