@gentbajko/slopify 3.25.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/cache.js +3 -5
  3. package/dist/adapters/alignment/ctc.js +1 -2
  4. package/dist/adapters/alignment/lock.js +4 -3
  5. package/dist/adapters/alignment/multilingual.js +0 -3
  6. package/dist/adapters/alignment/prefetch.js +2 -1
  7. package/dist/adapters/alignment/worker.js +2 -1
  8. package/dist/adapters/explain.js +0 -3
  9. package/dist/adapters/ffmpeg.js +19 -1
  10. package/dist/adapters/host-cli/index.js +19 -3
  11. package/dist/adapters/http-failure.js +53 -0
  12. package/dist/adapters/image/codex.js +36 -4
  13. package/dist/adapters/image/fal.js +17 -50
  14. package/dist/adapters/image/google.js +31 -55
  15. package/dist/adapters/image/openai.js +20 -47
  16. package/dist/adapters/image/reference.js +36 -0
  17. package/dist/adapters/image/replicate.js +18 -47
  18. package/dist/adapters/interest/search-interest.js +113 -0
  19. package/dist/adapters/key-probes.js +53 -2
  20. package/dist/adapters/llm/antigravity.js +4 -0
  21. package/dist/adapters/llm/assistant-cli.js +388 -0
  22. package/dist/adapters/llm/child-session.js +83 -0
  23. package/dist/adapters/llm/claude-code-models.js +51 -98
  24. package/dist/adapters/llm/claude-code.js +13 -2
  25. package/dist/adapters/llm/codex-limits.js +27 -68
  26. package/dist/adapters/llm/codex-server-models.js +61 -109
  27. package/dist/adapters/llm/codex.js +12 -1
  28. package/dist/adapters/llm/openrouter.js +10 -47
  29. package/dist/adapters/llm/run-cli.js +6 -22
  30. package/dist/adapters/llm/sse-lines.js +5 -1
  31. package/dist/adapters/model-access.js +37 -0
  32. package/dist/adapters/tts/cartesia.js +15 -46
  33. package/dist/adapters/tts/elevenlabs.js +20 -52
  34. package/dist/adapters/tts/gemini.js +19 -40
  35. package/dist/adapters/tts/inworld-async.js +4 -1
  36. package/dist/adapters/tts/inworld.js +3 -0
  37. package/dist/adapters/tts/openai.js +20 -50
  38. package/dist/assets/models-lifecycle.yaml +6 -0
  39. package/dist/assistant-wiring.js +60 -0
  40. package/dist/autopilot-timers.js +59 -0
  41. package/dist/catalog/lifecycle.js +52 -0
  42. package/dist/catalog/propose.js +204 -0
  43. package/dist/catalog/registry.js +3 -1
  44. package/dist/catalog/schema.js +2 -0
  45. package/dist/catalog/store.js +39 -0
  46. package/dist/edge/autostart/native.js +3 -5
  47. package/dist/edge/autostart/prompt.js +2 -1
  48. package/dist/edge/autostart/service.js +3 -4
  49. package/dist/edge/cli-args.js +3 -4
  50. package/dist/edge/cli.js +2 -1
  51. package/dist/edge/docker-install/apply.js +11 -11
  52. package/dist/edge/docker-install/run.js +2 -1
  53. package/dist/edge/docker-install/state.js +4 -3
  54. package/dist/edge/docker-install/tree.js +2 -4
  55. package/dist/edge/docker-install/volume.js +3 -2
  56. package/dist/edge/docker.js +5 -4
  57. package/dist/edge/events/hub.js +5 -5
  58. package/dist/edge/http/actions.js +4 -25
  59. package/dist/edge/http/actor.js +5 -3
  60. package/dist/edge/http/app.js +85 -10
  61. package/dist/edge/http/assistant.js +127 -0
  62. package/dist/edge/http/audio-preview.js +4 -11
  63. package/dist/edge/http/auditions.js +1 -1
  64. package/dist/edge/http/autostart.js +5 -5
  65. package/dist/edge/http/backups.js +1 -3
  66. package/dist/edge/http/channel-memory.js +7 -20
  67. package/dist/edge/http/channels.js +13 -26
  68. package/dist/edge/http/checkpoints.js +33 -17
  69. package/dist/edge/http/design-templates.js +167 -0
  70. package/dist/edge/http/designs.js +178 -0
  71. package/dist/edge/http/diagnostics.js +5 -1
  72. package/dist/edge/http/document-themes.js +2 -11
  73. package/dist/edge/http/draft-files.js +1 -3
  74. package/dist/edge/http/draft-problem.js +4 -9
  75. package/dist/edge/http/drafts.js +3 -3
  76. package/dist/edge/http/entries.js +6 -9
  77. package/dist/edge/http/extension-auth.js +27 -0
  78. package/dist/edge/http/external-reviews.js +12 -11
  79. package/dist/edge/http/files.js +5 -11
  80. package/dist/edge/http/folder-location.js +1 -4
  81. package/dist/edge/http/fonts.js +0 -7
  82. package/dist/edge/http/home.js +38 -13
  83. package/dist/edge/http/host-cli.js +18 -8
  84. package/dist/edge/http/ids.js +8 -0
  85. package/dist/edge/http/keywords.js +35 -0
  86. package/dist/edge/http/model-checks.js +132 -0
  87. package/dist/edge/http/narration-peaks.js +5 -13
  88. package/dist/edge/http/onboarding.js +1 -14
  89. package/dist/edge/http/open-folder.js +5 -10
  90. package/dist/edge/http/outputs.js +4 -12
  91. package/dist/edge/http/patch-notes.js +4 -10
  92. package/dist/edge/http/planning.js +2 -6
  93. package/dist/edge/http/problem.js +2 -4
  94. package/dist/edge/http/project-create.js +1 -0
  95. package/dist/edge/http/project-templates.js +29 -17
  96. package/dist/edge/http/projects.js +2 -12
  97. package/dist/edge/http/prompts.js +7 -14
  98. package/dist/edge/http/pronunciations.js +1 -2
  99. package/dist/edge/http/providers.js +39 -24
  100. package/dist/edge/http/refusal.js +29 -0
  101. package/dist/edge/http/remote-access.js +40 -0
  102. package/dist/edge/http/remote-guard.js +71 -0
  103. package/dist/edge/http/request-guard.js +91 -0
  104. package/dist/edge/http/reviews.js +11 -24
  105. package/dist/edge/http/revision-files.js +5 -10
  106. package/dist/edge/http/revisions.js +10 -15
  107. package/dist/edge/http/run-cost.js +1 -2
  108. package/dist/edge/http/same-page.js +80 -0
  109. package/dist/edge/http/schedules.js +59 -32
  110. package/dist/edge/http/settings.js +22 -13
  111. package/dist/edge/http/social.js +282 -0
  112. package/dist/edge/http/staging.js +2 -4
  113. package/dist/edge/http/storage-files.js +5 -7
  114. package/dist/edge/http/storage.js +23 -23
  115. package/dist/edge/http/studio.js +23 -50
  116. package/dist/edge/http/style-preview.js +1 -4
  117. package/dist/edge/http/subtitles.js +3 -17
  118. package/dist/edge/http/template-studio.js +433 -0
  119. package/dist/edge/http/trash.js +10 -11
  120. package/dist/edge/http/tutorial.js +1 -2
  121. package/dist/edge/http/tutorials.js +5 -11
  122. package/dist/edge/http/update.js +7 -13
  123. package/dist/edge/http/waveform.js +3 -6
  124. package/dist/edge/http/youtube-edits.js +2 -3
  125. package/dist/edge/mcp/assistant.js +279 -0
  126. package/dist/edge/mcp/attribution.js +8 -2
  127. package/dist/edge/mcp/call-route.js +11 -2
  128. package/dist/edge/mcp/excluded-routes.js +25 -0
  129. package/dist/edge/mcp/http.js +11 -2
  130. package/dist/edge/mcp/instructions.js +5 -3
  131. package/dist/edge/mcp/library-tools.js +1 -0
  132. package/dist/edge/mcp/route-tool.js +35 -15
  133. package/dist/edge/mcp/server.js +13 -8
  134. package/dist/edge/mcp/tools-channels.js +2 -0
  135. package/dist/edge/mcp/tools-designs.js +165 -0
  136. package/dist/edge/mcp/tools-library.js +14 -2
  137. package/dist/edge/mcp/tools-planning.js +23 -4
  138. package/dist/edge/mcp/tools-projects.js +28 -1
  139. package/dist/edge/mcp/tools-runs.js +14 -1
  140. package/dist/edge/mcp/tools-schedules.js +28 -4
  141. package/dist/edge/mcp/tools-setup.js +61 -3
  142. package/dist/edge/mcp/tools-social.js +46 -0
  143. package/dist/edge/mcp/tools-storage.js +8 -0
  144. package/dist/edge/mcp/tools-template-studio.js +166 -0
  145. package/dist/edge/mcp/tools.js +6 -0
  146. package/dist/edge/remote-access/cli.js +71 -0
  147. package/dist/edge/remote-access/model.js +32 -0
  148. package/dist/edge/remote-access/rules.js +207 -0
  149. package/dist/edge/remote-access/service.js +203 -0
  150. package/dist/edge/remote-access/store.js +44 -0
  151. package/dist/edge/remote-access/views.js +43 -0
  152. package/dist/extension/slopify-studio-chrome.zip +0 -0
  153. package/dist/extension/slopify-studio-firefox.zip +0 -0
  154. package/dist/host-cli/install.js +2 -2
  155. package/dist/host-cli/open-folder.js +2 -2
  156. package/dist/host-cli/paths.js +2 -4
  157. package/dist/host-cli/server.js +4 -4
  158. package/dist/host-cli/service.js +3 -3
  159. package/dist/host-cli/status.js +7 -7
  160. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  161. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  162. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  163. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  164. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  165. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  166. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  167. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  168. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  169. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  170. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  171. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  172. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  173. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  174. package/dist/kernel/db/tx.js +2 -4
  175. package/dist/kernel/errors.js +27 -1
  176. package/dist/kernel/json-answer.js +15 -0
  177. package/dist/kernel/lock.js +4 -9
  178. package/dist/kernel/paths.js +8 -0
  179. package/dist/kernel/ports/assistant-cli.js +4 -0
  180. package/dist/kernel/ports/host-cli.js +3 -0
  181. package/dist/kernel/ports/interest.js +4 -0
  182. package/dist/kernel/ports/languages.js +0 -3
  183. package/dist/kernel/ports/model.js +2 -0
  184. package/dist/kernel/runner/attempt.js +3 -1
  185. package/dist/kernel/runner/audition.js +3 -1
  186. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  187. package/dist/kernel/runner/index.js +8 -10
  188. package/dist/kernel/runner/model-access.js +40 -0
  189. package/dist/kernel/runner/model-check.js +153 -0
  190. package/dist/kernel/runner/providers.js +15 -10
  191. package/dist/kernel/runner/standalone.js +9 -5
  192. package/dist/main.js +334 -19
  193. package/dist/model-checks.js +166 -0
  194. package/dist/patch-notes/4.0.0.md +51 -0
  195. package/dist/patch-notes/index.json +6 -0
  196. package/dist/slices/admission/heads.js +2 -1
  197. package/dist/slices/admission/repo.js +2 -1
  198. package/dist/slices/admission/rules.js +43 -8
  199. package/dist/slices/admission/schema.js +8 -0
  200. package/dist/slices/admission/stage-writes.js +0 -3
  201. package/dist/slices/admission/start.js +48 -31
  202. package/dist/slices/admission/substitute.js +7 -4
  203. package/dist/slices/agents/attention-posts.js +31 -0
  204. package/dist/slices/agents/attention-templates.js +33 -0
  205. package/dist/slices/agents/attention.js +10 -1
  206. package/dist/slices/agents/connection.js +2 -6
  207. package/dist/slices/agents/model.js +6 -3
  208. package/dist/slices/agents/repo.js +15 -7
  209. package/dist/slices/article/continuation.js +0 -63
  210. package/dist/slices/article/plain.js +3 -2
  211. package/dist/slices/article/segments.js +2 -62
  212. package/dist/slices/assistant/access.js +1 -0
  213. package/dist/slices/assistant/asks.js +80 -0
  214. package/dist/slices/assistant/cost.js +54 -0
  215. package/dist/slices/assistant/gate.js +222 -0
  216. package/dist/slices/assistant/model.js +79 -0
  217. package/dist/slices/assistant/names.js +23 -0
  218. package/dist/slices/assistant/prompt.js +74 -0
  219. package/dist/slices/assistant/repo.js +74 -0
  220. package/dist/slices/assistant/tokens.js +18 -0
  221. package/dist/slices/assistant/turn.js +342 -0
  222. package/dist/slices/autopilot/buffer.js +76 -0
  223. package/dist/slices/autopilot/model.js +14 -0
  224. package/dist/slices/autopilot/notify.js +27 -0
  225. package/dist/slices/autopilot/repo.js +34 -0
  226. package/dist/slices/autopilot/signals.js +194 -0
  227. package/dist/slices/autopilot/warnings.js +85 -0
  228. package/dist/slices/autopilot/weekly.js +100 -0
  229. package/dist/slices/backups/files.js +2 -6
  230. package/dist/slices/backups/repo.js +3 -15
  231. package/dist/slices/backups/service.js +4 -3
  232. package/dist/slices/channels/cast-images.js +2 -1
  233. package/dist/slices/channels/model.js +0 -1
  234. package/dist/slices/channels/rebrand.js +0 -3
  235. package/dist/slices/channels/repo.js +4 -0
  236. package/dist/slices/channels/schema.js +6 -0
  237. package/dist/slices/channels/service.js +10 -0
  238. package/dist/slices/channels/videos.js +2 -14
  239. package/dist/slices/checkpoints/index.js +0 -1
  240. package/dist/slices/control/providers.js +0 -26
  241. package/dist/slices/designs/artwork.js +66 -0
  242. package/dist/slices/designs/budget.js +23 -0
  243. package/dist/slices/designs/calls.js +124 -0
  244. package/dist/slices/designs/check-context.js +38 -0
  245. package/dist/slices/designs/check-text.js +43 -0
  246. package/dist/slices/designs/fields.js +88 -0
  247. package/dist/slices/designs/frames.js +34 -0
  248. package/dist/slices/designs/gate.js +128 -0
  249. package/dist/slices/designs/layout.js +293 -0
  250. package/dist/slices/designs/model.js +35 -0
  251. package/dist/slices/designs/pdf-job.js +23 -0
  252. package/dist/slices/designs/pdf-pages.js +30 -0
  253. package/dist/slices/designs/pdf-runner.js +68 -0
  254. package/dist/slices/designs/pdf-worker.js +24 -0
  255. package/dist/slices/designs/picture-size.js +79 -0
  256. package/dist/slices/designs/pictures.js +55 -0
  257. package/dist/slices/designs/quote.js +24 -0
  258. package/dist/slices/designs/render-args.js +36 -0
  259. package/dist/slices/designs/render-input.js +59 -0
  260. package/dist/slices/designs/render.js +215 -0
  261. package/dist/slices/designs/repo.js +101 -0
  262. package/dist/slices/designs/requests.js +80 -0
  263. package/dist/slices/designs/runner.js +239 -0
  264. package/dist/slices/designs/sample-repo.js +91 -0
  265. package/dist/slices/designs/sample.js +222 -0
  266. package/dist/slices/designs/schema.js +87 -0
  267. package/dist/slices/designs/sections.js +44 -0
  268. package/dist/slices/designs/service.js +233 -0
  269. package/dist/slices/designs/steps-repo.js +102 -0
  270. package/dist/slices/designs/studio-kind.js +39 -0
  271. package/dist/slices/designs/template-checks.js +44 -0
  272. package/dist/slices/designs/template-make.js +118 -0
  273. package/dist/slices/designs/templates.js +218 -0
  274. package/dist/slices/document/fonts.js +2 -1
  275. package/dist/slices/episodes/summarize.js +5 -1
  276. package/dist/slices/estimate/index.js +45 -7
  277. package/dist/slices/fonts/catalog.js +3 -2
  278. package/dist/slices/fonts/discovery.js +3 -2
  279. package/dist/slices/fonts/files.js +2 -6
  280. package/dist/slices/fonts/index.js +0 -1
  281. package/dist/slices/fonts/sfnt.js +45 -10
  282. package/dist/slices/images/appearance.js +6 -15
  283. package/dist/slices/images/scenes.js +100 -27
  284. package/dist/slices/keywords/digest.js +84 -0
  285. package/dist/slices/keywords/facts.js +92 -0
  286. package/dist/slices/keywords/fill.js +80 -0
  287. package/dist/slices/keywords/model.js +75 -0
  288. package/dist/slices/keywords/rules.js +187 -0
  289. package/dist/slices/keywords/schema.js +28 -0
  290. package/dist/slices/keywords/specs.js +260 -0
  291. package/dist/slices/keywords/values.js +74 -0
  292. package/dist/slices/library/lint.js +29 -12
  293. package/dist/slices/library/model.js +2 -0
  294. package/dist/slices/library/photorealistic.js +4 -12
  295. package/dist/slices/library/rename.js +9 -7
  296. package/dist/slices/library/save.js +14 -10
  297. package/dist/slices/library/slots.js +37 -11
  298. package/dist/slices/library/transfer.js +2 -1
  299. package/dist/slices/library/used-by.js +25 -10
  300. package/dist/slices/loudness/loudnorm.js +51 -11
  301. package/dist/slices/model-checks/cost.js +61 -0
  302. package/dist/slices/model-checks/listing.js +124 -0
  303. package/dist/slices/model-checks/model.js +20 -0
  304. package/dist/slices/model-checks/record.js +32 -0
  305. package/dist/slices/model-checks/repo.js +120 -0
  306. package/dist/slices/model-checks/rules.js +112 -0
  307. package/dist/slices/model-checks/run.js +143 -0
  308. package/dist/slices/model-checks/service.js +198 -0
  309. package/dist/slices/model-upkeep/switch.js +70 -25
  310. package/dist/slices/model-upkeep/usage.js +26 -11
  311. package/dist/slices/narration/breaths.js +2 -1
  312. package/dist/slices/narration/clean-pieces.js +2 -1
  313. package/dist/slices/narration/concat.js +2 -1
  314. package/dist/slices/narration/peaks.js +3 -4
  315. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  316. package/dist/slices/narration/pronunciation.js +3 -2
  317. package/dist/slices/narration/steering.js +4 -3
  318. package/dist/slices/notifications/notifier.js +4 -3
  319. package/dist/slices/notifications/rules.js +5 -2
  320. package/dist/slices/notifications/settings.js +4 -12
  321. package/dist/slices/notifications/subject.js +2 -1
  322. package/dist/slices/onboarding/copy.js +2 -1
  323. package/dist/slices/onboarding/first-run.js +3 -2
  324. package/dist/slices/onboarding/full-video.js +3 -2
  325. package/dist/slices/onboarding/install.js +4 -3
  326. package/dist/slices/onboarding/model.js +0 -16
  327. package/dist/slices/onboarding/packs.js +22 -22
  328. package/dist/slices/onboarding/quick-short.js +2 -1
  329. package/dist/slices/onboarding/sample.js +0 -1
  330. package/dist/slices/onboarding/state.js +2 -11
  331. package/dist/slices/onboarding/template.js +2 -1
  332. package/dist/slices/patch-notes/library.js +3 -2
  333. package/dist/slices/play-drafts/convert.js +4 -0
  334. package/dist/slices/play-drafts/review-inputs.js +8 -2
  335. package/dist/slices/play-drafts/review.js +5 -4
  336. package/dist/slices/play-drafts/schema.js +4 -0
  337. package/dist/slices/project-templates/from-project.js +9 -2
  338. package/dist/slices/project-templates/repo.js +6 -1
  339. package/dist/slices/project-templates/setup.js +8 -0
  340. package/dist/slices/project-templates/stand-ins.js +22 -0
  341. package/dist/slices/rebuild/activity.js +2 -0
  342. package/dist/slices/rebuild/admission-repo.js +3 -2
  343. package/dist/slices/rebuild/narration-history.js +4 -5
  344. package/dist/slices/rebuild/narration-retry.js +2 -1
  345. package/dist/slices/rebuild/preview-details.js +27 -9
  346. package/dist/slices/rebuild/preview-plan.js +3 -8
  347. package/dist/slices/rebuild/recipe-build.js +8 -3
  348. package/dist/slices/rebuild/recipe-describe.js +3 -3
  349. package/dist/slices/rebuild/recipe-digest.js +58 -0
  350. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  351. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  352. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  353. package/dist/slices/rebuild/recipe-model.js +4 -0
  354. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  355. package/dist/slices/rebuild/recipe-preparation.js +3 -3
  356. package/dist/slices/rebuild/recipe-reference.js +8 -4
  357. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  358. package/dist/slices/rebuild/recipe-save.js +6 -3
  359. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  360. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  361. package/dist/slices/rebuild/recipe-social.js +46 -0
  362. package/dist/slices/rebuild/recipe-text.js +64 -20
  363. package/dist/slices/rebuild/recipe-validation.js +98 -28
  364. package/dist/slices/rebuild/recipe-visual.js +55 -16
  365. package/dist/slices/rebuild/recipe-work.js +15 -3
  366. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  367. package/dist/slices/rebuild/recovery-selection.js +3 -1
  368. package/dist/slices/rebuild/recovery.js +3 -2
  369. package/dist/slices/rebuild/repo.js +4 -3
  370. package/dist/slices/rebuild/review-promise.js +25 -0
  371. package/dist/slices/rebuild/review-redo.js +2 -1
  372. package/dist/slices/rebuild/runtime-admission.js +4 -3
  373. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  374. package/dist/slices/rebuild/runtime-animate.js +9 -8
  375. package/dist/slices/rebuild/runtime-article.js +3 -2
  376. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  377. package/dist/slices/rebuild/runtime-document.js +8 -11
  378. package/dist/slices/rebuild/runtime-export-bed.js +6 -11
  379. package/dist/slices/rebuild/runtime-export-edit.js +6 -15
  380. package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
  381. package/dist/slices/rebuild/runtime-export-short.js +8 -4
  382. package/dist/slices/rebuild/runtime-export.js +34 -12
  383. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  384. package/dist/slices/rebuild/runtime-image.js +4 -9
  385. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  386. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  387. package/dist/slices/rebuild/runtime-lines.js +2 -11
  388. package/dist/slices/rebuild/runtime-local.js +14 -20
  389. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  390. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  391. package/dist/slices/rebuild/runtime-narration-text.js +10 -15
  392. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  393. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  394. package/dist/slices/rebuild/runtime-plan.js +31 -5
  395. package/dist/slices/rebuild/runtime-provider.js +60 -19
  396. package/dist/slices/rebuild/runtime-publication.js +10 -9
  397. package/dist/slices/rebuild/runtime-review.js +33 -28
  398. package/dist/slices/rebuild/runtime-run.js +4 -1
  399. package/dist/slices/rebuild/runtime-shorts.js +25 -45
  400. package/dist/slices/rebuild/runtime-store.js +3 -8
  401. package/dist/slices/rebuild/runtime-subtitles.js +16 -14
  402. package/dist/slices/rebuild/runtime-voices.js +8 -15
  403. package/dist/slices/rebuild/runtime-youtube.js +11 -29
  404. package/dist/slices/rebuild/transition-repo.js +6 -5
  405. package/dist/slices/rebuild/work-records.js +6 -0
  406. package/dist/slices/reruns/cascade.js +1 -2
  407. package/dist/slices/reruns/index.js +6 -9
  408. package/dist/slices/reviews/external-model.js +3 -0
  409. package/dist/slices/reviews/external-repo.js +2 -1
  410. package/dist/slices/reviews/external-rows.js +2 -3
  411. package/dist/slices/reviews/model.js +26 -8
  412. package/dist/slices/reviews/repo.js +2 -1
  413. package/dist/slices/reviews/rules.js +1 -0
  414. package/dist/slices/reviews/view.js +5 -2
  415. package/dist/slices/revisions/downloads.js +4 -5
  416. package/dist/slices/revisions/mutation-assets.js +7 -4
  417. package/dist/slices/revisions/mutation-cues.js +2 -5
  418. package/dist/slices/revisions/mutations.js +0 -1
  419. package/dist/slices/revisions/repo.js +3 -1
  420. package/dist/slices/revisions/schema.js +1 -0
  421. package/dist/slices/revisions/view.js +20 -0
  422. package/dist/slices/run-cost/limits.js +5 -2
  423. package/dist/slices/run-cost/meter.js +12 -5
  424. package/dist/slices/run-cost/panel.js +8 -74
  425. package/dist/slices/run-cost/plan-readings.js +127 -0
  426. package/dist/slices/run-cost/plan-use.js +169 -0
  427. package/dist/slices/run-cost/pricing.js +3 -3
  428. package/dist/slices/run-cost/week.js +32 -4
  429. package/dist/slices/schedules/agenda.js +11 -2
  430. package/dist/slices/schedules/by-date.js +42 -0
  431. package/dist/slices/schedules/calendar.js +5 -0
  432. package/dist/slices/schedules/design-agenda.js +36 -0
  433. package/dist/slices/schedules/design-check.js +41 -0
  434. package/dist/slices/schedules/design-run.js +126 -0
  435. package/dist/slices/schedules/next-run.js +62 -0
  436. package/dist/slices/schedules/prepare.js +18 -2
  437. package/dist/slices/schedules/repo.js +24 -16
  438. package/dist/slices/schedules/rescore.js +35 -0
  439. package/dist/slices/schedules/scheduler.js +13 -3
  440. package/dist/slices/schedules/schema.js +54 -3
  441. package/dist/slices/schedules/score.js +107 -0
  442. package/dist/slices/schedules/service.js +57 -24
  443. package/dist/slices/schedules/topic-list.js +15 -0
  444. package/dist/slices/schedules/topic-scores.js +104 -0
  445. package/dist/slices/schedules/topics.js +48 -36
  446. package/dist/slices/settings/cli-paths.js +4 -6
  447. package/dist/slices/settings/first-run.js +2 -11
  448. package/dist/slices/settings/model-reach.js +3 -17
  449. package/dist/slices/settings/model.js +10 -0
  450. package/dist/slices/settings/playback.js +33 -22
  451. package/dist/slices/settings/probe-read.js +16 -0
  452. package/dist/slices/settings/repo.js +33 -0
  453. package/dist/slices/settings/tutorial.js +2 -2
  454. package/dist/slices/settings/voices.js +4 -12
  455. package/dist/slices/shorts/pick.js +2 -15
  456. package/dist/slices/shorts/prompts.js +2 -13
  457. package/dist/slices/shorts/render.js +3 -0
  458. package/dist/slices/social/channel.js +44 -0
  459. package/dist/slices/social/defaults.js +17 -0
  460. package/dist/slices/social/due.js +36 -0
  461. package/dist/slices/social/extension.js +18 -0
  462. package/dist/slices/social/fit.js +115 -0
  463. package/dist/slices/social/limits.js +186 -0
  464. package/dist/slices/social/links.js +21 -0
  465. package/dist/slices/social/media.js +131 -0
  466. package/dist/slices/social/model.js +75 -0
  467. package/dist/slices/social/notice.js +19 -0
  468. package/dist/slices/social/pack.js +176 -0
  469. package/dist/slices/social/post-prompt.js +55 -0
  470. package/dist/slices/social/queue.js +31 -0
  471. package/dist/slices/social/repo.js +133 -0
  472. package/dist/slices/social/reshape.js +97 -0
  473. package/dist/slices/social/rules.js +5 -0
  474. package/dist/slices/social/schema.js +70 -0
  475. package/dist/slices/social/serve.js +51 -0
  476. package/dist/slices/social/service.js +128 -0
  477. package/dist/slices/social/settings.js +21 -0
  478. package/dist/slices/social/sources.js +114 -0
  479. package/dist/slices/social/state.js +110 -0
  480. package/dist/slices/social/text.js +149 -0
  481. package/dist/slices/storage/arrange.js +3 -1
  482. package/dist/slices/storage/assets.js +8 -10
  483. package/dist/slices/storage/backup-designs.js +74 -0
  484. package/dist/slices/storage/backup-export.js +8 -3
  485. package/dist/slices/storage/backup-format.js +10 -0
  486. package/dist/slices/storage/backup-import.js +20 -2
  487. package/dist/slices/storage/delete-project.js +6 -5
  488. package/dist/slices/storage/files-location.js +6 -8
  489. package/dist/slices/storage/layout.js +13 -2
  490. package/dist/slices/storage/model.js +3 -0
  491. package/dist/slices/storage/portable.js +16 -11
  492. package/dist/slices/storage/schema.js +2 -0
  493. package/dist/slices/storage/staging-refs.js +2 -1
  494. package/dist/slices/storage/staging.js +6 -9
  495. package/dist/slices/storage/trim.js +10 -3
  496. package/dist/slices/studio/calendar.js +4 -1
  497. package/dist/slices/studio/pick.js +2 -11
  498. package/dist/slices/studio/plan.js +6 -15
  499. package/dist/slices/studio/queue.js +3 -14
  500. package/dist/slices/studio/releases.js +26 -0
  501. package/dist/slices/studio/settings.js +14 -51
  502. package/dist/slices/studio/videos.js +12 -0
  503. package/dist/slices/style-preview/render.js +34 -13
  504. package/dist/slices/style-preview/schema.js +13 -0
  505. package/dist/slices/style-preview/service.js +2 -1
  506. package/dist/slices/style-preview/settings.js +33 -5
  507. package/dist/slices/telemetry/collector-client.js +2 -4
  508. package/dist/slices/telemetry/flush.js +2 -4
  509. package/dist/slices/telemetry/model.js +0 -7
  510. package/dist/slices/telemetry/record.js +10 -3
  511. package/dist/slices/template-studio/anatomy-view.js +118 -0
  512. package/dist/slices/template-studio/anatomy.js +73 -0
  513. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  514. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  515. package/dist/slices/template-studio/bakeoff.js +333 -0
  516. package/dist/slices/template-studio/budget.js +103 -0
  517. package/dist/slices/template-studio/checks.js +273 -0
  518. package/dist/slices/template-studio/document-sample.js +56 -0
  519. package/dist/slices/template-studio/edit.js +15 -0
  520. package/dist/slices/template-studio/for-all.js +161 -0
  521. package/dist/slices/template-studio/free-video.js +31 -0
  522. package/dist/slices/template-studio/free.js +176 -0
  523. package/dist/slices/template-studio/gate.js +40 -0
  524. package/dist/slices/template-studio/hidden.js +20 -0
  525. package/dist/slices/template-studio/jobs.js +79 -0
  526. package/dist/slices/template-studio/loudness.js +111 -0
  527. package/dist/slices/template-studio/make.js +40 -0
  528. package/dist/slices/template-studio/model.js +54 -0
  529. package/dist/slices/template-studio/pack-import.js +253 -0
  530. package/dist/slices/template-studio/pack-samples.js +60 -0
  531. package/dist/slices/template-studio/pack.js +205 -0
  532. package/dist/slices/template-studio/paid-view.js +66 -0
  533. package/dist/slices/template-studio/paid.js +348 -0
  534. package/dist/slices/template-studio/quote.js +148 -0
  535. package/dist/slices/template-studio/ready.js +104 -0
  536. package/dist/slices/template-studio/repo.js +134 -0
  537. package/dist/slices/template-studio/reviews-path.js +58 -0
  538. package/dist/slices/template-studio/runner.js +84 -0
  539. package/dist/slices/template-studio/save.js +236 -0
  540. package/dist/slices/template-studio/say-it-as.js +248 -0
  541. package/dist/slices/template-studio/schedule-gate.js +34 -0
  542. package/dist/slices/template-studio/schedule-price.js +30 -0
  543. package/dist/slices/template-studio/schedule-sample.js +87 -0
  544. package/dist/slices/template-studio/schema.js +218 -0
  545. package/dist/slices/template-studio/scope.js +124 -0
  546. package/dist/slices/template-studio/sections.js +176 -0
  547. package/dist/slices/template-studio/service.js +173 -0
  548. package/dist/slices/template-studio/status.js +174 -0
  549. package/dist/slices/template-studio/topic.js +223 -0
  550. package/dist/slices/trash/designs.js +76 -0
  551. package/dist/slices/trash/model.js +13 -4
  552. package/dist/slices/trash/service.js +52 -2
  553. package/dist/slices/tutorials/library.js +3 -2
  554. package/dist/slices/video/ass-text.js +17 -0
  555. package/dist/slices/video/audio-export.js +2 -64
  556. package/dist/slices/video/encoding.js +160 -0
  557. package/dist/slices/video/ffmpeg.js +23 -21
  558. package/dist/slices/video/figure-card.js +1 -15
  559. package/dist/slices/video/slideshow.js +43 -18
  560. package/dist/slices/voices/palette.js +0 -3
  561. package/dist/slices/youtube/answer.js +2 -15
  562. package/dist/slices/youtube/edits-repo.js +2 -11
  563. package/dist/tutorials/Agents-MCP.md +2 -1
  564. package/dist/tutorials/Assistant.md +42 -0
  565. package/dist/tutorials/Channels.md +2 -0
  566. package/dist/tutorials/Home-and-Projects.md +19 -0
  567. package/dist/tutorials/Install.md +1 -0
  568. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  569. package/dist/tutorials/Models.md +33 -1
  570. package/dist/tutorials/Notifications.md +6 -0
  571. package/dist/tutorials/Play-Images.md +4 -0
  572. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  573. package/dist/tutorials/Prompts.md +10 -6
  574. package/dist/tutorials/Remote-Access.md +63 -0
  575. package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
  576. package/dist/tutorials/Schedules.md +21 -1
  577. package/dist/tutorials/Settings-Reference.md +50 -8
  578. package/dist/tutorials/Template-Studio.md +125 -0
  579. package/dist/tutorials/Templates.md +3 -0
  580. package/dist/tutorials/_Sidebar.md +5 -0
  581. package/dist/updater/work-in-progress.js +42 -12
  582. package/dist/web/assets/index-CkAI_2E7.css +1 -0
  583. package/dist/web/assets/index-DcD0cdfo.js +334 -0
  584. package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
  585. package/dist/web/index.html +2 -2
  586. package/package.json +5 -1
  587. package/dist/slices/article/provided-entries.js +0 -66
  588. package/dist/slices/article/run.js +0 -145
  589. package/dist/slices/images/run.js +0 -234
  590. package/dist/slices/narration/legacy-plan.js +0 -58
  591. package/dist/slices/narration/run.js +0 -270
  592. package/dist/slices/rebuild/runtime-actions.js +0 -90
  593. package/dist/slices/research/run.js +0 -224
  594. package/dist/slices/subtitles/prepare.js +0 -194
  595. package/dist/slices/subtitles/transcript.js +0 -34
  596. package/dist/slices/thumbnail/run.js +0 -219
  597. package/dist/slices/video/audio-inputs.js +0 -33
  598. package/dist/slices/video/reuse-audio.js +0 -62
  599. package/dist/slices/video/run.js +0 -78
  600. package/dist/slices/video/write-export.js +0 -158
  601. package/dist/slices/video/write-subtitles.js +0 -59
  602. package/dist/web/assets/index-BadsoORk.css +0 -1
  603. package/dist/web/assets/index-D8LqNsCl.js +0 -326
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { redact } from "../../kernel/log.js";
3
3
  import { providerError } from "../../kernel/ports/model.js";
4
- import { httpFailure, missingKey, noImage, quoted, refusedImage, unreadable } from "../explain.js";
5
- import { retryAfter } from "../retry-after.js";
4
+ import { httpFailure, noImage, quoted, refusedImage, unreadable } from "../explain.js";
5
+ import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
6
+ import { modelAccessOf } from "../model-access.js";
6
7
  import { describeBytes, sniffImage } from "./bytes.js";
7
8
  import { discoverGoogleImages } from "./models.js";
8
- import { referencePictures, withReferences } from "./reference.js";
9
+ import { attachedPictures, promptFor } from "./reference.js";
9
10
  // The HTTP gateway adapter for Google's own image generation, billed to a Gemini API key
10
11
  // rather than to a host reselling the same models. Like the OpenAI one it hands back the
11
12
  // bytes with the call, so there is no link to follow.
@@ -65,15 +66,15 @@ export function googleImage(deps) {
65
66
  model: req.model,
66
67
  // With an establishing image the input is two parts, the image first; the Gemini
67
68
  // image models draw with an input image as their reference.
68
- input: referencePictures(req).length === 0
69
+ input: attachedPictures(req).length === 0
69
70
  ? req.prompt
70
71
  : [
71
- ...referencePictures(req).map(({ image }) => ({
72
+ ...attachedPictures(req).map(({ image }) => ({
72
73
  type: "image",
73
74
  mime_type: image.mime,
74
75
  data: Buffer.from(image.bytes).toString("base64"),
75
76
  })),
76
- { type: "text", text: withReferences(req.prompt, req) },
77
+ { type: "text", text: promptFor(req.prompt, req) },
77
78
  ],
78
79
  // The stage sends Number as that many independent calls, one piece each, so one
79
80
  // image per request is what it asks for. Nothing about style is set: the stage
@@ -123,12 +124,7 @@ function decode(part) {
123
124
  return { bytes, mime };
124
125
  }
125
126
  function keyOf(deps) {
126
- // `missing_key`, not `auth`, because that rule makes it terminal.
127
- const key = deps.key();
128
- if (key === undefined || key === "") {
129
- throw providerError({ kind: "missing_key", message: missingKey("Google") });
130
- }
131
- return key;
127
+ return requireKey(deps.key(), "Google");
132
128
  }
133
129
  // Only the adapter sees the vendor's status code, so only the adapter names the kind.
134
130
  function kindOf(status, named, message) {
@@ -141,27 +137,16 @@ function kindOf(status, named, message) {
141
137
  if (status === 400 && refusalWords.test(message)) {
142
138
  return "refusal";
143
139
  }
144
- if (status === 401 || status === 403) {
145
- return "auth";
146
- }
147
- if (status === 429) {
148
- // Interactions wraps quota failures in a string-coded error and exposes the limit
149
- // in the message. Zero allocated capacity cannot be fixed by waiting and retrying.
150
- if (/quota exceeded for metric:[^\r\n]*?\blimit:\s*0\s*(?:,|$)/im.test(message)) {
151
- return "unsupported";
152
- }
153
- return "rate_limit";
140
+ // Interactions wraps quota failures in a string-coded error and exposes the limit
141
+ // in the message. Zero allocated capacity cannot be fixed by waiting and retrying.
142
+ if (status === 429 &&
143
+ /quota exceeded for metric:[^\r\n]*?\blimit:\s*0\s*(?:,|$)/im.test(message)) {
144
+ return "unsupported";
154
145
  }
155
- // ceiling: everything else is `other` and is retried, so a prompt past the model's limit
156
- // fails the same way four times over. A terminal "this will never work" kind has to reach
157
- // the port's error contract first, which is not this adapter's to widen.
158
- // The provider's own server failed; the same request may well succeed later.
159
- if (status >= 500)
160
- return "dropped";
161
- return "other";
146
+ return kindOfStatus(status);
162
147
  }
163
148
  async function failure(response) {
164
- const text = await response.text().catch(() => "");
149
+ const text = await failureText(response);
165
150
  const parsed = errorBody.safeParse(safeJson(text));
166
151
  // The provider's own words, through the redactor - an error body may quote the key back.
167
152
  const named = parsed.success ? parsed.data.error.status : undefined;
@@ -170,24 +155,23 @@ async function failure(response) {
170
155
  const delay = parsed.success
171
156
  ? parsed.data.error.details?.find((one) => one["@type"] === "type.googleapis.com/google.rpc.RetryInfo")?.retryDelay
172
157
  : undefined;
173
- const retryAfterMs = retryAfter(response.headers.get("retry-after")) ?? googleRetryDelay(delay, message);
174
158
  const detail = message || response.statusText;
175
- return providerError({
176
- kind,
177
- message: kind === "unsupported"
178
- ? `Google gives this API key no image quota for this model${quoted(`error ${String(response.status)}`, detail)}. Retrying will not help: turn on billing for the key's project in Google AI Studio, or choose another image provider in the Providers section of Edit project.`
179
- : // PERMISSION_DENIED and FAILED_PRECONDITION also cover keys without access, billing
180
- // and region limits, so they read as a content refusal only when Google says so.
181
- kind === "refusal" && refusalWords.test(message)
182
- ? refusedImage("Google", detail)
183
- : httpFailure({
184
- provider: "Google",
185
- status: response.status,
186
- detail,
187
- subject: "image request",
188
- }),
189
- ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
190
- });
159
+ const refused = kind === "refusal" && refusalWords.test(message);
160
+ return httpProviderError(response, kind, kind === "unsupported"
161
+ ? `Google gives this API key no image quota for this model${quoted(`error ${String(response.status)}`, detail)}. Retrying will not help: turn on billing for the key's project in Google AI Studio, or choose another image provider in the Providers section of Edit project.`
162
+ : // PERMISSION_DENIED and FAILED_PRECONDITION also cover keys without access, billing
163
+ // and region limits, so they read as a content refusal only when Google says so.
164
+ refused
165
+ ? refusedImage("Google", detail)
166
+ : httpFailure({
167
+ provider: "Google",
168
+ status: response.status,
169
+ detail,
170
+ subject: "image request",
171
+ }), googleRetryDelay(delay, message),
172
+ // A key that reads the model list but is denied or told "not found" here is denied the
173
+ // model the request names: Google answers a bad key with 400 API_KEY_INVALID.
174
+ refused ? undefined : modelAccessOf(response.status, `${named ?? ""} ${message}`, true));
191
175
  }
192
176
  function googleRetryDelay(delay, message) {
193
177
  const seconds = delay?.match(/^(\d+(?:\.\d+)?)s$/)?.[1] ??
@@ -207,11 +191,3 @@ function parse(text) {
207
191
  }
208
192
  return parsed.data;
209
193
  }
210
- function safeJson(text) {
211
- try {
212
- return JSON.parse(text);
213
- }
214
- catch {
215
- return undefined;
216
- }
217
- }
@@ -1,11 +1,12 @@
1
1
  import { z } from "zod";
2
2
  import { redact } from "../../kernel/log.js";
3
3
  import { providerError } from "../../kernel/ports/model.js";
4
- import { httpFailure, missingKey, noImage, refusedImage, unreadable } from "../explain.js";
5
- import { retryAfter } from "../retry-after.js";
4
+ import { httpFailure, noImage, refusedImage, unreadable } from "../explain.js";
5
+ import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
6
+ import { modelAccessOf } from "../model-access.js";
6
7
  import { describeBytes, sniffImage } from "./bytes.js";
7
8
  import { discoverOpenAiImages } from "./models.js";
8
- import { referencePictures, withReferences } from "./reference.js";
9
+ import { attachedPictures, promptFor } from "./reference.js";
9
10
  // The HTTP gateway adapter for OpenAI's images endpoint: the platform's own `fetch` and
10
11
  // nothing else, because the whole call is one request. Unlike fal and Replicate this one
11
12
  // hands back the image itself - a GPT image model always answers with base64, never a URL -
@@ -56,7 +57,7 @@ export function openAiImage(deps) {
56
57
  id: "openai-image",
57
58
  models: () => discoverOpenAiImages(deps),
58
59
  generate: async (req) => {
59
- const pictures = referencePictures(req);
60
+ const pictures = attachedPictures(req);
60
61
  const response = pictures.length === 0
61
62
  ? await deps.fetch(`${openAiImagesBase}/images/generations`, {
62
63
  method: "POST",
@@ -96,11 +97,11 @@ export function openAiImage(deps) {
96
97
  };
97
98
  }
98
99
  // The edits endpoint takes several input images as repeated `image[]` parts: the establishing
99
- // image first, then the cast pictures.
100
+ // image (or, for a design's edit, the picture to change) first, then the cast pictures.
100
101
  function editForm(req, pictures) {
101
102
  const form = new FormData();
102
103
  form.set("model", req.model);
103
- form.set("prompt", withReferences(req.prompt, req));
104
+ form.set("prompt", promptFor(req.prompt, req));
104
105
  form.set("n", "1");
105
106
  form.set("size", sizeFor(req.model, req.aspect));
106
107
  for (const [index, { image }] of pictures.entries())
@@ -122,51 +123,31 @@ function decode(b64) {
122
123
  return { bytes, mime };
123
124
  }
124
125
  function keyOf(deps) {
125
- // `missing_key`, not `auth`, because that rule makes it terminal.
126
- const key = deps.key();
127
- if (key === undefined || key === "") {
128
- throw providerError({ kind: "missing_key", message: missingKey("OpenAI") });
129
- }
130
- return key;
126
+ return requireKey(deps.key(), "OpenAI");
131
127
  }
132
128
  // Only the adapter sees the vendor's status code, so only the adapter names the kind.
133
129
  function kindOf(status, code) {
134
130
  if (code !== undefined && code !== null && refusalCodes.includes(code)) {
135
131
  return "refusal";
136
132
  }
137
- if (status === 401 || status === 403) {
138
- return "auth";
139
- }
140
- if (status === 429) {
141
- return "rate_limit";
142
- }
143
- // ceiling: everything else is `other` and is retried, so a prompt past the model's length
144
- // limit fails the same way four times over. A terminal "this will never work" kind has to
145
- // reach the port's error contract first, which is not this adapter's to widen.
146
- // The provider's own server failed; the same request may well succeed later.
147
- if (status >= 500)
148
- return "dropped";
149
- return "other";
133
+ return kindOfStatus(status);
150
134
  }
151
135
  async function failure(response) {
152
- const text = await response.text().catch(() => "");
136
+ const text = await failureText(response);
153
137
  const parsed = errorBody.safeParse(safeJson(text));
154
138
  // The provider's own words, through the redactor - an error body may quote the key back.
155
139
  const message = redact(parsed.success ? parsed.data.error.message : text.trim() || response.statusText);
156
- const retryAfterMs = retryAfter(response.headers.get("retry-after"));
157
140
  const kind = kindOf(response.status, parsed.success ? parsed.data.error.code : undefined);
158
- return providerError({
159
- kind,
160
- message: kind === "refusal"
161
- ? refusedImage("OpenAI", message)
162
- : httpFailure({
163
- provider: "OpenAI",
164
- status: response.status,
165
- detail: message,
166
- subject: "image request",
167
- }),
168
- ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
169
- });
141
+ return httpProviderError(response, kind, kind === "refusal"
142
+ ? refusedImage("OpenAI", message)
143
+ : httpFailure({
144
+ provider: "OpenAI",
145
+ status: response.status,
146
+ detail: message,
147
+ subject: "image request",
148
+ }), undefined, kind === "refusal"
149
+ ? undefined
150
+ : modelAccessOf(response.status, `${parsed.success ? (parsed.data.error.code ?? "") : ""} ${message}`));
170
151
  }
171
152
  function parse(text) {
172
153
  const parsed = generated.safeParse(safeJson(text));
@@ -178,11 +159,3 @@ function parse(text) {
178
159
  }
179
160
  return parsed.data;
180
161
  }
181
- function safeJson(text) {
182
- try {
183
- return JSON.parse(text);
184
- }
185
- catch {
186
- return undefined;
187
- }
188
- }
@@ -39,3 +39,39 @@ export function withReferences(prompt, req, limit = Number.POSITIVE_INFINITY) {
39
39
  function describe(description) {
40
40
  return description.trim() === "" ? "" : ` (${description.trim()})`;
41
41
  }
42
+ // What an edit model is told about the first attached picture: it is the canvas to change,
43
+ // the opposite of a reference.
44
+ export const editNote = "Change the first attached image as follows. Keep everything the instruction does not mention exactly as it is: people, faces, composition and framing.";
45
+ // The pictures an edit request attaches: the picture to change, then each cast member's
46
+ // pictures. An edit never sends an establishing image.
47
+ export function editPictures(req) {
48
+ return [
49
+ ...(req.edit === undefined ? [] : [{ image: req.edit.source }]),
50
+ ...(req.cast ?? []).flatMap((member) => member.images.map((image) => ({ image, member: member.name }))),
51
+ ];
52
+ }
53
+ // The prompt an edit request sends with the first `limit` of `editPictures(req)` attached.
54
+ export function withEditInstruction(instruction, req, limit = Number.POSITIVE_INFINITY) {
55
+ const sent = editPictures(req).slice(0, limit);
56
+ const lines = [editNote];
57
+ for (const [index, picture] of sent.entries()) {
58
+ const member = req.cast?.find((row) => row.name === picture.member);
59
+ if (member !== undefined)
60
+ lines.push(`Image ${String(index + 1)} shows ${member.name}${describe(member.description)}: draw ${member.name} to look exactly like this.`);
61
+ }
62
+ const shown = new Set(sent.flatMap((picture) => picture.member ?? []));
63
+ for (const member of req.cast ?? [])
64
+ if (!shown.has(member.name))
65
+ lines.push(`${member.name} appears in this image${describe(member.description)}.`);
66
+ return `${lines.join("\n")}\n\n${instruction}`;
67
+ }
68
+ // What an adapter attaches and says: an edit's pictures and instruction, or, for every other
69
+ // request, exactly what it always attached and said.
70
+ export function attachedPictures(req) {
71
+ return req.edit === undefined ? referencePictures(req) : editPictures(req);
72
+ }
73
+ export function promptFor(prompt, req, limit = Number.POSITIVE_INFINITY) {
74
+ return req.edit === undefined
75
+ ? withReferences(prompt, req, limit)
76
+ : withEditInstruction(prompt, req, limit);
77
+ }
@@ -1,8 +1,9 @@
1
1
  import { z } from "zod";
2
2
  import { redact } from "../../kernel/log.js";
3
3
  import { providerError } from "../../kernel/ports/model.js";
4
- import { httpFailure, missingKey, noImage, providerSaid, refusedImage, unreadable, } from "../explain.js";
5
- import { retryAfter } from "../retry-after.js";
4
+ import { httpFailure, noImage, providerSaid, refusedImage, unreadable } from "../explain.js";
5
+ import { failureText, httpProviderError, kindOfStatus, requireKey, safeJson, } from "../http-failure.js";
6
+ import { modelAccessOf } from "../model-access.js";
6
7
  import { downloadImage } from "./bytes.js";
7
8
  import { withReferences } from "./reference.js";
8
9
  import { dataUri, downloadVideo } from "./video.js";
@@ -37,6 +38,7 @@ const errorBody = z.object({ detail: z.string().optional(), title: z.string().op
37
38
  // `canceled` cannot happen here - nothing cancels a prediction this app made - but reading
38
39
  // it as terminal beats polling forever.
39
40
  const settled = ["succeeded", "failed", "canceled"];
41
+ export const replicateEditRefusal = "Replicate's image models can't edit a picture. Choose Codex CLI, OpenAI, Google, or FLUX.2 or Nano Banana 2 on fal.ai under Made with … · change.";
40
42
  export function replicateImage(deps) {
41
43
  return {
42
44
  id: "replicate",
@@ -44,6 +46,8 @@ export function replicateImage(deps) {
44
46
  generate: async (req) => {
45
47
  // The Replicate models listed take no input image to draw from (the FLUX image inputs
46
48
  // copy the picture's composition), so an establishing image is refused, not dropped.
49
+ if (req.edit !== undefined)
50
+ throw providerError({ kind: "unsupported", message: replicateEditRefusal });
47
51
  if (req.reference !== undefined)
48
52
  throw providerError({
49
53
  kind: "unsupported",
@@ -70,7 +74,7 @@ export function replicateImage(deps) {
70
74
  }),
71
75
  });
72
76
  if (!response.ok) {
73
- throw await failure(response);
77
+ throw await failure(response, "image request", true);
74
78
  }
75
79
  const url = await settle(deps, parse(await response.text()), req.signal);
76
80
  // The download rides inside the attempt: a link that 404s is a failed attempt.
@@ -100,7 +104,7 @@ export function replicateImage(deps) {
100
104
  }),
101
105
  });
102
106
  if (!response.ok)
103
- throw await failure(response, "video clip request");
107
+ throw await failure(response, "video clip request", true);
104
108
  const url = await settle(deps, parse(await response.text()), req.signal);
105
109
  return await downloadVideo({
106
110
  fetch: deps.fetch,
@@ -172,46 +176,21 @@ function declined(current) {
172
176
  });
173
177
  }
174
178
  function auth(deps) {
175
- // `missing_key`, not `auth`, because that rule makes it terminal.
176
- const key = deps.key();
177
- if (key === undefined || key === "") {
178
- throw providerError({ kind: "missing_key", message: missingKey("Replicate") });
179
- }
180
- return { Authorization: `Bearer ${key}` };
179
+ return { Authorization: `Bearer ${requireKey(deps.key(), "Replicate")}` };
181
180
  }
182
- // Only the adapter sees the vendor's status code, so only the adapter names the kind.
183
- function kindOf(status) {
184
- if (status === 401 || status === 403) {
185
- return "auth";
186
- }
187
- if (status === 429) {
188
- return "rate_limit";
189
- }
190
- // ceiling: everything else is `other` and is retried, so a 402 with no credit left fails the
191
- // same way four times over. A terminal "this will never work" kind has to reach the port's
192
- // error contract first, which is not this adapter's to widen.
193
- // The provider's own server failed; the same request may well succeed later.
194
- if (status >= 500)
195
- return "dropped";
196
- return "other";
197
- }
198
- async function failure(response, subject = "image request") {
199
- const text = await response.text().catch(() => "");
181
+ // `atModel`: the request went to the model's own address, so a 404 or 403 is about the model.
182
+ async function failure(response, subject = "image request", atModel = false) {
183
+ const text = await failureText(response);
200
184
  const parsed = errorBody.safeParse(safeJson(text));
201
185
  // The provider's own words, through the redactor - an error body may quote the key back.
202
186
  const detail = parsed.success ? (parsed.data.detail ?? parsed.data.title) : undefined;
203
187
  const message = redact(detail ?? text.trim());
204
- const retryAfterMs = retryAfter(response.headers.get("retry-after"));
205
- return providerError({
206
- kind: kindOf(response.status),
207
- message: httpFailure({
208
- provider: "Replicate",
209
- status: response.status,
210
- detail: message || response.statusText,
211
- subject,
212
- }),
213
- ...(retryAfterMs === undefined ? {} : { retryAfterMs }),
214
- });
188
+ return httpProviderError(response, kindOfStatus(response.status), httpFailure({
189
+ provider: "Replicate",
190
+ status: response.status,
191
+ detail: message || response.statusText,
192
+ subject,
193
+ }), undefined, modelAccessOf(response.status, text, atModel));
215
194
  }
216
195
  function parse(text) {
217
196
  const parsed = prediction.safeParse(safeJson(text));
@@ -223,11 +202,3 @@ function parse(text) {
223
202
  }
224
203
  return parsed.data;
225
204
  }
226
- function safeJson(text) {
227
- try {
228
- return JSON.parse(text);
229
- }
230
- catch {
231
- return undefined;
232
- }
233
- }
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ // Public search interest without a key: Google's search suggestions (DuckDuckGo's when Google
3
+ // doesn't answer) and the Wikipedia article's views last month. Checked keyless on 2026-10-18;
4
+ // Google Trends answers 429/404 to such calls, so it isn't asked.
5
+ // ceiling: one service that hangs costs a topic this long at most.
6
+ export const interestRequestMs = 4000;
7
+ const suggestAnswer = z.tuple([z.string(), z.array(z.string())]).rest(z.unknown());
8
+ const openSearchAnswer = z.tuple([z.string(), z.array(z.string())]).rest(z.unknown());
9
+ const pageviewsAnswer = z.object({ items: z.array(z.object({ views: z.number() }).loose()) });
10
+ class Unanswered extends Error {
11
+ reason;
12
+ constructor(reason) {
13
+ super(reason);
14
+ this.reason = reason;
15
+ }
16
+ }
17
+ // "en" from "en-GB"; a language Wikipedia names by its two letters.
18
+ function languageOf(language) {
19
+ const code = language.trim().toLowerCase().slice(0, 2);
20
+ return /^[a-z]{2}$/.test(code) ? code : "en";
21
+ }
22
+ // The last whole month before `now`, as the pageviews API names its days.
23
+ export function lastMonth(now) {
24
+ const first = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1));
25
+ const last = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 0));
26
+ const day = (date) => `${String(date.getUTCFullYear())}${String(date.getUTCMonth() + 1).padStart(2, "0")}${String(date.getUTCDate()).padStart(2, "0")}00`;
27
+ return { start: day(first), end: day(last) };
28
+ }
29
+ export function createSearchInterest(deps) {
30
+ const headers = {
31
+ "User-Agent": `Slopify/${deps.version} (https://github.com/gentbajko/slopify)`,
32
+ Accept: "application/json",
33
+ };
34
+ const json = async (url, signal) => {
35
+ const timeout = AbortSignal.timeout(interestRequestMs);
36
+ let response;
37
+ try {
38
+ response = await deps.fetch(url, { headers, signal: AbortSignal.any([signal, timeout]) });
39
+ }
40
+ catch {
41
+ throw new Unanswered(timeout.aborted ? "timeout" : "offline");
42
+ }
43
+ if (!response.ok)
44
+ throw new Unanswered("refused");
45
+ try {
46
+ return await response.json();
47
+ }
48
+ catch {
49
+ throw new Unanswered("refused");
50
+ }
51
+ };
52
+ const reasonOf = (error) => error instanceof Unanswered ? error.reason : "offline";
53
+ const suggestions = async (topic, language, signal) => {
54
+ const q = encodeURIComponent(topic);
55
+ let failure = "offline";
56
+ for (const url of [
57
+ `https://suggestqueries.google.com/complete/search?client=firefox&hl=${language}&q=${q}`,
58
+ `https://duckduckgo.com/ac/?q=${q}&type=list&kl=wt-wt`,
59
+ ]) {
60
+ try {
61
+ const parsed = suggestAnswer.safeParse(await json(url, signal));
62
+ if (parsed.success)
63
+ return parsed.data[1].slice(0, 10);
64
+ failure = "refused";
65
+ }
66
+ catch (error) {
67
+ failure = reasonOf(error);
68
+ }
69
+ }
70
+ return failure;
71
+ };
72
+ const wikiViews = async (topic, language, signal) => {
73
+ try {
74
+ const found = openSearchAnswer.safeParse(await json(`https://${language}.wikipedia.org/w/api.php?action=opensearch&format=json&namespace=0&limit=1&search=${encodeURIComponent(topic)}`, signal));
75
+ if (!found.success)
76
+ return "refused";
77
+ const title = found.data[1][0];
78
+ if (title === undefined)
79
+ return null;
80
+ const { start, end } = lastMonth(deps.now());
81
+ let answer;
82
+ try {
83
+ answer = await json(`https://wikimedia.org/api/rest_v1/metrics/pageviews/per-article/${language}.wikipedia/all-access/user/${encodeURIComponent(title.replaceAll(" ", "_"))}/monthly/${start}/${end}`, signal);
84
+ }
85
+ catch (error) {
86
+ // An article too new to have a month of views answers 404.
87
+ if (reasonOf(error) === "refused")
88
+ return null;
89
+ throw error;
90
+ }
91
+ const views = pageviewsAnswer.safeParse(answer);
92
+ return views.success ? views.data.items.reduce((sum, item) => sum + item.views, 0) : null;
93
+ }
94
+ catch (error) {
95
+ return reasonOf(error);
96
+ }
97
+ };
98
+ return async (topic, rawLanguage, signal) => {
99
+ const language = languageOf(rawLanguage);
100
+ const [offered, views] = await Promise.all([
101
+ suggestions(topic, language, signal),
102
+ wikiViews(topic, language, signal),
103
+ ]);
104
+ const failed = (value) => typeof value === "string";
105
+ if (failed(offered) && failed(views))
106
+ return { ok: false, reason: offered };
107
+ return {
108
+ ok: true,
109
+ suggestions: failed(offered) ? null : offered,
110
+ wikiViews: failed(views) ? null : views,
111
+ };
112
+ };
113
+ }
@@ -1,3 +1,4 @@
1
+ import { safeJson } from "./http-failure.js";
1
2
  import { cartesiaVersion } from "./tts/cartesia.js";
2
3
  // Settings → Providers' Test button, per provider. Some answer a key they do not recognise
3
4
  // with something other than 401: Google with 400 API_KEY_INVALID, ElevenLabs with 400
@@ -21,6 +22,37 @@ function listed(ids) {
21
22
  };
22
23
  }
23
24
  const field = (value, key) => typeof value === "object" && value !== null ? value[key] : undefined;
25
+ function idsOf(rows, key, strip) {
26
+ if (!Array.isArray(rows))
27
+ return undefined;
28
+ return rows.flatMap((row) => {
29
+ const id = field(row, key);
30
+ return typeof id === "string" && id !== "" ? [strip ? id.replace(strip, "") : id] : [];
31
+ });
32
+ }
33
+ // `{ data: [{ id }] }`: OpenAI's and OpenRouter's lists.
34
+ function dataList(url) {
35
+ return {
36
+ url: () => url,
37
+ read: (body) => {
38
+ const ids = idsOf(field(safeJson(body), "data"), "id");
39
+ return ids === undefined ? undefined : { ids };
40
+ },
41
+ };
42
+ }
43
+ const googleBase = "https://generativelanguage.googleapis.com/v1beta/models?pageSize=1000";
44
+ const googleList = {
45
+ url: (page) => page === undefined ? googleBase : `${googleBase}&pageToken=${encodeURIComponent(page)}`,
46
+ read: (body) => {
47
+ const value = safeJson(body);
48
+ const ids = idsOf(field(value, "models"), "name", /^models\//);
49
+ if (ids === undefined)
50
+ return undefined;
51
+ const next = field(value, "nextPageToken");
52
+ return typeof next === "string" && next !== "" ? { ids, next } : { ids };
53
+ },
54
+ };
55
+ const openAiList = dataList("https://api.openai.com/v1/models");
24
56
  const google = {
25
57
  url: "https://generativelanguage.googleapis.com/v1beta/models?pageSize=1",
26
58
  headers: (key) => ({ "x-goog-api-key": key }),
@@ -28,6 +60,7 @@ const google = {
28
60
  model: {
29
61
  url: (model) => `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model.replace(/^models\//, ""))}`,
30
62
  },
63
+ list: googleList,
31
64
  };
32
65
  export const keyProbes = {
33
66
  openrouter: {
@@ -40,9 +73,20 @@ export const keyProbes = {
40
73
  return Array.isArray(data) ? data.map((one) => field(one, "id")) : [];
41
74
  }),
42
75
  },
76
+ list: dataList("https://openrouter.ai/api/v1/models"),
77
+ },
78
+ "openai-tts": {
79
+ url: "https://api.openai.com/v1/models",
80
+ headers: bearer,
81
+ model: openAiModel,
82
+ list: openAiList,
83
+ },
84
+ "openai-image": {
85
+ url: "https://api.openai.com/v1/models",
86
+ headers: bearer,
87
+ model: openAiModel,
88
+ list: openAiList,
43
89
  },
44
- "openai-tts": { url: "https://api.openai.com/v1/models", headers: bearer, model: openAiModel },
45
- "openai-image": { url: "https://api.openai.com/v1/models", headers: bearer, model: openAiModel },
46
90
  "google-image": google,
47
91
  "google-tts": google,
48
92
  // fal.ai's model list answers without a key; its price lookup requires one.
@@ -72,6 +116,13 @@ export const keyProbes = {
72
116
  url: () => "https://api.elevenlabs.io/v1/models",
73
117
  lists: listed((body) => Array.isArray(body) ? body.map((one) => field(one, "model_id")) : []),
74
118
  },
119
+ list: {
120
+ url: () => "https://api.elevenlabs.io/v1/models",
121
+ read: (body) => {
122
+ const ids = idsOf(safeJson(body), "model_id");
123
+ return ids === undefined ? undefined : { ids };
124
+ },
125
+ },
75
126
  },
76
127
  cartesia: {
77
128
  url: "https://api.cartesia.ai/voices?limit=1",
@@ -6,6 +6,7 @@ import { redact } from "../../kernel/log.js";
6
6
  import { documentMessages } from "../../kernel/ports/llm-documents.js";
7
7
  import { providerError } from "../../kernel/ports/model.js";
8
8
  import { cliCheck, cliName, cliReported, commandOf } from "../explain.js";
9
+ import { accessField } from "../model-access.js";
9
10
  import { nodeAntigravityModels } from "./antigravity-models.js";
10
11
  import { imageWorkspace, readFileName, unseenImages } from "./image-workspace.js";
11
12
  import { cliInput, cliShaped, deliveredInput, endedWithout, promptOf, stopCliRun, stuckCli, } from "./run-cli.js";
@@ -205,6 +206,8 @@ function failure(binary, model, message, sentence, now = Date.now) {
205
206
  if (/invalid model selection/i.test(message))
206
207
  return providerError({
207
208
  kind: "unsupported",
209
+ // agy lists models a plan can't run and refuses them with this sentence.
210
+ access: "no-access",
208
211
  message: `The ${cliName(binary)} does not offer the model "${model}". Run "agy models" in a terminal to see the ones it has, pick one in the Edit tab (Edit project → Providers), then use Continue the run.`,
209
212
  });
210
213
  if (/quota|resource.?exhausted|capacity/i.test(message) &&
@@ -233,6 +236,7 @@ function failure(binary, model, message, sentence, now = Date.now) {
233
236
  }
234
237
  return providerError({
235
238
  kind: "other",
239
+ ...accessField(null, message),
236
240
  message: sentence ?? cliReported(binary, redact(message), cliCheck(binary)),
237
241
  });
238
242
  }