@aglyn/plugins-ai 1.0.0-beta.229 → 1.0.0-beta.231

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 (340) hide show
  1. package/README.md +3 -3
  2. package/package.json +17 -17
  3. package/src/lib/activity/ai-activity-actions.js +7 -1
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  7. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  8. package/src/lib/components/ai-assist-provider.component.js +9 -10
  9. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  10. package/src/lib/components/ai-automation-zones.d.ts +38 -0
  11. package/src/lib/components/ai-automation-zones.js +5 -1
  12. package/src/lib/components/ai-automation-zones.js.map +1 -1
  13. package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
  14. package/src/lib/components/ai-brief-dialog.component.js +87 -8
  15. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  16. package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
  17. package/src/lib/components/ai-campaign-create.component.js +270 -0
  18. package/src/lib/components/ai-campaign-create.component.js.map +1 -0
  19. package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
  20. package/src/lib/components/ai-describe-automation.component.js +15 -6
  21. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  22. package/src/lib/components/ai-describe-button.component.d.ts +20 -8
  23. package/src/lib/components/ai-describe-button.component.js +19 -36
  24. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  25. package/src/lib/components/ai-describe-email.component.d.ts +31 -0
  26. package/src/lib/components/ai-describe-email.component.js +22 -0
  27. package/src/lib/components/ai-describe-email.component.js.map +1 -0
  28. package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
  29. package/src/lib/components/ai-describe-org-automation.component.js +238 -0
  30. package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
  31. package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
  32. package/src/lib/components/ai-experiment-cards.component.js +104 -18
  33. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  34. package/src/lib/components/ai-explain-automation.component.js +1 -5
  35. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  36. package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
  37. package/src/lib/components/ai-funnel-zones.component.js +178 -0
  38. package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
  39. package/src/lib/components/ai-insight-card.component.d.ts +42 -0
  40. package/src/lib/components/ai-insight-card.component.js +137 -0
  41. package/src/lib/components/ai-insight-card.component.js.map +1 -0
  42. package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
  43. package/src/lib/components/ai-insight-dialog.component.js +9 -3
  44. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  45. package/src/lib/components/ai-job-plan.component.d.ts +7 -2
  46. package/src/lib/components/ai-job-plan.component.js +38 -6
  47. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  48. package/src/lib/components/ai-job-requests.d.ts +17 -1
  49. package/src/lib/components/ai-job-requests.js +39 -3
  50. package/src/lib/components/ai-job-requests.js.map +1 -1
  51. package/src/lib/components/ai-logic-zones.d.ts +64 -0
  52. package/src/lib/components/ai-logic-zones.js +18 -0
  53. package/src/lib/components/ai-logic-zones.js.map +1 -0
  54. package/src/lib/components/ai-logic.component.d.ts +77 -0
  55. package/src/lib/components/ai-logic.component.js +553 -0
  56. package/src/lib/components/ai-logic.component.js.map +1 -0
  57. package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
  58. package/src/lib/components/ai-marketing-insight.component.js +80 -0
  59. package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
  60. package/src/lib/components/ai-media-create.component.d.ts +55 -0
  61. package/src/lib/components/ai-media-create.component.js +433 -0
  62. package/src/lib/components/ai-media-create.component.js.map +1 -0
  63. package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
  64. package/src/lib/components/ai-overlay-cards.component.js +505 -0
  65. package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
  66. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  67. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  68. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  69. package/src/lib/components/ai-product-zones.d.ts +5 -0
  70. package/src/lib/components/ai-product-zones.js +1 -1
  71. package/src/lib/components/ai-product-zones.js.map +1 -1
  72. package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
  73. package/src/lib/components/ai-products-create-button.component.js +67 -0
  74. package/src/lib/components/ai-products-create-button.component.js.map +1 -0
  75. package/src/lib/components/ai-products-hub-card.component.js +100 -81
  76. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  77. package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
  78. package/src/lib/components/ai-revise-automation.component.js +207 -0
  79. package/src/lib/components/ai-revise-automation.component.js.map +1 -0
  80. package/src/lib/components/ai-site-build-page.component.js +160 -24
  81. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  82. package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
  83. package/src/lib/components/ai-upsell-dialog.component.js +177 -0
  84. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
  85. package/src/lib/components/assist-build-card.component.d.ts +42 -0
  86. package/src/lib/components/assist-build-card.component.js +183 -0
  87. package/src/lib/components/assist-build-card.component.js.map +1 -0
  88. package/src/lib/components/assist-edit-canvas.d.ts +2 -0
  89. package/src/lib/components/assist-edit-canvas.js +56 -1
  90. package/src/lib/components/assist-edit-canvas.js.map +1 -1
  91. package/src/lib/components/assist-jobs-drawer.component.js +48 -3
  92. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  93. package/src/lib/components/assist-panel.component.js +51 -2
  94. package/src/lib/components/assist-panel.component.js.map +1 -1
  95. package/src/lib/components/use-ai-job-run.d.ts +3 -14
  96. package/src/lib/components/use-ai-job-run.js +0 -50
  97. package/src/lib/components/use-ai-job-run.js.map +1 -1
  98. package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
  99. package/src/lib/jobs/ai-build-capabilities.js +256 -0
  100. package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
  101. package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
  102. package/src/lib/jobs/ai-build-unit-outcome.js +140 -0
  103. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
  104. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  105. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  106. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  107. package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
  108. package/src/lib/jobs/ai-job-build-step.js +668 -0
  109. package/src/lib/jobs/ai-job-build-step.js.map +1 -0
  110. package/src/lib/jobs/ai-job-draft-ids.js +9 -1
  111. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  112. package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
  113. package/src/lib/jobs/ai-job-drafts.js +25 -4
  114. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  115. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  116. package/src/lib/jobs/ai-job-generation.js +13 -2
  117. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  118. package/src/lib/jobs/ai-job-insight-step.js +8 -1
  119. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  120. package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
  121. package/src/lib/jobs/ai-job-layout-language.js +194 -0
  122. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  123. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  124. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  125. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  126. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  127. package/src/lib/jobs/ai-job-layout-step.js +105 -5
  128. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  129. package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
  130. package/src/lib/jobs/ai-job-logic-step.js +395 -0
  131. package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
  132. package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
  133. package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
  134. package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
  135. package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
  136. package/src/lib/jobs/ai-job-page-language.js +355 -0
  137. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  138. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  139. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  140. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  141. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  142. package/src/lib/jobs/ai-job-page-step.js +111 -20
  143. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  144. package/src/lib/jobs/ai-job-plan-step.d.ts +27 -2
  145. package/src/lib/jobs/ai-job-plan-step.js +87 -13
  146. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  147. package/src/lib/jobs/ai-job-site-step.d.ts +19 -1
  148. package/src/lib/jobs/ai-job-site-step.js +220 -61
  149. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  150. package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
  151. package/src/lib/jobs/ai-job-text-step.js +12 -0
  152. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  153. package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
  154. package/src/lib/jobs/ai-job-workflow-step.js +320 -15
  155. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  156. package/src/lib/jobs/ai-jobs.d.ts +80 -2
  157. package/src/lib/jobs/ai-jobs.js +391 -18
  158. package/src/lib/jobs/ai-jobs.js.map +1 -1
  159. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  160. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  161. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  162. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  163. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  164. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  165. package/src/lib/jobs/ai-logic-records.d.ts +40 -0
  166. package/src/lib/jobs/ai-logic-records.js +55 -0
  167. package/src/lib/jobs/ai-logic-records.js.map +1 -0
  168. package/src/lib/jobs/ai-site-publish.d.ts +7 -0
  169. package/src/lib/jobs/ai-site-publish.js +9 -0
  170. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  171. package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
  172. package/src/lib/jobs/ai-workflow-records.js +40 -1
  173. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  174. package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
  175. package/src/lib/layout-language/ai-layout-compiler.js +1148 -0
  176. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  177. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  178. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  179. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  180. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  181. package/src/lib/layout-language/ai-layout-frame.js +457 -0
  182. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  183. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  184. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  185. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  186. package/src/lib/layout-language/ai-layout-language.d.ts +161 -0
  187. package/src/lib/layout-language/ai-layout-language.js +446 -0
  188. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  189. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  190. package/src/lib/layout-language/ai-layout-links.js +109 -0
  191. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  192. package/src/lib/layout-language/ai-layout-store.d.ts +39 -0
  193. package/src/lib/layout-language/ai-layout-store.js +69 -0
  194. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  195. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  196. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  197. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  198. package/src/lib/lazy-widget.d.ts +45 -0
  199. package/src/lib/lazy-widget.js +53 -0
  200. package/src/lib/lazy-widget.js.map +1 -0
  201. package/src/lib/model/ai-automation-draft.d.ts +18 -0
  202. package/src/lib/model/ai-automation-draft.js +86 -0
  203. package/src/lib/model/ai-automation-draft.js.map +1 -1
  204. package/src/lib/model/ai-build-job.d.ts +201 -0
  205. package/src/lib/model/ai-build-job.js +472 -0
  206. package/src/lib/model/ai-build-job.js.map +1 -0
  207. package/src/lib/model/ai-build-plan.d.ts +61 -0
  208. package/src/lib/model/ai-build-plan.js +154 -9
  209. package/src/lib/model/ai-build-plan.js.map +1 -1
  210. package/src/lib/model/ai-build-progress.d.ts +73 -0
  211. package/src/lib/model/ai-build-progress.js +170 -0
  212. package/src/lib/model/ai-build-progress.js.map +1 -0
  213. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  214. package/src/lib/model/ai-host-publish-context.js +104 -0
  215. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  216. package/src/lib/model/ai-insight.d.ts +20 -10
  217. package/src/lib/model/ai-insight.js +72 -9
  218. package/src/lib/model/ai-insight.js.map +1 -1
  219. package/src/lib/model/ai-job-activity.d.ts +22 -0
  220. package/src/lib/model/ai-job-activity.js +25 -2
  221. package/src/lib/model/ai-job-activity.js.map +1 -1
  222. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  223. package/src/lib/model/ai-job-failure-copy.js +56 -1
  224. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  225. package/src/lib/model/ai-job-notice.d.ts +3 -1
  226. package/src/lib/model/ai-job-notice.js +31 -9
  227. package/src/lib/model/ai-job-notice.js.map +1 -1
  228. package/src/lib/model/ai-jobs.types.d.ts +99 -2
  229. package/src/lib/model/ai-jobs.types.js +3 -1
  230. package/src/lib/model/ai-jobs.types.js.map +1 -1
  231. package/src/lib/model/ai-logic-job.d.ts +108 -0
  232. package/src/lib/model/ai-logic-job.js +327 -0
  233. package/src/lib/model/ai-logic-job.js.map +1 -0
  234. package/src/lib/model/ai-media-credits.d.ts +52 -0
  235. package/src/lib/model/ai-media-credits.js +61 -0
  236. package/src/lib/model/ai-media-credits.js.map +1 -0
  237. package/src/lib/model/ai-overlay-copy.d.ts +114 -0
  238. package/src/lib/model/ai-overlay-copy.js +175 -0
  239. package/src/lib/model/ai-overlay-copy.js.map +1 -0
  240. package/src/lib/model/ai-site-build-progress.d.ts +25 -4
  241. package/src/lib/model/ai-site-build-progress.js +194 -11
  242. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  243. package/src/lib/model/ai-site-job.d.ts +8 -0
  244. package/src/lib/model/ai-site-job.js +9 -0
  245. package/src/lib/model/ai-site-job.js.map +1 -1
  246. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  247. package/src/lib/model/ai-site-start.d.ts +5 -2
  248. package/src/lib/model/ai-site-start.js +5 -2
  249. package/src/lib/model/ai-site-start.js.map +1 -1
  250. package/src/lib/model/ai-svg.d.ts +69 -0
  251. package/src/lib/model/ai-svg.js +265 -0
  252. package/src/lib/model/ai-svg.js.map +1 -0
  253. package/src/lib/model/ai-usage-by-user.d.ts +3 -2
  254. package/src/lib/model/ai-usage-by-user.js.map +1 -1
  255. package/src/lib/model/ai-workflow-job.d.ts +92 -4
  256. package/src/lib/model/ai-workflow-job.js +124 -7
  257. package/src/lib/model/ai-workflow-job.js.map +1 -1
  258. package/src/lib/model/assist-build.d.ts +58 -0
  259. package/src/lib/model/assist-build.js +52 -0
  260. package/src/lib/model/assist-build.js.map +1 -0
  261. package/src/lib/model/assist-edit.d.ts +48 -3
  262. package/src/lib/model/assist-edit.js +48 -4
  263. package/src/lib/model/assist-edit.js.map +1 -1
  264. package/src/lib/plugin-config.js +2 -0
  265. package/src/lib/plugin-config.js.map +1 -1
  266. package/src/lib/plugin.js +308 -47
  267. package/src/lib/plugin.js.map +1 -1
  268. package/src/lib/providers/catalog.d.ts +82 -5
  269. package/src/lib/providers/catalog.js +83 -12
  270. package/src/lib/providers/catalog.js.map +1 -1
  271. package/src/lib/providers/image-contract.d.ts +116 -0
  272. package/src/lib/providers/image-contract.js +74 -0
  273. package/src/lib/providers/image-contract.js.map +1 -0
  274. package/src/lib/providers/model-choice.js +11 -0
  275. package/src/lib/providers/model-choice.js.map +1 -1
  276. package/src/lib/providers/routing.js +18 -0
  277. package/src/lib/providers/routing.js.map +1 -1
  278. package/src/lib/providers/vertex-image.d.ts +121 -0
  279. package/src/lib/providers/vertex-image.js +312 -0
  280. package/src/lib/providers/vertex-image.js.map +1 -0
  281. package/src/lib/runtime/ai-doctrine-validators.d.ts +101 -0
  282. package/src/lib/runtime/ai-doctrine-validators.js +525 -4
  283. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  284. package/src/lib/runtime/ai-doctrine.js +21 -6
  285. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  286. package/src/lib/runtime/ai-eval-live.js +4 -0
  287. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  288. package/src/lib/runtime/ai-eval.d.ts +15 -1
  289. package/src/lib/runtime/ai-eval.js +52 -1
  290. package/src/lib/runtime/ai-eval.js.map +1 -1
  291. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  292. package/src/lib/runtime/ai-icon-library.js +254 -0
  293. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  294. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  295. package/src/lib/runtime/ai-node-tree.js +30 -4
  296. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  297. package/src/lib/runtime/ai-palette.d.ts +6 -0
  298. package/src/lib/runtime/ai-palette.generated.js +70 -0
  299. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  300. package/src/lib/runtime/ai-palette.js.map +1 -1
  301. package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
  302. package/src/lib/runtime/experiment-variant-copy.js +102 -0
  303. package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
  304. package/src/lib/server/ai-experiment-versions.d.ts +51 -0
  305. package/src/lib/server/ai-experiment-versions.js +270 -0
  306. package/src/lib/server/ai-experiment-versions.js.map +1 -0
  307. package/src/lib/server/ai-jobs-resume.js +74 -4
  308. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  309. package/src/lib/server/ai-media-image.d.ts +66 -0
  310. package/src/lib/server/ai-media-image.js +521 -0
  311. package/src/lib/server/ai-media-image.js.map +1 -0
  312. package/src/lib/server/ai-media-svg.d.ts +91 -0
  313. package/src/lib/server/ai-media-svg.js +186 -0
  314. package/src/lib/server/ai-media-svg.js.map +1 -0
  315. package/src/lib/server/assist-build.d.ts +54 -0
  316. package/src/lib/server/assist-build.js +121 -0
  317. package/src/lib/server/assist-build.js.map +1 -0
  318. package/src/lib/server/assist-chat.js +108 -12
  319. package/src/lib/server/assist-chat.js.map +1 -1
  320. package/src/lib/server/assist-edit.js +213 -3
  321. package/src/lib/server/assist-edit.js.map +1 -1
  322. package/src/lib/server/assist-view-context.js +242 -4
  323. package/src/lib/server/assist-view-context.js.map +1 -1
  324. package/src/lib/server.js +26 -0
  325. package/src/lib/server.js.map +1 -1
  326. package/src/lib/tools/ai-logic-tool.d.ts +27 -0
  327. package/src/lib/tools/ai-logic-tool.js +207 -0
  328. package/src/lib/tools/ai-logic-tool.js.map +1 -0
  329. package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
  330. package/src/lib/tools/ai-overlay-tool.js +107 -0
  331. package/src/lib/tools/ai-overlay-tool.js.map +1 -0
  332. package/src/lib/tools/ai-workflow-tool.d.ts +17 -2
  333. package/src/lib/tools/ai-workflow-tool.js +47 -14
  334. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  335. package/src/lib/usage/assist-job-refund.d.ts +28 -1
  336. package/src/lib/usage/assist-job-refund.js +44 -9
  337. package/src/lib/usage/assist-job-refund.js.map +1 -1
  338. package/src/lib/usage/assist-usage.d.ts +5 -0
  339. package/src/lib/usage/assist-usage.js +2 -0
  340. package/src/lib/usage/assist-usage.js.map +1 -1
@@ -0,0 +1,521 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ // lockdown-423: via libs/plugins/ai/src/lib/runtime/ai-gate.ts
18
+ // The POST climbs `aiGateLadder`, whose lockdown rung is the verdict, and
19
+ // every stored picture also passes the upload route's.
20
+ import { mediaFilterKeys } from "@aglyn/aglyn/app-utils/media-metadata";
21
+ import { MEDIA_ALT_MAX_LENGTH } from "@aglyn/aglyn/app-utils/media-alt";
22
+ import { aiMediaCreditsPerPicture } from "../model/ai-media-credits.js";
23
+ import { aiSvgThemePalette, isAiSvgColor } from "../model/ai-svg.js";
24
+ import { estimateAiBilledUsd } from "../providers/catalog.js";
25
+ import { AI_UPSTREAM_FAILURE_COPY, AiUpstreamError } from "../providers/contract.js";
26
+ import { AI_IMAGE_ASPECT_RATIOS, AI_IMAGE_MAX_COUNT, AI_IMAGE_PROMPT_MAX_CHARS, AI_SVG_MAX_COLORS, AI_SVG_STYLES, AiImageSafetyRefusal, isAiImageAspectRatio } from "../providers/image-contract.js";
27
+ import { vertexImageProvider } from "../providers/vertex-image.js";
28
+ import { aiGateLadder } from "../runtime/ai-gate.js";
29
+ import { aiInventoryTheme } from "../runtime/site-inventory.js";
30
+ import { assistBandRefuses, assistCreditsFromUsd } from "../usage/assist-credits.js";
31
+ import { publicAssistQuota, recordAssistCost, releaseAssistMessage } from "../usage/assist-usage.js";
32
+ import { aiMediaSvgModel, generateAiMediaSvg } from "./ai-media-svg.js";
33
+ /**
34
+ * "Create with AI" in Media (AGL-3602): pictures from a description, stored
35
+ * in the library that asked.
36
+ *
37
+ * `POST /api/ai/media/images { orgId, library, hostId?, folderId?, mode,
38
+ * prompt, aspectRatio, count, style?, palette? }` answers
39
+ * `{ mediaIds, filtered, failed, credits, warning? }`.
40
+ *
41
+ * Two modes:
42
+ *
43
+ * - `photo` — Google's image models on Vertex AI (`vertex-image.ts`). Off
44
+ * unless the deployment configured them; the door answers 404 with a
45
+ * sentence the dialog shows.
46
+ * - `illustration` — an SVG drawn by the text provider every other AI door
47
+ * uses (`ai-media-svg.ts`): an illustration, an icon, a tileable pattern or
48
+ * a simple logo mark, in the site's theme colors or the person's own. No
49
+ * new vendor, so it runs wherever text AI does.
50
+ *
51
+ * There is no GET: the button draws from the shell's own gates, and this door
52
+ * decides when someone asks for a picture.
53
+ *
54
+ * ## A request, not a job
55
+ *
56
+ * Both modes answer inside the request, the way "Save as a component with
57
+ * AI" does, and are metered on the same meter every door uses.
58
+ *
59
+ * ## The money, in order
60
+ *
61
+ * 1. `aiGateLadder` RESERVES before anything is spent: the `aiGenerative`
62
+ * entitlement, `release_ai_generative`, the `ai-generate` kill switch,
63
+ * `ai.generate` on the site, the per-account and per-address windows, the
64
+ * Free taste's own rungs and the band.
65
+ * 2. A workspace whose band is a wall (Free, or a hard cap) is refused before
66
+ * the provider is called when the pictures' estimate is more than it has
67
+ * left.
68
+ * 3. The provider is called. A fault of ours or the provider's hands the
69
+ * reservation back and charges nothing. A declined description charges
70
+ * only what the decline spent, recorded as a declined request (which a
71
+ * Free workspace is never charged for, and which its daily refusal pause
72
+ * counts).
73
+ * 4. Each picture is stored, and only what was stored is SETTLED, at billed
74
+ * rates above the provider's: a photo per picture plus its prompt and
75
+ * thinking, an illustration as the tokens that drew it. A picture held
76
+ * back, one the library refused, and an illustration that failed its
77
+ * safety check twice are never charged — that last one is ours.
78
+ *
79
+ * ## Stored the way any upload is stored
80
+ *
81
+ * Each picture is posted to the console's own `/api/media/upload` with the
82
+ * caller's credential, so the scope, the uploads lockdown, the structural
83
+ * inspection, SVG sanitizing, the quarantine list, the storage band, the CDN
84
+ * variants and the storage counter all apply exactly as they do to a file
85
+ * the person dropped in. This door writes no media document of its own; it
86
+ * only adds, to the documents that route made, the alt text and the record
87
+ * of how the picture was made.
88
+ */ /** The per-account window: a person makes a few sets a minute, not dozens. */ const RATE_LIMIT = {
89
+ key: 'ai-media-image',
90
+ limit: 6,
91
+ windowMs: 60000
92
+ };
93
+ /** What a person reads when a picture failed on our side. */ export const AI_MEDIA_OURS_COPY = "This one's on us — you weren't charged. The picture didn't come out right; try again, or describe it a little differently.";
94
+ /** What the door answers on a deployment that makes no photos. */ export const AI_MEDIA_NO_PHOTOS_COPY = "Photos aren't available here yet. Choose Illustration to draw one instead.";
95
+ /** The provider photos come from; a spec stands one in through jest. */ function imageProvider() {
96
+ return vertexImageProvider;
97
+ }
98
+ /** Every control character as a space, squeezed, trimmed and cut. */ export function cleanAiImagePrompt(value) {
99
+ let out = '';
100
+ for (const char of String(value != null ? value : '')){
101
+ const code = char.charCodeAt(0);
102
+ out += code < 32 || code === 127 || code === 0x2028 || code === 0x2029 ? ' ' : char;
103
+ }
104
+ return out.replace(/\s+/g, ' ').trim().slice(0, AI_IMAGE_PROMPT_MAX_CHARS);
105
+ }
106
+ /**
107
+ * The alt text a picture is stored with: the sentence given, as a sentence,
108
+ * cut at a word inside the library's alt limit. A person edits it like any
109
+ * other.
110
+ */ export function aiImageAltText(text) {
111
+ const clean = text.replace(/\s+/g, ' ').trim();
112
+ if (!clean) return '';
113
+ const sentence = clean.charAt(0).toUpperCase() + clean.slice(1);
114
+ if (sentence.length <= MEDIA_ALT_MAX_LENGTH) return sentence;
115
+ const cut = sentence.slice(0, MEDIA_ALT_MAX_LENGTH - 1);
116
+ const space = cut.lastIndexOf(' ');
117
+ return `${(space > 40 ? cut.slice(0, space) : cut).trim()}…`;
118
+ }
119
+ const EXTENSIONS = {
120
+ 'image/png': 'png',
121
+ 'image/jpeg': 'jpg',
122
+ 'image/webp': 'webp',
123
+ 'image/svg+xml': 'svg'
124
+ };
125
+ /** A file name from the description's first words: `ai-a-red-barn-2.png`. */ export function aiImageFileName(prompt, index, mimeType) {
126
+ var _EXTENSIONS_mimeType;
127
+ const words = prompt.toLowerCase().normalize('NFKD').replace(/[^a-z0-9\s-]/g, ' ').split(/\s+/).filter(Boolean).slice(0, 6).join('-').slice(0, 60).replace(/-+$/, '');
128
+ return `ai-${words || 'image'}-${index + 1}.${(_EXTENSIONS_mimeType = EXTENSIONS[mimeType]) != null ? _EXTENSIONS_mimeType : 'png'}`;
129
+ }
130
+ export function parseAiMediaImageInput(body) {
131
+ var _ref;
132
+ const text = (key, limit = 128)=>{
133
+ const value = body == null ? void 0 : body[key];
134
+ return typeof value === 'string' ? value.trim().slice(0, limit) : '';
135
+ };
136
+ const count = Math.floor(Number((_ref = body == null ? void 0 : body['count']) != null ? _ref : 1));
137
+ const palette = body == null ? void 0 : body['palette'];
138
+ const colors = Array.isArray(palette == null ? void 0 : palette['colors']) ? palette == null ? void 0 : palette['colors'] : [];
139
+ return {
140
+ orgId: text('orgId'),
141
+ library: (body == null ? void 0 : body['library']) === 'org' ? 'org' : 'host',
142
+ hostId: text('hostId') || null,
143
+ folderId: text('folderId', 64) || null,
144
+ mode: (body == null ? void 0 : body['mode']) === 'illustration' ? 'illustration' : 'photo',
145
+ prompt: cleanAiImagePrompt(body == null ? void 0 : body['prompt']),
146
+ aspectRatio: text('aspectRatio', 8),
147
+ count: Number.isFinite(count) ? count : 0,
148
+ style: text('style', 24) || 'illustration',
149
+ palette: (palette == null ? void 0 : palette['source']) === 'custom' ? {
150
+ source: 'custom',
151
+ colors: colors.map((color)=>String(color != null ? color : '').trim().toLowerCase()).slice(0, AI_SVG_MAX_COLORS + 1)
152
+ } : {
153
+ source: 'theme'
154
+ }
155
+ };
156
+ }
157
+ /**
158
+ * Posts one picture to the console's own upload route, as the caller. The
159
+ * same body the library's Upload button sends, so the route cannot tell the
160
+ * two apart and applies every check it applies to that one.
161
+ */ async function storeThroughUploadRoute(request, input, image, fileName) {
162
+ const scope = input.library === 'org' ? _extends({
163
+ orgId: input.orgId
164
+ }, input.hostId ? {
165
+ forHostId: input.hostId
166
+ } : {}) : {
167
+ hostId: input.hostId
168
+ };
169
+ let response;
170
+ try {
171
+ var _request_headers_get;
172
+ response = await fetch(new URL('/api/media/upload', request.url), {
173
+ method: 'POST',
174
+ headers: {
175
+ Authorization: (_request_headers_get = request.headers.get('authorization')) != null ? _request_headers_get : '',
176
+ 'Content-Type': 'application/json'
177
+ },
178
+ body: JSON.stringify(_extends({}, scope, {
179
+ fileName,
180
+ contentType: image.mimeType,
181
+ folderId: input.folderId,
182
+ data: image.base64
183
+ }))
184
+ });
185
+ } catch (unused) {
186
+ return {
187
+ error: 'The picture could not be saved to the media library.',
188
+ status: 502
189
+ };
190
+ }
191
+ const payload = await response.json().catch(()=>({}));
192
+ const mediaId = typeof payload['mediaId'] === 'string' ? payload['mediaId'] : '';
193
+ if (response.ok && mediaId) return {
194
+ mediaId
195
+ };
196
+ return {
197
+ error: typeof payload['error'] === 'string' ? payload['error'] : 'The picture could not be saved to the media library.',
198
+ status: response.ok ? 502 : response.status
199
+ };
200
+ }
201
+ /**
202
+ * Adds the alt text and the provenance to a document the upload route made.
203
+ * An `update`, which cannot mint a document: the route minted it.
204
+ */ async function annotateStoredImage(gate, input, mediaId, alt, provenance) {
205
+ var _data;
206
+ const scopeRef = input.library === 'org' ? gate.firestore.collection('orgs').doc(input.orgId) : gate.firestore.collection('hosts').doc(String(input.hostId));
207
+ const mediaRef = scopeRef.collection('media').doc(mediaId);
208
+ const stored = (_data = (await mediaRef.get()).data()) != null ? _data : {};
209
+ await mediaRef.update(_extends({
210
+ alt,
211
+ description: 'Created with AI from a description.',
212
+ aiGenerated: _extends({}, provenance, {
213
+ createdAt: new Date()
214
+ })
215
+ }, mediaFilterKeys(_extends({}, stored, {
216
+ alt
217
+ }))));
218
+ }
219
+ /** The colors an illustration is drawn in. */ async function illustrationPalette(gate, input) {
220
+ var _snapshot_data;
221
+ var _aiInventoryTheme;
222
+ if (input.palette.source === 'custom') return input.palette.colors;
223
+ if (!input.hostId) return [];
224
+ const snapshot = await gate.firestore.collection('hosts').doc(input.hostId).get();
225
+ return aiSvgThemePalette((_aiInventoryTheme = aiInventoryTheme(snapshot.exists ? (_snapshot_data = snapshot.data()) != null ? _snapshot_data : null : null)) == null ? void 0 : _aiInventoryTheme.colors);
226
+ }
227
+ const ZERO_USAGE = {
228
+ inputTokens: 0,
229
+ outputTokens: 0,
230
+ cacheReadTokens: 0,
231
+ cacheWriteTokens: 0
232
+ };
233
+ const sumUsage = (usages)=>usages.reduce((sum, usage)=>({
234
+ inputTokens: sum.inputTokens + usage.inputTokens,
235
+ outputTokens: sum.outputTokens + usage.outputTokens,
236
+ cacheReadTokens: sum.cacheReadTokens + usage.cacheReadTokens,
237
+ cacheWriteTokens: sum.cacheWriteTokens + usage.cacheWriteTokens
238
+ }), ZERO_USAGE);
239
+ export async function POST(request) {
240
+ var _ref;
241
+ let body;
242
+ try {
243
+ body = await request.json();
244
+ } catch (unused) {
245
+ body = null;
246
+ }
247
+ const input = parseAiMediaImageInput(body);
248
+ const provider = imageProvider();
249
+ const svgModel = input.mode === 'illustration' ? aiMediaSvgModel() : undefined;
250
+ // A deployment that makes no pictures of this kind has no such door, so
251
+ // nothing about the caller or the workspace is read first.
252
+ if (input.mode === 'photo' && !provider.configured()) {
253
+ return Response.json({
254
+ error: AI_MEDIA_NO_PHOTOS_COPY,
255
+ reason: 'unavailable'
256
+ }, {
257
+ status: 404
258
+ });
259
+ }
260
+ if (input.mode === 'illustration' && !svgModel) {
261
+ return Response.json({
262
+ error: 'Not found'
263
+ }, {
264
+ status: 404
265
+ });
266
+ }
267
+ const gate = await aiGateLadder({
268
+ request,
269
+ orgId: input.orgId,
270
+ hostId: input.hostId
271
+ }, {
272
+ feature: 'aiGenerative',
273
+ releaseFlag: 'release_ai_generative',
274
+ lockdownFeature: 'ai-generate',
275
+ permission: 'ai.generate',
276
+ rateLimit: RATE_LIMIT
277
+ });
278
+ if (gate instanceof Response) return gate;
279
+ // Nothing is spent until a provider answers, so every exit before it
280
+ // hands the reservation back.
281
+ const release = ()=>releaseAssistMessage(gate.firestore, gate.orgId, gate.reservation).catch(()=>undefined);
282
+ const refuse = async (error, status, extra = {})=>{
283
+ await release();
284
+ return Response.json(_extends({
285
+ error
286
+ }, extra), {
287
+ status
288
+ });
289
+ };
290
+ if (input.library === 'host' && !input.hostId) {
291
+ return refuse('Open the site whose media library the pictures go in.', 400);
292
+ }
293
+ if (!input.prompt) return refuse('Describe the picture you want first.', 400);
294
+ if (!isAiImageAspectRatio(input.aspectRatio)) {
295
+ return refuse(`Choose a shape: ${AI_IMAGE_ASPECT_RATIOS.join(', ')}.`, 400);
296
+ }
297
+ const aspectRatio = input.aspectRatio;
298
+ if (input.count < 1 || input.count > AI_IMAGE_MAX_COUNT) {
299
+ return refuse(`Make between 1 and ${AI_IMAGE_MAX_COUNT} pictures at a time.`, 400);
300
+ }
301
+ if (input.mode === 'illustration') {
302
+ if (!AI_SVG_STYLES.includes(input.style)) {
303
+ return refuse('Choose an illustration, an icon, a pattern or a logo mark.', 400);
304
+ }
305
+ if (input.palette.source === 'custom' && (!input.palette.colors.length || input.palette.colors.length > AI_SVG_MAX_COLORS || !input.palette.colors.every(isAiSvgColor))) {
306
+ return refuse(`Give between 1 and ${AI_SVG_MAX_COLORS} colors as hex values, such as #1a73e8.`, 400);
307
+ }
308
+ }
309
+ const model = input.mode === 'photo' ? provider.defaultModel() : svgModel;
310
+ const perPicture = aiMediaCreditsPerPicture(input.mode, model);
311
+ const estimate = perPicture * input.count;
312
+ const credits = publicAssistQuota(gate.reservation).credits;
313
+ if (credits && assistBandRefuses(gate.org) && credits.remaining < estimate) {
314
+ return refuse(credits.remaining < perPicture ? 'This workspace has used its AI credits for the month.' : `That needs about ${estimate} credits and this workspace has ${credits.remaining} left this month. Make fewer pictures.`, 429, {
315
+ reason: 'quota',
316
+ quota: publicAssistQuota(gate.reservation)
317
+ });
318
+ }
319
+ /** Records what a decline spent: a declined request, never a picture. */ const recordDecline = (usage, declinedModel)=>{
320
+ var _gate_reservation_free;
321
+ return recordAssistCost(gate.firestore, gate.orgId, {
322
+ route: '/api/ai/media/images',
323
+ hostId: input.hostId,
324
+ model: declinedModel,
325
+ tier: 'entitled',
326
+ usage: _extends({}, usage, {
327
+ images: 0
328
+ }),
329
+ docsPaths: [],
330
+ stopReason: 'refusal',
331
+ free: (_gate_reservation_free = gate.reservation.free) != null ? _gate_reservation_free : null,
332
+ uid: gate.uid,
333
+ kind: 'image'
334
+ }).catch((meterError)=>console.error('ai media refusal not recorded', {
335
+ orgId: gate.orgId,
336
+ meterError
337
+ }));
338
+ };
339
+ let made;
340
+ /** Pictures asked for and not made: held back, declined or failed on our side. */ let unmade;
341
+ /** Tokens a photo request spent beside its pictures, charged once any is stored. */ let photoUsage = ZERO_USAGE;
342
+ let declineReason = null;
343
+ let style;
344
+ if (input.mode === 'photo') {
345
+ try {
346
+ const result = await provider.generate({
347
+ model,
348
+ prompt: input.prompt,
349
+ aspectRatio,
350
+ count: input.count
351
+ });
352
+ photoUsage = result.usage;
353
+ unmade = result.filtered;
354
+ made = result.images.map((image)=>_extends({}, image, {
355
+ alt: aiImageAltText(input.prompt),
356
+ usage: ZERO_USAGE
357
+ }));
358
+ } catch (error) {
359
+ if (error instanceof AiImageSafetyRefusal) {
360
+ // Declined, not failed: no picture is charged, and the request is
361
+ // recorded as a declined one so the Free taste's refusal pause
362
+ // counts it.
363
+ await recordDecline(ZERO_USAGE, model);
364
+ return Response.json({
365
+ error: error.message,
366
+ reason: 'safety'
367
+ }, {
368
+ status: 422
369
+ });
370
+ }
371
+ await release();
372
+ console.error('ai photo generation failed', {
373
+ orgId: gate.orgId,
374
+ error
375
+ });
376
+ const status = error instanceof AiUpstreamError && error.accountProblem ? 503 : 502;
377
+ return Response.json({
378
+ error: AI_UPSTREAM_FAILURE_COPY
379
+ }, {
380
+ status
381
+ });
382
+ }
383
+ } else {
384
+ var _ref1;
385
+ var _declined_;
386
+ style = input.style;
387
+ const palette = await illustrationPalette(gate, input);
388
+ const settled = await Promise.allSettled(Array.from({
389
+ length: input.count
390
+ }, (_unused, variant)=>generateAiMediaSvg({
391
+ model,
392
+ prompt: input.prompt,
393
+ style: style,
394
+ aspectRatio,
395
+ palette,
396
+ variant,
397
+ count: input.count
398
+ })));
399
+ const outcomes = settled.flatMap((entry)=>entry.status === 'fulfilled' ? [
400
+ entry.value
401
+ ] : []);
402
+ if (!outcomes.length) {
403
+ var _settled_;
404
+ await release();
405
+ console.error('ai illustration generation failed', {
406
+ orgId: gate.orgId,
407
+ error: (_settled_ = settled[0]) == null ? void 0 : _settled_.reason
408
+ });
409
+ return Response.json({
410
+ error: AI_UPSTREAM_FAILURE_COPY
411
+ }, {
412
+ status: 502
413
+ });
414
+ }
415
+ const drawn = outcomes.filter((outcome)=>outcome.kind === 'drawn');
416
+ const declined = outcomes.filter((outcome)=>outcome.kind === 'declined');
417
+ unmade = input.count - drawn.length;
418
+ declineReason = (_ref1 = (_declined_ = declined[0]) == null ? void 0 : _declined_.reason) != null ? _ref1 : null;
419
+ made = drawn.map((outcome)=>({
420
+ base64: Buffer.from(outcome.svg, 'utf8').toString('base64'),
421
+ mimeType: 'image/svg+xml',
422
+ alt: aiImageAltText(outcome.alt || input.prompt),
423
+ usage: outcome.usage
424
+ }));
425
+ if (!made.length) {
426
+ if (declined.length) {
427
+ // The model declined: what it spent is a declined request.
428
+ await recordDecline(sumUsage(declined.map((outcome)=>outcome.usage)), model);
429
+ return Response.json({
430
+ error: declineReason,
431
+ reason: 'safety'
432
+ }, {
433
+ status: 422
434
+ });
435
+ }
436
+ // Every picture failed its check twice. Ours, so nothing is charged.
437
+ await release();
438
+ return Response.json({
439
+ error: AI_MEDIA_OURS_COPY,
440
+ reason: 'ours'
441
+ }, {
442
+ status: 502
443
+ });
444
+ }
445
+ }
446
+ const stored = [];
447
+ let refusal = null;
448
+ for (const [index, picture] of made.entries()){
449
+ const outcome = await storeThroughUploadRoute(request, input, picture, aiImageFileName(input.prompt, index, picture.mimeType));
450
+ if ('mediaId' in outcome) stored.push({
451
+ mediaId: outcome.mediaId,
452
+ picture
453
+ });
454
+ else refusal != null ? refusal : refusal = outcome;
455
+ }
456
+ if (!stored.length) {
457
+ var _ref2, _ref3;
458
+ // Made and not kept: the library refused every picture (its storage
459
+ // band, a lockdown, a permission), so nothing is charged.
460
+ await release();
461
+ return Response.json({
462
+ error: (_ref2 = refusal == null ? void 0 : refusal.error) != null ? _ref2 : 'The pictures could not be saved.'
463
+ }, {
464
+ status: (_ref3 = refusal == null ? void 0 : refusal.status) != null ? _ref3 : 502
465
+ });
466
+ }
467
+ // Only what was stored is settled: a photo's pictures at the per-picture
468
+ // rate with the request's prompt and thinking, an illustration's tokens.
469
+ const usage = input.mode === 'photo' ? _extends({}, photoUsage, {
470
+ images: stored.length
471
+ }) : sumUsage(stored.map((entry)=>entry.picture.usage));
472
+ let signalId = null;
473
+ try {
474
+ var _gate_reservation_free;
475
+ signalId = await recordAssistCost(gate.firestore, gate.orgId, {
476
+ route: '/api/ai/media/images',
477
+ hostId: input.hostId,
478
+ model,
479
+ tier: 'entitled',
480
+ usage,
481
+ docsPaths: [],
482
+ stopReason: 'end_turn',
483
+ free: (_gate_reservation_free = gate.reservation.free) != null ? _gate_reservation_free : null,
484
+ uid: gate.uid,
485
+ kind: 'image'
486
+ });
487
+ } catch (error) {
488
+ // The pictures are in the library and were paid for by us; a meter
489
+ // that could not be written is not a reason to hide them.
490
+ console.error('ai media usage not recorded', {
491
+ orgId: gate.orgId,
492
+ error
493
+ });
494
+ }
495
+ await Promise.all(stored.map(({ mediaId, picture })=>annotateStoredImage(gate, input, mediaId, picture.alt, _extends({
496
+ model,
497
+ prompt: input.prompt,
498
+ mode: input.mode
499
+ }, style ? {
500
+ style
501
+ } : {}, {
502
+ aspectRatio,
503
+ signalId,
504
+ generatedBy: gate.uid
505
+ })).catch((error)=>console.error('ai media alt text not written', {
506
+ mediaId,
507
+ error
508
+ }))));
509
+ const warning = (_ref = refusal == null ? void 0 : refusal.error) != null ? _ref : declineReason && unmade > 0 ? declineReason : unmade > 0 && input.mode === 'illustration' ? AI_MEDIA_OURS_COPY : null;
510
+ return Response.json(_extends({
511
+ mediaIds: stored.map((entry)=>entry.mediaId),
512
+ filtered: unmade,
513
+ failed: made.length - stored.length,
514
+ // What was charged, as the meter priced it.
515
+ credits: assistCreditsFromUsd(estimateAiBilledUsd(usage, model))
516
+ }, warning ? {
517
+ warning
518
+ } : {}));
519
+ }
520
+
521
+ //# sourceMappingURL=ai-media-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/server/ai-media-image.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// lockdown-423: via libs/plugins/ai/src/lib/runtime/ai-gate.ts\n// The POST climbs `aiGateLadder`, whose lockdown rung is the verdict, and\n// every stored picture also passes the upload route's.\n\nimport { mediaFilterKeys } from '@aglyn/aglyn/app-utils/media-metadata'\nimport { MEDIA_ALT_MAX_LENGTH } from '@aglyn/aglyn/app-utils/media-alt'\nimport { aiMediaCreditsPerPicture } from '../model/ai-media-credits'\nimport { aiSvgThemePalette, isAiSvgColor } from '../model/ai-svg'\nimport { estimateAiBilledUsd } from '../providers/catalog'\nimport { AI_UPSTREAM_FAILURE_COPY, AiUpstreamError, type AiUsage } from '../providers/contract'\nimport {\n AI_IMAGE_ASPECT_RATIOS,\n AI_IMAGE_MAX_COUNT,\n AI_IMAGE_PROMPT_MAX_CHARS,\n AI_SVG_MAX_COLORS,\n AI_SVG_STYLES,\n AiImageSafetyRefusal,\n isAiImageAspectRatio,\n type AiImageAspectRatio,\n type AiImageMode,\n type AiImageProvider,\n type AiSvgStyle,\n} from '../providers/image-contract'\nimport { vertexImageProvider } from '../providers/vertex-image'\nimport { aiGateLadder, type AiGateContext } from '../runtime/ai-gate'\nimport { aiInventoryTheme } from '../runtime/site-inventory'\nimport { assistBandRefuses, assistCreditsFromUsd } from '../usage/assist-credits'\nimport {\n publicAssistQuota,\n recordAssistCost,\n releaseAssistMessage,\n} from '../usage/assist-usage'\nimport { aiMediaSvgModel, generateAiMediaSvg, type AiMediaSvgOutcome } from './ai-media-svg'\n\n/**\n * \"Create with AI\" in Media (AGL-3602): pictures from a description, stored\n * in the library that asked.\n *\n * `POST /api/ai/media/images { orgId, library, hostId?, folderId?, mode,\n * prompt, aspectRatio, count, style?, palette? }` answers\n * `{ mediaIds, filtered, failed, credits, warning? }`.\n *\n * Two modes:\n *\n * - `photo` — Google's image models on Vertex AI (`vertex-image.ts`). Off\n * unless the deployment configured them; the door answers 404 with a\n * sentence the dialog shows.\n * - `illustration` — an SVG drawn by the text provider every other AI door\n * uses (`ai-media-svg.ts`): an illustration, an icon, a tileable pattern or\n * a simple logo mark, in the site's theme colors or the person's own. No\n * new vendor, so it runs wherever text AI does.\n *\n * There is no GET: the button draws from the shell's own gates, and this door\n * decides when someone asks for a picture.\n *\n * ## A request, not a job\n *\n * Both modes answer inside the request, the way \"Save as a component with\n * AI\" does, and are metered on the same meter every door uses.\n *\n * ## The money, in order\n *\n * 1. `aiGateLadder` RESERVES before anything is spent: the `aiGenerative`\n * entitlement, `release_ai_generative`, the `ai-generate` kill switch,\n * `ai.generate` on the site, the per-account and per-address windows, the\n * Free taste's own rungs and the band.\n * 2. A workspace whose band is a wall (Free, or a hard cap) is refused before\n * the provider is called when the pictures' estimate is more than it has\n * left.\n * 3. The provider is called. A fault of ours or the provider's hands the\n * reservation back and charges nothing. A declined description charges\n * only what the decline spent, recorded as a declined request (which a\n * Free workspace is never charged for, and which its daily refusal pause\n * counts).\n * 4. Each picture is stored, and only what was stored is SETTLED, at billed\n * rates above the provider's: a photo per picture plus its prompt and\n * thinking, an illustration as the tokens that drew it. A picture held\n * back, one the library refused, and an illustration that failed its\n * safety check twice are never charged — that last one is ours.\n *\n * ## Stored the way any upload is stored\n *\n * Each picture is posted to the console's own `/api/media/upload` with the\n * caller's credential, so the scope, the uploads lockdown, the structural\n * inspection, SVG sanitizing, the quarantine list, the storage band, the CDN\n * variants and the storage counter all apply exactly as they do to a file\n * the person dropped in. This door writes no media document of its own; it\n * only adds, to the documents that route made, the alt text and the record\n * of how the picture was made.\n */\n\n/** The per-account window: a person makes a few sets a minute, not dozens. */\nconst RATE_LIMIT = { key: 'ai-media-image', limit: 6, windowMs: 60_000 }\n\n/** What a person reads when a picture failed on our side. */\nexport const AI_MEDIA_OURS_COPY =\n \"This one's on us — you weren't charged. The picture didn't come out right; try again, or describe it a little differently.\"\n\n/** What the door answers on a deployment that makes no photos. */\nexport const AI_MEDIA_NO_PHOTOS_COPY =\n \"Photos aren't available here yet. Choose Illustration to draw one instead.\"\n\n/** The provider photos come from; a spec stands one in through jest. */\nfunction imageProvider(): AiImageProvider {\n return vertexImageProvider\n}\n\n/** Every control character as a space, squeezed, trimmed and cut. */\nexport function cleanAiImagePrompt(value: unknown): string {\n let out = ''\n for (const char of String(value ?? '')) {\n const code = char.charCodeAt(0)\n out += code < 32 || code === 127 || code === 0x2028 || code === 0x2029 ? ' ' : char\n }\n return out.replace(/\\s+/g, ' ').trim().slice(0, AI_IMAGE_PROMPT_MAX_CHARS)\n}\n\n/**\n * The alt text a picture is stored with: the sentence given, as a sentence,\n * cut at a word inside the library's alt limit. A person edits it like any\n * other.\n */\nexport function aiImageAltText(text: string): string {\n const clean = text.replace(/\\s+/g, ' ').trim()\n if (!clean) return ''\n const sentence = clean.charAt(0).toUpperCase() + clean.slice(1)\n if (sentence.length <= MEDIA_ALT_MAX_LENGTH) return sentence\n const cut = sentence.slice(0, MEDIA_ALT_MAX_LENGTH - 1)\n const space = cut.lastIndexOf(' ')\n return `${(space > 40 ? cut.slice(0, space) : cut).trim()}…`\n}\n\nconst EXTENSIONS: Readonly<Record<string, string>> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/svg+xml': 'svg',\n}\n\n/** A file name from the description's first words: `ai-a-red-barn-2.png`. */\nexport function aiImageFileName(prompt: string, index: number, mimeType: string): string {\n const words = prompt\n .toLowerCase()\n .normalize('NFKD')\n .replace(/[^a-z0-9\\s-]/g, ' ')\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 6)\n .join('-')\n .slice(0, 60)\n .replace(/-+$/, '')\n return `ai-${words || 'image'}-${index + 1}.${EXTENSIONS[mimeType] ?? 'png'}`\n}\n\n/** What the library asked for, read and bounded. */\nexport interface AiMediaImageInput {\n orgId: string\n library: 'host' | 'org'\n /** The site whose library this is, or the site on screen for the org's. */\n hostId: string | null\n folderId: string | null\n mode: AiImageMode\n prompt: string\n aspectRatio: string\n count: number\n /** An illustration's kind; unread for a photo. */\n style: string\n /** An illustration's colors: the site theme's, or these. */\n palette: { source: 'theme' } | { source: 'custom'; colors: string[] }\n}\n\nexport function parseAiMediaImageInput(body: Record<string, unknown> | null): AiMediaImageInput {\n const text = (key: string, limit = 128) => {\n const value = body?.[key]\n return typeof value === 'string' ? value.trim().slice(0, limit) : ''\n }\n const count = Math.floor(Number(body?.['count'] ?? 1))\n const palette = body?.['palette'] as Record<string, unknown> | undefined\n const colors = Array.isArray(palette?.['colors']) ? (palette?.['colors'] as unknown[]) : []\n return {\n orgId: text('orgId'),\n library: body?.['library'] === 'org' ? 'org' : 'host',\n hostId: text('hostId') || null,\n folderId: text('folderId', 64) || null,\n mode: body?.['mode'] === 'illustration' ? 'illustration' : 'photo',\n prompt: cleanAiImagePrompt(body?.['prompt']),\n aspectRatio: text('aspectRatio', 8),\n count: Number.isFinite(count) ? count : 0,\n style: text('style', 24) || 'illustration',\n palette:\n palette?.['source'] === 'custom'\n ? {\n source: 'custom',\n colors: colors\n .map((color) => String(color ?? '').trim().toLowerCase())\n .slice(0, AI_SVG_MAX_COLORS + 1),\n }\n : { source: 'theme' },\n }\n}\n\n/** One stored picture, or the upload route's refusal. */\ntype StoreOutcome = { mediaId: string } | { error: string; status: number }\n\n/**\n * Posts one picture to the console's own upload route, as the caller. The\n * same body the library's Upload button sends, so the route cannot tell the\n * two apart and applies every check it applies to that one.\n */\nasync function storeThroughUploadRoute(\n request: Request,\n input: AiMediaImageInput,\n image: { base64: string; mimeType: string },\n fileName: string,\n): Promise<StoreOutcome> {\n const scope =\n input.library === 'org'\n ? { orgId: input.orgId, ...(input.hostId ? { forHostId: input.hostId } : {}) }\n : { hostId: input.hostId }\n let response: Response\n try {\n response = await fetch(new URL('/api/media/upload', request.url), {\n method: 'POST',\n headers: {\n Authorization: request.headers.get('authorization') ?? '',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n ...scope,\n fileName,\n contentType: image.mimeType,\n folderId: input.folderId,\n data: image.base64,\n }),\n })\n } catch {\n return { error: 'The picture could not be saved to the media library.', status: 502 }\n }\n const payload = (await response.json().catch(() => ({}))) as Record<string, unknown>\n const mediaId = typeof payload['mediaId'] === 'string' ? payload['mediaId'] : ''\n if (response.ok && mediaId) return { mediaId }\n return {\n error:\n typeof payload['error'] === 'string'\n ? payload['error']\n : 'The picture could not be saved to the media library.',\n status: response.ok ? 502 : response.status,\n }\n}\n\n/** The record a generated picture keeps of how it was made. */\nexport interface AiMediaProvenance {\n model: string\n prompt: string\n mode: AiImageMode\n /** An illustration's kind; absent on a photo. */\n style?: AiSvgStyle\n aspectRatio: string\n /** The meter's signal id for the request that made it. */\n signalId: string | null\n generatedBy: string\n}\n\n/**\n * Adds the alt text and the provenance to a document the upload route made.\n * An `update`, which cannot mint a document: the route minted it.\n */\nasync function annotateStoredImage(\n gate: AiGateContext,\n input: AiMediaImageInput,\n mediaId: string,\n alt: string,\n provenance: AiMediaProvenance,\n): Promise<void> {\n const scopeRef =\n input.library === 'org'\n ? gate.firestore.collection('orgs').doc(input.orgId)\n : gate.firestore.collection('hosts').doc(String(input.hostId))\n const mediaRef = scopeRef.collection('media').doc(mediaId)\n const stored = ((await mediaRef.get()).data() ?? {}) as Parameters<typeof mediaFilterKeys>[0]\n await mediaRef.update({\n alt,\n description: 'Created with AI from a description.',\n aiGenerated: { ...provenance, createdAt: new Date() },\n // What the library filters by, restamped with the alt now set: the upload\n // route wrote \"Alt text is missing\" before the alt existed.\n ...mediaFilterKeys({ ...stored, alt }),\n })\n}\n\n/** The colors an illustration is drawn in. */\nasync function illustrationPalette(\n gate: AiGateContext,\n input: AiMediaImageInput,\n): Promise<string[]> {\n if (input.palette.source === 'custom') return input.palette.colors\n if (!input.hostId) return []\n const snapshot = await gate.firestore.collection('hosts').doc(input.hostId).get()\n return aiSvgThemePalette(aiInventoryTheme(snapshot.exists ? snapshot.data() ?? null : null)?.colors)\n}\n\nconst ZERO_USAGE: AiUsage = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 }\n\nconst sumUsage = (usages: readonly AiUsage[]): AiUsage =>\n usages.reduce<AiUsage>(\n (sum, usage) => ({\n inputTokens: sum.inputTokens + usage.inputTokens,\n outputTokens: sum.outputTokens + usage.outputTokens,\n cacheReadTokens: sum.cacheReadTokens + usage.cacheReadTokens,\n cacheWriteTokens: sum.cacheWriteTokens + usage.cacheWriteTokens,\n }),\n ZERO_USAGE,\n )\n\n/** A picture made, before it is stored. */\ninterface MadePicture {\n base64: string\n mimeType: string\n alt: string\n /** What making it spent, charged only once it is stored. */\n usage: AiUsage\n}\n\nexport async function POST(request: Request): Promise<Response> {\n let body: Record<string, unknown> | null\n try {\n body = (await request.json()) as Record<string, unknown>\n } catch {\n body = null\n }\n const input = parseAiMediaImageInput(body)\n const provider = imageProvider()\n const svgModel = input.mode === 'illustration' ? aiMediaSvgModel() : undefined\n // A deployment that makes no pictures of this kind has no such door, so\n // nothing about the caller or the workspace is read first.\n if (input.mode === 'photo' && !provider.configured()) {\n return Response.json({ error: AI_MEDIA_NO_PHOTOS_COPY, reason: 'unavailable' }, { status: 404 })\n }\n if (input.mode === 'illustration' && !svgModel) {\n return Response.json({ error: 'Not found' }, { status: 404 })\n }\n\n const gate = await aiGateLadder(\n { request, orgId: input.orgId, hostId: input.hostId },\n {\n feature: 'aiGenerative',\n releaseFlag: 'release_ai_generative',\n lockdownFeature: 'ai-generate',\n permission: 'ai.generate',\n rateLimit: RATE_LIMIT,\n },\n )\n if (gate instanceof Response) return gate\n // Nothing is spent until a provider answers, so every exit before it\n // hands the reservation back.\n const release = () =>\n releaseAssistMessage(gate.firestore, gate.orgId, gate.reservation).catch(() => undefined)\n const refuse = async (error: string, status: number, extra: Record<string, unknown> = {}) => {\n await release()\n return Response.json({ error, ...extra }, { status })\n }\n\n if (input.library === 'host' && !input.hostId) {\n return refuse('Open the site whose media library the pictures go in.', 400)\n }\n if (!input.prompt) return refuse('Describe the picture you want first.', 400)\n if (!isAiImageAspectRatio(input.aspectRatio)) {\n return refuse(`Choose a shape: ${AI_IMAGE_ASPECT_RATIOS.join(', ')}.`, 400)\n }\n const aspectRatio: AiImageAspectRatio = input.aspectRatio\n if (input.count < 1 || input.count > AI_IMAGE_MAX_COUNT) {\n return refuse(`Make between 1 and ${AI_IMAGE_MAX_COUNT} pictures at a time.`, 400)\n }\n if (input.mode === 'illustration') {\n if (!(AI_SVG_STYLES as readonly string[]).includes(input.style)) {\n return refuse('Choose an illustration, an icon, a pattern or a logo mark.', 400)\n }\n if (\n input.palette.source === 'custom' &&\n (!input.palette.colors.length ||\n input.palette.colors.length > AI_SVG_MAX_COLORS ||\n !input.palette.colors.every(isAiSvgColor))\n ) {\n return refuse(`Give between 1 and ${AI_SVG_MAX_COLORS} colors as hex values, such as #1a73e8.`, 400)\n }\n }\n\n const model = input.mode === 'photo' ? provider.defaultModel() : (svgModel as string)\n const perPicture = aiMediaCreditsPerPicture(input.mode, model)\n const estimate = perPicture * input.count\n const credits = publicAssistQuota(gate.reservation).credits\n if (credits && assistBandRefuses(gate.org) && credits.remaining < estimate) {\n return refuse(\n credits.remaining < perPicture\n ? 'This workspace has used its AI credits for the month.'\n : `That needs about ${estimate} credits and this workspace has ${credits.remaining} left this month. Make fewer pictures.`,\n 429,\n { reason: 'quota', quota: publicAssistQuota(gate.reservation) },\n )\n }\n\n /** Records what a decline spent: a declined request, never a picture. */\n const recordDecline = (usage: AiUsage, declinedModel: string) =>\n recordAssistCost(gate.firestore, gate.orgId, {\n route: '/api/ai/media/images',\n hostId: input.hostId,\n model: declinedModel,\n tier: 'entitled',\n usage: { ...usage, images: 0 },\n docsPaths: [],\n stopReason: 'refusal',\n free: gate.reservation.free ?? null,\n uid: gate.uid,\n kind: 'image',\n }).catch((meterError) =>\n console.error('ai media refusal not recorded', { orgId: gate.orgId, meterError }),\n )\n\n let made: MadePicture[]\n /** Pictures asked for and not made: held back, declined or failed on our side. */\n let unmade: number\n /** Tokens a photo request spent beside its pictures, charged once any is stored. */\n let photoUsage: AiUsage = ZERO_USAGE\n let declineReason: string | null = null\n let style: AiSvgStyle | undefined\n\n if (input.mode === 'photo') {\n try {\n const result = await provider.generate({\n model,\n prompt: input.prompt,\n aspectRatio,\n count: input.count,\n })\n photoUsage = result.usage\n unmade = result.filtered\n made = result.images.map((image) => ({\n ...image,\n alt: aiImageAltText(input.prompt),\n usage: ZERO_USAGE,\n }))\n } catch (error) {\n if (error instanceof AiImageSafetyRefusal) {\n // Declined, not failed: no picture is charged, and the request is\n // recorded as a declined one so the Free taste's refusal pause\n // counts it.\n await recordDecline(ZERO_USAGE, model)\n return Response.json({ error: error.message, reason: 'safety' }, { status: 422 })\n }\n await release()\n console.error('ai photo generation failed', { orgId: gate.orgId, error })\n const status = error instanceof AiUpstreamError && error.accountProblem ? 503 : 502\n return Response.json({ error: AI_UPSTREAM_FAILURE_COPY }, { status })\n }\n } else {\n style = input.style as AiSvgStyle\n const palette = await illustrationPalette(gate, input)\n const settled = await Promise.allSettled(\n Array.from({ length: input.count }, (_unused, variant) =>\n generateAiMediaSvg({\n model,\n prompt: input.prompt,\n style: style as AiSvgStyle,\n aspectRatio,\n palette,\n variant,\n count: input.count,\n }),\n ),\n )\n const outcomes = settled.flatMap((entry) =>\n entry.status === 'fulfilled' ? [entry.value] : [],\n )\n if (!outcomes.length) {\n await release()\n console.error('ai illustration generation failed', {\n orgId: gate.orgId,\n error: (settled[0] as PromiseRejectedResult | undefined)?.reason,\n })\n return Response.json({ error: AI_UPSTREAM_FAILURE_COPY }, { status: 502 })\n }\n const drawn = outcomes.filter(\n (outcome): outcome is Extract<AiMediaSvgOutcome, { kind: 'drawn' }> => outcome.kind === 'drawn',\n )\n const declined = outcomes.filter((outcome) => outcome.kind === 'declined')\n unmade = input.count - drawn.length\n declineReason =\n (declined[0] as Extract<AiMediaSvgOutcome, { kind: 'declined' }> | undefined)?.reason ?? null\n made = drawn.map((outcome) => ({\n base64: Buffer.from(outcome.svg, 'utf8').toString('base64'),\n mimeType: 'image/svg+xml',\n alt: aiImageAltText(outcome.alt || input.prompt),\n usage: outcome.usage,\n }))\n if (!made.length) {\n if (declined.length) {\n // The model declined: what it spent is a declined request.\n await recordDecline(sumUsage(declined.map((outcome) => outcome.usage)), model)\n return Response.json({ error: declineReason, reason: 'safety' }, { status: 422 })\n }\n // Every picture failed its check twice. Ours, so nothing is charged.\n await release()\n return Response.json({ error: AI_MEDIA_OURS_COPY, reason: 'ours' }, { status: 502 })\n }\n }\n\n const stored: Array<{ mediaId: string; picture: MadePicture }> = []\n let refusal: { error: string; status: number } | null = null\n for (const [index, picture] of made.entries()) {\n const outcome = await storeThroughUploadRoute(\n request,\n input,\n picture,\n aiImageFileName(input.prompt, index, picture.mimeType),\n )\n if ('mediaId' in outcome) stored.push({ mediaId: outcome.mediaId, picture })\n else refusal ??= outcome\n }\n\n if (!stored.length) {\n // Made and not kept: the library refused every picture (its storage\n // band, a lockdown, a permission), so nothing is charged.\n await release()\n return Response.json(\n { error: refusal?.error ?? 'The pictures could not be saved.' },\n { status: refusal?.status ?? 502 },\n )\n }\n\n // Only what was stored is settled: a photo's pictures at the per-picture\n // rate with the request's prompt and thinking, an illustration's tokens.\n const usage =\n input.mode === 'photo'\n ? { ...photoUsage, images: stored.length }\n : sumUsage(stored.map((entry) => entry.picture.usage))\n let signalId: string | null = null\n try {\n signalId = await recordAssistCost(gate.firestore, gate.orgId, {\n route: '/api/ai/media/images',\n hostId: input.hostId,\n model,\n tier: 'entitled',\n usage,\n docsPaths: [],\n stopReason: 'end_turn',\n free: gate.reservation.free ?? null,\n uid: gate.uid,\n kind: 'image',\n })\n } catch (error) {\n // The pictures are in the library and were paid for by us; a meter\n // that could not be written is not a reason to hide them.\n console.error('ai media usage not recorded', { orgId: gate.orgId, error })\n }\n\n await Promise.all(\n stored.map(({ mediaId, picture }) =>\n annotateStoredImage(gate, input, mediaId, picture.alt, {\n model,\n prompt: input.prompt,\n mode: input.mode,\n ...(style ? { style } : {}),\n aspectRatio,\n signalId,\n generatedBy: gate.uid,\n }).catch((error) => console.error('ai media alt text not written', { mediaId, error })),\n ),\n )\n\n const warning =\n refusal?.error ??\n (declineReason && unmade > 0 ? declineReason : unmade > 0 && input.mode === 'illustration'\n ? AI_MEDIA_OURS_COPY\n : null)\n return Response.json({\n mediaIds: stored.map((entry) => entry.mediaId),\n filtered: unmade,\n failed: made.length - stored.length,\n // What was charged, as the meter priced it.\n credits: assistCreditsFromUsd(estimateAiBilledUsd(usage, model)),\n ...(warning ? { warning } : {}),\n })\n}\n"],"names":["mediaFilterKeys","MEDIA_ALT_MAX_LENGTH","aiMediaCreditsPerPicture","aiSvgThemePalette","isAiSvgColor","estimateAiBilledUsd","AI_UPSTREAM_FAILURE_COPY","AiUpstreamError","AI_IMAGE_ASPECT_RATIOS","AI_IMAGE_MAX_COUNT","AI_IMAGE_PROMPT_MAX_CHARS","AI_SVG_MAX_COLORS","AI_SVG_STYLES","AiImageSafetyRefusal","isAiImageAspectRatio","vertexImageProvider","aiGateLadder","aiInventoryTheme","assistBandRefuses","assistCreditsFromUsd","publicAssistQuota","recordAssistCost","releaseAssistMessage","aiMediaSvgModel","generateAiMediaSvg","RATE_LIMIT","key","limit","windowMs","AI_MEDIA_OURS_COPY","AI_MEDIA_NO_PHOTOS_COPY","imageProvider","cleanAiImagePrompt","value","out","char","String","code","charCodeAt","replace","trim","slice","aiImageAltText","text","clean","sentence","charAt","toUpperCase","length","cut","space","lastIndexOf","EXTENSIONS","aiImageFileName","prompt","index","mimeType","words","toLowerCase","normalize","split","filter","Boolean","join","parseAiMediaImageInput","body","count","Math","floor","Number","palette","colors","Array","isArray","orgId","library","hostId","folderId","mode","aspectRatio","isFinite","style","source","map","color","storeThroughUploadRoute","request","input","image","fileName","scope","forHostId","response","fetch","URL","url","method","headers","Authorization","get","JSON","stringify","contentType","data","base64","error","status","payload","json","catch","mediaId","ok","annotateStoredImage","gate","alt","provenance","scopeRef","firestore","collection","doc","mediaRef","stored","update","description","aiGenerated","createdAt","Date","illustrationPalette","snapshot","exists","ZERO_USAGE","inputTokens","outputTokens","cacheReadTokens","cacheWriteTokens","sumUsage","usages","reduce","sum","usage","POST","provider","svgModel","undefined","configured","Response","reason","feature","releaseFlag","lockdownFeature","permission","rateLimit","release","reservation","refuse","extra","includes","every","model","defaultModel","perPicture","estimate","credits","org","remaining","quota","recordDecline","declinedModel","route","tier","images","docsPaths","stopReason","free","uid","kind","meterError","console","made","unmade","photoUsage","declineReason","result","generate","filtered","message","accountProblem","declined","settled","Promise","allSettled","from","_unused","variant","outcomes","flatMap","entry","drawn","outcome","Buffer","svg","toString","refusal","picture","entries","push","signalId","all","generatedBy","warning","mediaIds","failed"],"mappings":"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"}