@aglyn/plugins-ai 1.0.0-beta.218 → 1.0.0-beta.220

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 (311) hide show
  1. package/README.md +2 -2
  2. package/package.json +18 -18
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js.map +1 -1
  5. package/src/lib/activity/ai-activity-actions.js +8 -1
  6. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  7. package/src/lib/billing/ai-month-meter.d.ts +51 -0
  8. package/src/lib/billing/ai-month-meter.js +89 -0
  9. package/src/lib/billing/ai-month-meter.js.map +1 -0
  10. package/src/lib/billing/ai-overage-close.d.ts +11 -33
  11. package/src/lib/billing/ai-overage-close.js +4 -3
  12. package/src/lib/billing/ai-overage-close.js.map +1 -1
  13. package/src/lib/billing/ai-overage-cutover.d.ts +28 -12
  14. package/src/lib/billing/ai-overage-cutover.js +1 -28
  15. package/src/lib/billing/ai-overage-cutover.js.map +1 -1
  16. package/src/lib/billing/ai-overage-ledger.js +1 -1
  17. package/src/lib/billing/ai-overage-ledger.js.map +1 -1
  18. package/src/lib/billing/ai-overage-standing.js +1 -1
  19. package/src/lib/billing/ai-overage-standing.js.map +1 -1
  20. package/src/lib/components/ai-allotment-editor.component.js +1 -1
  21. package/src/lib/components/ai-allotment-editor.component.js.map +1 -1
  22. package/src/lib/components/ai-assist-provider.component.js +5 -4
  23. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  24. package/src/lib/components/ai-automation-zones.d.ts +63 -0
  25. package/src/lib/components/ai-automation-zones.js +31 -0
  26. package/src/lib/components/ai-automation-zones.js.map +1 -0
  27. package/src/lib/components/ai-brief-dialog.component.js +3 -3
  28. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  29. package/src/lib/components/ai-describe-automation.component.d.ts +1 -1
  30. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  31. package/src/lib/components/ai-describe-button.component.d.ts +6 -3
  32. package/src/lib/components/ai-describe-button.component.js +4 -1
  33. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  34. package/src/lib/components/ai-experiment-cards.component.js +1 -1
  35. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  36. package/src/lib/components/ai-explain-automation.component.d.ts +1 -1
  37. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  38. package/src/lib/components/ai-job-plan.component.d.ts +8 -0
  39. package/src/lib/components/ai-job-plan.component.js +21 -4
  40. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  41. package/src/lib/components/ai-permissions-on-host.component.d.ts +12 -2
  42. package/src/lib/components/ai-permissions-on-host.component.js +12 -3
  43. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  44. package/src/lib/components/ai-product-copy-card.component.d.ts +1 -1
  45. package/src/lib/components/ai-product-copy-card.component.js.map +1 -1
  46. package/src/lib/components/ai-product-import-option.component.d.ts +1 -1
  47. package/src/lib/components/ai-product-import-option.component.js.map +1 -1
  48. package/src/lib/components/ai-product-zones.d.ts +124 -0
  49. package/src/lib/components/ai-product-zones.js +32 -0
  50. package/src/lib/components/ai-product-zones.js.map +1 -0
  51. package/src/lib/components/ai-products-hub-card.component.d.ts +1 -1
  52. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  53. package/src/lib/components/ai-products-proposals.component.d.ts +1 -1
  54. package/src/lib/components/ai-products-proposals.component.js.map +1 -1
  55. package/src/lib/components/ai-seo-audit-card.component.js +35 -92
  56. package/src/lib/components/ai-seo-audit-card.component.js.map +1 -1
  57. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  58. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  59. package/src/lib/components/assist-jobs-drawer.component.js +21 -15
  60. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  61. package/src/lib/components/assist-panel.component.js +2 -2
  62. package/src/lib/components/assist-panel.component.js.map +1 -1
  63. package/src/lib/components/assist-signals-page.component.js +2 -2
  64. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  65. package/src/lib/components/billing-ai-allotments.component.js +1 -1
  66. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  67. package/src/lib/components/billing-assist-overage-card.component.js +1 -1
  68. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  69. package/src/lib/constants.d.ts +5 -0
  70. package/src/lib/constants.js +4 -0
  71. package/src/lib/constants.js.map +1 -1
  72. package/src/lib/declarations.js +2 -0
  73. package/src/lib/declarations.js.map +1 -1
  74. package/src/lib/declarations.server.js +24 -10
  75. package/src/lib/declarations.server.js.map +1 -1
  76. package/src/lib/insights/ai-dataset-catalog.d.ts +49 -0
  77. package/src/lib/insights/ai-dataset-catalog.js +57 -0
  78. package/src/lib/insights/ai-dataset-catalog.js.map +1 -0
  79. package/src/lib/insights/ai-figure-readers.d.ts +6 -39
  80. package/src/lib/insights/ai-figure-readers.js +2 -391
  81. package/src/lib/insights/ai-figure-readers.js.map +1 -1
  82. package/src/lib/insights/ai-insight-digest.d.ts +10 -1
  83. package/src/lib/insights/ai-insight-digest.js +16 -4
  84. package/src/lib/insights/ai-insight-digest.js.map +1 -1
  85. package/src/lib/insights/ai-insight-readers.d.ts +1 -1
  86. package/src/lib/insights/ai-insight-readers.js.map +1 -1
  87. package/src/lib/jobs/ai-email-bindings.d.ts +28 -21
  88. package/src/lib/jobs/ai-email-bindings.js +75 -42
  89. package/src/lib/jobs/ai-email-bindings.js.map +1 -1
  90. package/src/lib/jobs/ai-job-campaign-step.d.ts +0 -2
  91. package/src/lib/jobs/ai-job-campaign-step.js +5 -10
  92. package/src/lib/jobs/ai-job-campaign-step.js.map +1 -1
  93. package/src/lib/jobs/ai-job-component-step.d.ts +6 -1
  94. package/src/lib/jobs/ai-job-component-step.js +32 -9
  95. package/src/lib/jobs/ai-job-component-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-draft-ids.js +11 -0
  97. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  98. package/src/lib/jobs/ai-job-drafts.js +1 -1
  99. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  100. package/src/lib/jobs/ai-job-email-step.d.ts +5 -3
  101. package/src/lib/jobs/ai-job-email-step.js +11 -4
  102. package/src/lib/jobs/ai-job-email-step.js.map +1 -1
  103. package/src/lib/jobs/ai-job-experiment-step.d.ts +1 -1
  104. package/src/lib/jobs/ai-job-experiment-step.js +1 -1
  105. package/src/lib/jobs/ai-job-experiment-step.js.map +1 -1
  106. package/src/lib/jobs/ai-job-form-step.d.ts +17 -1
  107. package/src/lib/jobs/ai-job-form-step.js +60 -8
  108. package/src/lib/jobs/ai-job-form-step.js.map +1 -1
  109. package/src/lib/jobs/ai-job-generation.d.ts +22 -1
  110. package/src/lib/jobs/ai-job-generation.js +63 -3
  111. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  112. package/src/lib/jobs/ai-job-insight-step.js +1 -1
  113. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  114. package/src/lib/jobs/ai-job-layout-step.d.ts +15 -4
  115. package/src/lib/jobs/ai-job-layout-step.js +46 -13
  116. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  117. package/src/lib/jobs/ai-job-page-sections.d.ts +4 -2
  118. package/src/lib/jobs/ai-job-page-sections.js +7 -4
  119. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  120. package/src/lib/jobs/ai-job-page-step.js +7 -2
  121. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  122. package/src/lib/jobs/ai-job-plan-conformance.d.ts +5 -0
  123. package/src/lib/jobs/ai-job-plan-conformance.js +40 -1
  124. package/src/lib/jobs/ai-job-plan-conformance.js.map +1 -1
  125. package/src/lib/jobs/ai-job-plan-creations.d.ts +80 -0
  126. package/src/lib/jobs/ai-job-plan-creations.js +199 -0
  127. package/src/lib/jobs/ai-job-plan-creations.js.map +1 -0
  128. package/src/lib/jobs/ai-job-plan-step.d.ts +11 -2
  129. package/src/lib/jobs/ai-job-plan-step.js +49 -23
  130. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  131. package/src/lib/jobs/ai-job-plugin-drafts.d.ts +14 -6
  132. package/src/lib/jobs/ai-job-plugin-drafts.js +12 -4
  133. package/src/lib/jobs/ai-job-plugin-drafts.js.map +1 -1
  134. package/src/lib/jobs/ai-job-products-step.d.ts +7 -0
  135. package/src/lib/jobs/ai-job-products-step.js +54 -32
  136. package/src/lib/jobs/ai-job-products-step.js.map +1 -1
  137. package/src/lib/jobs/ai-job-seo-step.d.ts +3 -24
  138. package/src/lib/jobs/ai-job-seo-step.js +35 -108
  139. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  140. package/src/lib/jobs/ai-job-site-step.d.ts +2 -0
  141. package/src/lib/jobs/ai-job-site-step.js +6 -6
  142. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  143. package/src/lib/jobs/ai-job-template-step.d.ts +9 -6
  144. package/src/lib/jobs/ai-job-template-step.js +73 -15
  145. package/src/lib/jobs/ai-job-template-step.js.map +1 -1
  146. package/src/lib/jobs/ai-job-theme-step.d.ts +1 -1
  147. package/src/lib/jobs/ai-job-theme-step.js +1 -1
  148. package/src/lib/jobs/ai-job-theme-step.js.map +1 -1
  149. package/src/lib/jobs/ai-job-workflow-step.js +17 -9
  150. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  151. package/src/lib/jobs/ai-jobs.js +1 -1
  152. package/src/lib/jobs/ai-jobs.js.map +1 -1
  153. package/src/lib/jobs/ai-workflow-records.d.ts +20 -11
  154. package/src/lib/jobs/ai-workflow-records.js +80 -43
  155. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  156. package/src/lib/model/ai-allotments.d.ts +27 -6
  157. package/src/lib/model/ai-allotments.js +54 -14
  158. package/src/lib/model/ai-allotments.js.map +1 -1
  159. package/src/lib/model/ai-automation-outline.d.ts +21 -2
  160. package/src/lib/model/ai-automation-outline.js.map +1 -1
  161. package/src/lib/model/ai-build-plan.d.ts +57 -0
  162. package/src/lib/model/ai-build-plan.js +116 -7
  163. package/src/lib/model/ai-build-plan.js.map +1 -1
  164. package/src/lib/model/ai-job-creations.d.ts +55 -0
  165. package/src/lib/model/ai-job-creations.js +85 -0
  166. package/src/lib/model/ai-job-creations.js.map +1 -0
  167. package/src/lib/model/ai-page-job.d.ts +3 -2
  168. package/src/lib/model/ai-page-job.js +4 -3
  169. package/src/lib/model/ai-page-job.js.map +1 -1
  170. package/src/lib/model/ai-seo.d.ts +11 -51
  171. package/src/lib/model/ai-seo.js +0 -17
  172. package/src/lib/model/ai-seo.js.map +1 -1
  173. package/src/lib/model/ai-site-inventory.js +1 -1
  174. package/src/lib/model/ai-site-inventory.js.map +1 -1
  175. package/src/lib/model/ai-site-job.d.ts +4 -2
  176. package/src/lib/model/ai-site-job.js +8 -4
  177. package/src/lib/model/ai-site-job.js.map +1 -1
  178. package/src/lib/model/ai-template-subjects.d.ts +10 -0
  179. package/src/lib/model/ai-template-subjects.js +16 -2
  180. package/src/lib/model/ai-template-subjects.js.map +1 -1
  181. package/src/lib/model/ai-theme-proposal.d.ts +1 -1
  182. package/src/lib/model/ai-theme-proposal.js +1 -1
  183. package/src/lib/model/ai-theme-proposal.js.map +1 -1
  184. package/src/lib/model/ai-tokens.js +1 -1
  185. package/src/lib/model/ai-tokens.js.map +1 -1
  186. package/src/lib/model/ai-usage-by-user.js +1 -1
  187. package/src/lib/model/ai-usage-by-user.js.map +1 -1
  188. package/src/lib/model/ai-workflow-job.d.ts +1 -3
  189. package/src/lib/model/ai-workflow-job.js +1 -2
  190. package/src/lib/model/ai-workflow-job.js.map +1 -1
  191. package/src/lib/plan-entitlements.d.ts +149 -0
  192. package/src/lib/plan-entitlements.js +168 -0
  193. package/src/lib/plan-entitlements.js.map +1 -0
  194. package/src/lib/plugin.js +1 -1
  195. package/src/lib/plugin.js.map +1 -1
  196. package/src/lib/providers/catalog.js +1 -1
  197. package/src/lib/providers/catalog.js.map +1 -1
  198. package/src/lib/providers/model-choice.js +1 -1
  199. package/src/lib/providers/model-choice.js.map +1 -1
  200. package/src/lib/runtime/ai-device-audit.d.ts +1 -1
  201. package/src/lib/runtime/ai-device-audit.js +1 -1
  202. package/src/lib/runtime/ai-device-audit.js.map +1 -1
  203. package/src/lib/runtime/ai-doctrine-validators.d.ts +25 -4
  204. package/src/lib/runtime/ai-doctrine-validators.js +178 -21
  205. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  206. package/src/lib/runtime/ai-doctrine.js +5 -5
  207. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  208. package/src/lib/runtime/ai-eval-live.js +30 -15
  209. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  210. package/src/lib/runtime/ai-eval.d.ts +9 -2
  211. package/src/lib/runtime/ai-eval.js +29 -2
  212. package/src/lib/runtime/ai-eval.js.map +1 -1
  213. package/src/lib/runtime/ai-gate.js +1 -1
  214. package/src/lib/runtime/ai-gate.js.map +1 -1
  215. package/src/lib/runtime/ai-node-tree.js +3 -3
  216. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  217. package/src/lib/runtime/ai-palette.d.ts +7 -1
  218. package/src/lib/runtime/ai-palette.generated.d.ts +1 -1
  219. package/src/lib/runtime/ai-palette.generated.js +16 -13
  220. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  221. package/src/lib/runtime/ai-palette.js +7 -1
  222. package/src/lib/runtime/ai-palette.js.map +1 -1
  223. package/src/lib/runtime/seo-audit.d.ts +13 -63
  224. package/src/lib/runtime/seo-audit.js +22 -207
  225. package/src/lib/runtime/seo-audit.js.map +1 -1
  226. package/src/lib/runtime/site-inventory.js +29 -23
  227. package/src/lib/runtime/site-inventory.js.map +1 -1
  228. package/src/lib/server/ai-admin-org.js +1 -1
  229. package/src/lib/server/ai-admin-org.js.map +1 -1
  230. package/src/lib/server/ai-admin-orgs-spend.js +1 -1
  231. package/src/lib/server/ai-admin-orgs-spend.js.map +1 -1
  232. package/src/lib/server/ai-admin-signals.js +1 -1
  233. package/src/lib/server/ai-admin-signals.js.map +1 -1
  234. package/src/lib/server/ai-allotments.js +1 -1
  235. package/src/lib/server/ai-allotments.js.map +1 -1
  236. package/src/lib/server/ai-assist-prompts.js +3 -3
  237. package/src/lib/server/ai-assist-prompts.js.map +1 -1
  238. package/src/lib/server/ai-assist.js +2 -2
  239. package/src/lib/server/ai-assist.js.map +1 -1
  240. package/src/lib/server/assist-chat.d.ts +1 -1
  241. package/src/lib/server/assist-chat.js +13 -12
  242. package/src/lib/server/assist-chat.js.map +1 -1
  243. package/src/lib/server/assist-view-context.js +23 -23
  244. package/src/lib/server/assist-view-context.js.map +1 -1
  245. package/src/lib/server/billing-credits.d.ts +5 -0
  246. package/src/lib/server/billing-credits.js +8 -2
  247. package/src/lib/server/billing-credits.js.map +1 -1
  248. package/src/lib/server/billing-overage.js +2 -2
  249. package/src/lib/server/billing-overage.js.map +1 -1
  250. package/src/lib/server.d.ts +1 -0
  251. package/src/lib/server.js.map +1 -1
  252. package/src/lib/testing/stand-in-automation-index.d.ts +20 -0
  253. package/src/lib/testing/stand-in-automation-index.js +102 -0
  254. package/src/lib/testing/stand-in-automation-index.js.map +1 -0
  255. package/src/lib/testing/stand-in-dataset-index.d.ts +20 -0
  256. package/src/lib/testing/stand-in-dataset-index.js +80 -0
  257. package/src/lib/testing/stand-in-dataset-index.js.map +1 -0
  258. package/src/lib/testing/stand-in-list-send-time.d.ts +4 -0
  259. package/src/lib/testing/stand-in-list-send-time.js +77 -0
  260. package/src/lib/testing/stand-in-list-send-time.js.map +1 -0
  261. package/src/lib/testing/stand-in-pipeline-index.d.ts +32 -0
  262. package/src/lib/testing/stand-in-pipeline-index.js +59 -0
  263. package/src/lib/testing/stand-in-pipeline-index.js.map +1 -0
  264. package/src/lib/testing/stand-in-product-index.d.ts +20 -0
  265. package/src/lib/testing/stand-in-product-index.js +105 -0
  266. package/src/lib/testing/stand-in-product-index.js.map +1 -0
  267. package/src/lib/tools/ai-component-tool.js +2 -2
  268. package/src/lib/tools/ai-component-tool.js.map +1 -1
  269. package/src/lib/tools/ai-duplicate-resource-tool.js +1 -1
  270. package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
  271. package/src/lib/tools/ai-seo-tool.d.ts +6 -18
  272. package/src/lib/tools/ai-seo-tool.js +7 -38
  273. package/src/lib/tools/ai-seo-tool.js.map +1 -1
  274. package/src/lib/tools/ai-workflow-tool.d.ts +1 -1
  275. package/src/lib/tools/ai-workflow-tool.js +1 -1
  276. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  277. package/src/lib/usage/ai-allotment-alerts.d.ts +10 -7
  278. package/src/lib/usage/ai-allotment-alerts.js +39 -15
  279. package/src/lib/usage/ai-allotment-alerts.js.map +1 -1
  280. package/src/lib/usage/ai-quota-checks.d.ts +37 -0
  281. package/src/lib/usage/ai-quota-checks.js +69 -0
  282. package/src/lib/usage/ai-quota-checks.js.map +1 -0
  283. package/src/lib/usage/ai-usage-alerts.js +22 -10
  284. package/src/lib/usage/ai-usage-alerts.js.map +1 -1
  285. package/src/lib/usage/ai-usage-by-user.js +1 -1
  286. package/src/lib/usage/ai-usage-by-user.js.map +1 -1
  287. package/src/lib/usage/assist-ceiling.d.ts +2 -2
  288. package/src/lib/usage/assist-ceiling.js +4 -4
  289. package/src/lib/usage/assist-ceiling.js.map +1 -1
  290. package/src/lib/usage/assist-credits.d.ts +494 -0
  291. package/src/lib/usage/assist-credits.js +524 -0
  292. package/src/lib/usage/assist-credits.js.map +1 -0
  293. package/src/lib/usage/assist-free-taste.d.ts +1 -1
  294. package/src/lib/usage/assist-free-taste.js +3 -2
  295. package/src/lib/usage/assist-free-taste.js.map +1 -1
  296. package/src/lib/usage/assist-signal-mining.js +1 -1
  297. package/src/lib/usage/assist-signal-mining.js.map +1 -1
  298. package/src/lib/usage/assist-spend-guards.d.ts +153 -0
  299. package/src/lib/usage/assist-spend-guards.js +182 -0
  300. package/src/lib/usage/assist-spend-guards.js.map +1 -0
  301. package/src/lib/usage/assist-usage.d.ts +1 -1
  302. package/src/lib/usage/assist-usage.js +1 -1
  303. package/src/lib/usage/assist-usage.js.map +1 -1
  304. package/src/lib/usage/staff-org-ai.js +1 -1
  305. package/src/lib/usage/staff-org-ai.js.map +1 -1
  306. package/src/lib/usage-axes.d.ts +49 -0
  307. package/src/lib/usage-axes.js +108 -0
  308. package/src/lib/usage-axes.js.map +1 -0
  309. package/src/lib/runtime/seo-page-facts.d.ts +0 -61
  310. package/src/lib/runtime/seo-page-facts.js +0 -150
  311. package/src/lib/runtime/seo-page-facts.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-usage-by-user.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\nimport type { AiJobKind } from './ai-jobs.types'\nimport { assistCreditsFromUsd } from '@aglyn/aglyn/app-utils/assist-credits'\n\n/**\n * PER-USER AI USAGE (AGL-2928): the pure half.\n *\n * The org rollup at `assistUsage/{month}` says what a workspace spent on AI\n * and nothing about who spent it. This module is the shape and the arithmetic\n * of the second rollup — one document per person per month, under the org,\n * keyed by uid — so an agency or a multi-brand workspace can see which of its\n * people, on which site, is generating what. No prose, no PII beyond the uid\n * the document is keyed by.\n *\n * The write lives in the plugin's `usage/ai-usage-by-user.ts`, inside the\n * same batch as the org rollup. Everything here is dependency-free so the\n * plugin's tables and the doors that serve them read one definition of a\n * share, a month key and a retention window.\n */\n\n/** `orgs/{orgId}/{this}/{uid}/{months}/{YYYY-MM}` — the subcollection names. */\nexport const AI_USAGE_BY_USER_COLLECTION = 'aiUsageByUser'\nexport const AI_USAGE_BY_USER_MONTHS_COLLECTION = 'months'\n\n/**\n * How many months a person's usage is kept, counted from the month it\n * describes. Thirteen: a full year of month-over-month comparison for a\n * workspace admin, plus the month in progress. A TTL policy on the month\n * document's `expiresAt` enforces it — see `aiUsageByUserExpiry`.\n */\nexport const AI_USAGE_BY_USER_RETENTION_MONTHS = 13\n\n/**\n * What an AI request was FOR, as the per-user rollup buckets it.\n *\n * `assist` is a console chat turn; `element`, `blog` and `section` are the\n * besigner's three assist modes; the rest are generation job kinds, named\n * exactly as the job document names them so a reader can join the two — or\n * a purpose another plugin named on the text-generation seam.\n */\nexport type AiUsageKind =\n | 'assist'\n | 'element'\n | 'blog'\n | 'section'\n | AiJobKind\n | PluginAiUsageKind\n\n/**\n * A purpose another plugin named on the core's text-generation seam\n * (AGL-3324): the plugin's id and a word, hyphenated — `outreach-curate` —\n * so its spend sits in the same `byKind` map as a job kind and a reader can\n * tell whose it was.\n */\nexport type PluginAiUsageKind = `${string}-${string}`\n\n/** One person's month, zero-filled — the shape every reader hands on. */\nexport interface AiUsageByUserMonth {\n uid: string\n month: string\n /** Credits drawn, each request rounded up as the meter rounds. */\n credits: number\n /**\n * The measured provider spend behind `credits`. Server-side only: no\n * customer surface renders it, and `aiUsageShare` is how it becomes a\n * figure a customer may see.\n */\n estCostUsd: number\n requests: number\n refusals: number\n /** Credits by `AiUsageKind`. */\n byKind: Record<string, number>\n /** Credits by host id, for requests that named a site. */\n byHost: Record<string, number>\n}\n\nconst MONTH_KEY = /^\\d{4}-(0[1-9]|1[0-2])$/\n\n/** Whether `value` is a `YYYY-MM` month key. */\nexport function isAiUsageMonthKey(value: unknown): value is string {\n return typeof value === 'string' && MONTH_KEY.test(value)\n}\n\n/**\n * The month keys a reader may ask for, newest first: the month in progress\n * and the twelve before it — the retention window, so a picker built from\n * this list never names a month the TTL has reaped.\n */\nexport function aiUsageMonthKeys(\n now: Date = new Date(),\n count: number = AI_USAGE_BY_USER_RETENTION_MONTHS,\n): string[] {\n const keys: string[] = []\n for (let back = 0; back < count; back += 1) {\n keys.push(\n new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - back, 1))\n .toISOString()\n .slice(0, 7),\n )\n }\n return keys\n}\n\n/** Whether `month` is inside the window `aiUsageMonthKeys` describes. */\nexport function aiUsageMonthWithinRetention(\n month: string,\n now: Date = new Date(),\n): boolean {\n return isAiUsageMonthKey(month) && aiUsageMonthKeys(now).includes(month)\n}\n\n/**\n * When a month document expires: the first instant of the month that is\n * `AI_USAGE_BY_USER_RETENTION_MONTHS` after the one it describes has closed.\n *\n * A `Date`, not a number: the TTL policy keys on a Firestore Timestamp and\n * silently governs nothing when the field is a number. Deterministic per\n * month, so every increment into the document re-stamps the same value and\n * the policy never moves under a month that is still being written.\n */\nexport function aiUsageByUserExpiry(month: string): Date {\n if (!isAiUsageMonthKey(month)) {\n throw new Error(`not a month key: ${month}`)\n }\n const [year, monthOfYear] = month.split('-').map(Number)\n return new Date(\n Date.UTC(year, monthOfYear + AI_USAGE_BY_USER_RETENTION_MONTHS, 1),\n )\n}\n\n/**\n * The kind an assist request was, read off the route it arrived on — for the\n * meters that carry a route and no kind of their own. The three besigner\n * modes name themselves in the path; everything else is a chat turn.\n */\nexport function aiUsageKindFromRoute(route: string | null | undefined): AiUsageKind {\n const tail = String(route ?? '').split('/').filter(Boolean).pop() ?? ''\n if (tail === 'element' || tail === 'blog' || tail === 'section') return tail\n return 'assist'\n}\n\n/**\n * One person's share of the workspace's spend for the month, in `[0, 1]`.\n *\n * Measured on the dollar figures, never on credits: the org meter rounds the\n * month's spend up ONCE, while a person's credits round each request up, so\n * a roster's credits can sum to a little more than the pool they drew from.\n * The ratio of measured spends has no such drift. Clamped, because a month\n * document written a moment after the org rollup is still a fraction, not a\n * percentage over one hundred — and rounded to a basis point, which is finer\n * than any surface renders and coarser than floating-point noise.\n */\nexport function aiUsageShare(\n userEstCostUsd: number,\n orgEstCostUsd: number,\n): number {\n const user = Number(userEstCostUsd)\n const org = Number(orgEstCostUsd)\n if (!Number.isFinite(user) || user <= 0) return 0\n if (!Number.isFinite(org) || org <= 0) return 0\n return Math.min(1, Math.round((user / org) * 10_000) / 10_000)\n}\n\nconst finite = (value: unknown): number => {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 0\n}\n\nconst creditMap = (raw: unknown): Record<string, number> => {\n const out: Record<string, number> = {}\n for (const [key, value] of Object.entries(\n (raw ?? {}) as Record<string, unknown>,\n )) {\n const credits = Math.floor(finite(value))\n if (credits > 0) out[key] = credits\n }\n return out\n}\n\n/**\n * A month document as a complete record. Absent and malformed fields read as\n * zero; a `credits` the writer never set is derived from the spend, so a\n * document written before the field existed still ranks.\n */\nexport function aiUsageByUserMonthFrom(\n raw: Record<string, unknown> | null | undefined,\n uid: string,\n month: string,\n): AiUsageByUserMonth {\n const estCostUsd = finite(raw?.['estCostUsd'])\n const stored = Math.floor(finite(raw?.['credits']))\n return {\n uid,\n month,\n credits: stored > 0 ? stored : assistCreditsFromUsd(estCostUsd),\n estCostUsd,\n requests: Math.floor(finite(raw?.['requests'])),\n refusals: Math.floor(finite(raw?.['refusals'])),\n byKind: creditMap(raw?.['byKind']),\n byHost: creditMap(raw?.['byHost']),\n }\n}\n\n/** The kinds a person drew the most credits on, dearest first. */\nexport function topAiUsageKinds(\n byKind: Record<string, number>,\n limit = 3,\n): Array<{ kind: string; credits: number }> {\n return Object.entries(byKind)\n .map(([kind, credits]) => ({ kind, credits }))\n .sort((a, b) => b.credits - a.credits || a.kind.localeCompare(b.kind))\n .slice(0, limit)\n}\n\n/** Two month documents for one person, summed — the roster column's figure. */\nexport function sumAiUsageCredits(months: readonly AiUsageByUserMonth[]): number {\n return months.reduce((sum, entry) => sum + entry.credits, 0)\n}\n"],"names":["assistCreditsFromUsd","AI_USAGE_BY_USER_COLLECTION","AI_USAGE_BY_USER_MONTHS_COLLECTION","AI_USAGE_BY_USER_RETENTION_MONTHS","MONTH_KEY","isAiUsageMonthKey","value","test","aiUsageMonthKeys","now","Date","count","keys","back","push","UTC","getUTCFullYear","getUTCMonth","toISOString","slice","aiUsageMonthWithinRetention","month","includes","aiUsageByUserExpiry","Error","year","monthOfYear","split","map","Number","aiUsageKindFromRoute","route","String","tail","filter","Boolean","pop","aiUsageShare","userEstCostUsd","orgEstCostUsd","user","org","isFinite","Math","min","round","finite","parsed","creditMap","raw","out","key","Object","entries","credits","floor","aiUsageByUserMonthFrom","uid","estCostUsd","stored","requests","refusals","byKind","byHost","topAiUsageKinds","limit","kind","sort","a","b","localeCompare","sumAiUsageCredits","months","reduce","sum","entry"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-usage-by-user.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\nimport type { AiJobKind } from './ai-jobs.types'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\n\n/**\n * PER-USER AI USAGE (AGL-2928): the pure half.\n *\n * The org rollup at `assistUsage/{month}` says what a workspace spent on AI\n * and nothing about who spent it. This module is the shape and the arithmetic\n * of the second rollup — one document per person per month, under the org,\n * keyed by uid — so an agency or a multi-brand workspace can see which of its\n * people, on which site, is generating what. No prose, no PII beyond the uid\n * the document is keyed by.\n *\n * The write lives in the plugin's `usage/ai-usage-by-user.ts`, inside the\n * same batch as the org rollup. Everything here is dependency-free so the\n * plugin's tables and the doors that serve them read one definition of a\n * share, a month key and a retention window.\n */\n\n/** `orgs/{orgId}/{this}/{uid}/{months}/{YYYY-MM}` — the subcollection names. */\nexport const AI_USAGE_BY_USER_COLLECTION = 'aiUsageByUser'\nexport const AI_USAGE_BY_USER_MONTHS_COLLECTION = 'months'\n\n/**\n * How many months a person's usage is kept, counted from the month it\n * describes. Thirteen: a full year of month-over-month comparison for a\n * workspace admin, plus the month in progress. A TTL policy on the month\n * document's `expiresAt` enforces it — see `aiUsageByUserExpiry`.\n */\nexport const AI_USAGE_BY_USER_RETENTION_MONTHS = 13\n\n/**\n * What an AI request was FOR, as the per-user rollup buckets it.\n *\n * `assist` is a console chat turn; `element`, `blog` and `section` are the\n * besigner's three assist modes; the rest are generation job kinds, named\n * exactly as the job document names them so a reader can join the two — or\n * a purpose another plugin named on the text-generation seam.\n */\nexport type AiUsageKind =\n | 'assist'\n | 'element'\n | 'blog'\n | 'section'\n | AiJobKind\n | PluginAiUsageKind\n\n/**\n * A purpose another plugin named on the core's text-generation seam\n * (AGL-3324): the plugin's id and a word, hyphenated — `outreach-curate` —\n * so its spend sits in the same `byKind` map as a job kind and a reader can\n * tell whose it was.\n */\nexport type PluginAiUsageKind = `${string}-${string}`\n\n/** One person's month, zero-filled — the shape every reader hands on. */\nexport interface AiUsageByUserMonth {\n uid: string\n month: string\n /** Credits drawn, each request rounded up as the meter rounds. */\n credits: number\n /**\n * The measured provider spend behind `credits`. Server-side only: no\n * customer surface renders it, and `aiUsageShare` is how it becomes a\n * figure a customer may see.\n */\n estCostUsd: number\n requests: number\n refusals: number\n /** Credits by `AiUsageKind`. */\n byKind: Record<string, number>\n /** Credits by host id, for requests that named a site. */\n byHost: Record<string, number>\n}\n\nconst MONTH_KEY = /^\\d{4}-(0[1-9]|1[0-2])$/\n\n/** Whether `value` is a `YYYY-MM` month key. */\nexport function isAiUsageMonthKey(value: unknown): value is string {\n return typeof value === 'string' && MONTH_KEY.test(value)\n}\n\n/**\n * The month keys a reader may ask for, newest first: the month in progress\n * and the twelve before it — the retention window, so a picker built from\n * this list never names a month the TTL has reaped.\n */\nexport function aiUsageMonthKeys(\n now: Date = new Date(),\n count: number = AI_USAGE_BY_USER_RETENTION_MONTHS,\n): string[] {\n const keys: string[] = []\n for (let back = 0; back < count; back += 1) {\n keys.push(\n new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - back, 1))\n .toISOString()\n .slice(0, 7),\n )\n }\n return keys\n}\n\n/** Whether `month` is inside the window `aiUsageMonthKeys` describes. */\nexport function aiUsageMonthWithinRetention(\n month: string,\n now: Date = new Date(),\n): boolean {\n return isAiUsageMonthKey(month) && aiUsageMonthKeys(now).includes(month)\n}\n\n/**\n * When a month document expires: the first instant of the month that is\n * `AI_USAGE_BY_USER_RETENTION_MONTHS` after the one it describes has closed.\n *\n * A `Date`, not a number: the TTL policy keys on a Firestore Timestamp and\n * silently governs nothing when the field is a number. Deterministic per\n * month, so every increment into the document re-stamps the same value and\n * the policy never moves under a month that is still being written.\n */\nexport function aiUsageByUserExpiry(month: string): Date {\n if (!isAiUsageMonthKey(month)) {\n throw new Error(`not a month key: ${month}`)\n }\n const [year, monthOfYear] = month.split('-').map(Number)\n return new Date(\n Date.UTC(year, monthOfYear + AI_USAGE_BY_USER_RETENTION_MONTHS, 1),\n )\n}\n\n/**\n * The kind an assist request was, read off the route it arrived on — for the\n * meters that carry a route and no kind of their own. The three besigner\n * modes name themselves in the path; everything else is a chat turn.\n */\nexport function aiUsageKindFromRoute(route: string | null | undefined): AiUsageKind {\n const tail = String(route ?? '').split('/').filter(Boolean).pop() ?? ''\n if (tail === 'element' || tail === 'blog' || tail === 'section') return tail\n return 'assist'\n}\n\n/**\n * One person's share of the workspace's spend for the month, in `[0, 1]`.\n *\n * Measured on the dollar figures, never on credits: the org meter rounds the\n * month's spend up ONCE, while a person's credits round each request up, so\n * a roster's credits can sum to a little more than the pool they drew from.\n * The ratio of measured spends has no such drift. Clamped, because a month\n * document written a moment after the org rollup is still a fraction, not a\n * percentage over one hundred — and rounded to a basis point, which is finer\n * than any surface renders and coarser than floating-point noise.\n */\nexport function aiUsageShare(\n userEstCostUsd: number,\n orgEstCostUsd: number,\n): number {\n const user = Number(userEstCostUsd)\n const org = Number(orgEstCostUsd)\n if (!Number.isFinite(user) || user <= 0) return 0\n if (!Number.isFinite(org) || org <= 0) return 0\n return Math.min(1, Math.round((user / org) * 10_000) / 10_000)\n}\n\nconst finite = (value: unknown): number => {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 0\n}\n\nconst creditMap = (raw: unknown): Record<string, number> => {\n const out: Record<string, number> = {}\n for (const [key, value] of Object.entries(\n (raw ?? {}) as Record<string, unknown>,\n )) {\n const credits = Math.floor(finite(value))\n if (credits > 0) out[key] = credits\n }\n return out\n}\n\n/**\n * A month document as a complete record. Absent and malformed fields read as\n * zero; a `credits` the writer never set is derived from the spend, so a\n * document written before the field existed still ranks.\n */\nexport function aiUsageByUserMonthFrom(\n raw: Record<string, unknown> | null | undefined,\n uid: string,\n month: string,\n): AiUsageByUserMonth {\n const estCostUsd = finite(raw?.['estCostUsd'])\n const stored = Math.floor(finite(raw?.['credits']))\n return {\n uid,\n month,\n credits: stored > 0 ? stored : assistCreditsFromUsd(estCostUsd),\n estCostUsd,\n requests: Math.floor(finite(raw?.['requests'])),\n refusals: Math.floor(finite(raw?.['refusals'])),\n byKind: creditMap(raw?.['byKind']),\n byHost: creditMap(raw?.['byHost']),\n }\n}\n\n/** The kinds a person drew the most credits on, dearest first. */\nexport function topAiUsageKinds(\n byKind: Record<string, number>,\n limit = 3,\n): Array<{ kind: string; credits: number }> {\n return Object.entries(byKind)\n .map(([kind, credits]) => ({ kind, credits }))\n .sort((a, b) => b.credits - a.credits || a.kind.localeCompare(b.kind))\n .slice(0, limit)\n}\n\n/** Two month documents for one person, summed — the roster column's figure. */\nexport function sumAiUsageCredits(months: readonly AiUsageByUserMonth[]): number {\n return months.reduce((sum, entry) => sum + entry.credits, 0)\n}\n"],"names":["assistCreditsFromUsd","AI_USAGE_BY_USER_COLLECTION","AI_USAGE_BY_USER_MONTHS_COLLECTION","AI_USAGE_BY_USER_RETENTION_MONTHS","MONTH_KEY","isAiUsageMonthKey","value","test","aiUsageMonthKeys","now","Date","count","keys","back","push","UTC","getUTCFullYear","getUTCMonth","toISOString","slice","aiUsageMonthWithinRetention","month","includes","aiUsageByUserExpiry","Error","year","monthOfYear","split","map","Number","aiUsageKindFromRoute","route","String","tail","filter","Boolean","pop","aiUsageShare","userEstCostUsd","orgEstCostUsd","user","org","isFinite","Math","min","round","finite","parsed","creditMap","raw","out","key","Object","entries","credits","floor","aiUsageByUserMonthFrom","uid","estCostUsd","stored","requests","refusals","byKind","byHost","topAiUsageKinds","limit","kind","sort","a","b","localeCompare","sumAiUsageCredits","months","reduce","sum","entry"],"mappings":"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"}
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { type HostEventType } from '@aglyn/aglyn/app-utils/workflows';
17
+ import { type HostEventType } from '@aglyn/aglyn/app-utils/host-events';
18
18
  import type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types';
19
19
  /**
20
20
  * A `workflow` job (AGL-2919): an automation drafted from a description, or an
@@ -97,8 +97,6 @@ export type AiAutomationUnsupported = (typeof AI_AUTOMATION_UNSUPPORTED)[number]
97
97
  /** The need an unsupported reason names, for the reasons that name one. */
98
98
  export declare const AI_AUTOMATION_UNSUPPORTED_NEED: Readonly<Partial<Record<AiAutomationUnsupported, AiAutomationNeed>>>;
99
99
  export declare const AI_AUTOMATION_UNSUPPORTED_COPY: Readonly<Record<AiAutomationUnsupported, string>>;
100
- /** The id the workflows plugin is registered under. */
101
- export declare const AI_WORKFLOWS_PLUGIN_ID = "workflows";
102
100
  /** The resource the workflows plugin writes a drafted automation under. */
103
101
  export declare const AI_AUTOMATION_RESOURCE = "automation";
104
102
  export declare const AI_WORKFLOW_NO_SITE_COPY = "This automation job names a site this workspace does not have.";
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */ import { _ as _extends } from "@swc/helpers/_/_extends";
17
17
  import { checkEntitlement } from "@aglyn/aglyn/app-utils/plan-entitlements";
18
- import { HOST_EVENT_LABELS, HOST_EVENT_TYPES } from "@aglyn/aglyn/app-utils/workflows";
18
+ import { HOST_EVENT_LABELS, HOST_EVENT_TYPES } from "@aglyn/aglyn/app-utils/host-events";
19
19
  /**
20
20
  * A `workflow` job (AGL-2919): an automation drafted from a description, or an
21
21
  * existing automation explained — what it does, or why one of its runs failed.
@@ -169,7 +169,6 @@ export const AI_AUTOMATION_UNSUPPORTED_COPY = {
169
169
  'needs-bookings': 'This automation needs bookings, which this workspace’s plan does not include, so nothing was drafted.'
170
170
  };
171
171
  // ── What a job tells a person ─────────────────────────────────────────────
172
- /** The id the workflows plugin is registered under. */ export const AI_WORKFLOWS_PLUGIN_ID = 'workflows';
173
172
  /** The resource the workflows plugin writes a drafted automation under. */ export const AI_AUTOMATION_RESOURCE = 'automation';
174
173
  export const AI_WORKFLOW_NO_SITE_COPY = 'This automation job names a site this workspace does not have.';
175
174
  export const AI_WORKFLOW_UNAVAILABLE_COPY = 'Automations are not available on this site.';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-workflow-job.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\nimport type { HostActionStepType } from '@aglyn/aglyn/app-utils/actions'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n HOST_EVENT_LABELS,\n HOST_EVENT_TYPES,\n type HostEventType,\n} from '@aglyn/aglyn/app-utils/workflows'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\n\n/**\n * A `workflow` job (AGL-2919): an automation drafted from a description, or an\n * existing automation explained — what it does, or why one of its runs failed.\n *\n * One kind, three modes, because the three share everything a job carries:\n * the site, the gates, the meter and the automation vocabulary. `inputs.mode`\n * picks the mode, and a job created without one drafts.\n *\n * - `draft` writes a new automation to the site's Actions through the\n * workflows plugin's draft writer, OFF, built only from the triggers and\n * steps below that this workspace can run. What the description names that\n * the site does not have is left as a placeholder, never guessed.\n * - `explain` reads a saved automation — an action or a workflow — and\n * answers in plain words what it does.\n * - `diagnose` reads a saved automation and one of its FAILED runs, as the\n * run history recorded it, and answers why it failed and what to change.\n *\n * Nothing in any mode switches an automation on, runs one, or changes one.\n */\n\nexport const AI_WORKFLOW_JOB_MODES = ['draft', 'explain', 'diagnose'] as const\nexport type AiWorkflowJobMode = (typeof AI_WORKFLOW_JOB_MODES)[number]\n\n/** What an explained automation is: an action, or a workflow of function calls. */\nexport const AI_WORKFLOW_TARGET_TYPES = ['action', 'workflow'] as const\nexport type AiWorkflowTargetType = (typeof AI_WORKFLOW_TARGET_TYPES)[number]\n\nexport type AiWorkflowJobInputs =\n | { mode: 'draft' }\n | { mode: 'explain'; targetType: AiWorkflowTargetType; targetId: string }\n | { mode: 'diagnose'; targetType: AiWorkflowTargetType; targetId: string; runId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiWorkflowJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiWorkflowJobInputs | string {\n const mode = inputs?.['mode'] ?? 'draft'\n if (!(AI_WORKFLOW_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be draft, explain or diagnose'\n }\n if (mode === 'draft') return { mode }\n const targetType = inputs?.['targetType']\n const targetId = inputs?.['targetId']\n if (\n !(AI_WORKFLOW_TARGET_TYPES as readonly unknown[]).includes(targetType) ||\n typeof targetId !== 'string' ||\n !DOCUMENT_ID.test(targetId)\n ) {\n return 'Pick the automation to explain'\n }\n const target = { targetType: targetType as AiWorkflowTargetType, targetId }\n if (mode === 'explain') return { mode, ...target }\n const runId = inputs?.['runId']\n if (typeof runId !== 'string' || !DOCUMENT_ID.test(runId)) return 'Pick the run to explain'\n return { mode: 'diagnose', ...target, runId }\n}\n\n// ── What a workspace can run ──────────────────────────────────────────────\n\n/** A part of the platform some triggers and steps need beyond the actions builder itself. */\nexport type AiAutomationNeed = 'crm' | 'webhooks' | 'bookings'\n\nexport type AiAutomationCapabilities = Readonly<Record<AiAutomationNeed, boolean>>\n\n/** Each need as a sentence names it. */\nexport const AI_AUTOMATION_NEED_LABELS: Readonly<Record<AiAutomationNeed, string>> = {\n crm: 'the CRM',\n webhooks: 'webhooks',\n bookings: 'bookings',\n}\n\n/**\n * What this workspace can run, from the entitlements the executor itself\n * reads before it runs a step: `crm` for the CRM steps, `webhooks` for a\n * webhook post, and `bookings` for the booking trigger's door.\n */\nexport function aiAutomationCapabilities(\n org: Partial<AglynOrgBilling> | Readonly<Record<string, unknown>> | null | undefined,\n): AiAutomationCapabilities {\n const billing = org as Partial<AglynOrgBilling> | null | undefined\n return {\n crm: checkEntitlement(billing, 'crm'),\n webhooks: checkEntitlement(billing, 'webhooks'),\n bookings: checkEntitlement(billing, 'bookings'),\n }\n}\n\n// ── The vocabulary ────────────────────────────────────────────────────────\n\n/**\n * The triggers a drafted automation may start on: every host event, which is\n * every event a server door emits. The on-page events (a click, a scroll) are\n * left out: they watch one element of one page, which a description cannot\n * name, and they belong to the page's own interactions.\n */\nexport const AI_AUTOMATION_TRIGGERS: readonly HostEventType[] = HOST_EVENT_TYPES\n\n/** The triggers only a door of a part of the platform emits. */\nexport const AI_AUTOMATION_TRIGGER_NEEDS: Readonly<Partial<Record<HostEventType, AiAutomationNeed>>> = {\n booking: 'bookings',\n taskCompleted: 'crm',\n contactStageChanged: 'crm',\n dealStageChanged: 'crm',\n dealWon: 'crm',\n dealLost: 'crm',\n}\n\n/**\n * The steps a drafted automation may take: the server steps and the flow\n * steps, which act on the person an event names. The on-page steps — show an\n * element, open a menu, run a script — are left out for the reason the\n * on-page triggers are: they need a selector on a page a description cannot\n * name.\n */\nexport const AI_AUTOMATION_STEP_TYPES = [\n 'sendEmail',\n 'notifyAdmins',\n 'enrollList',\n 'assignCampaign',\n 'runWorkflow',\n 'datasetAppend',\n 'updateDataset',\n 'webhookPost',\n 'siteAlert',\n 'wait',\n 'waitForEvent',\n 'exitFlow',\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const satisfies readonly HostActionStepType[]\n\nexport type AiAutomationStepType = (typeof AI_AUTOMATION_STEP_TYPES)[number]\n\n/** The steps the executor refuses without a part of the platform. */\nexport const AI_AUTOMATION_STEP_NEEDS: Readonly<Partial<Record<AiAutomationStepType, AiAutomationNeed>>> = {\n webhookPost: 'webhooks',\n setContactStage: 'crm',\n addContactTag: 'crm',\n assignContactOwner: 'crm',\n createCrmTask: 'crm',\n logCrmActivity: 'crm',\n}\n\n/** The trigger's name as the Actions editor labels it. */\nexport function aiAutomationTriggerLabel(event: string): string {\n return HOST_EVENT_LABELS[event as HostEventType] ?? event\n}\n\n// ── A description the vocabulary cannot build ─────────────────────────────\n\n/**\n * Why a description was answered with no automation. Each is a fixed reason\n * with a fixed sentence, so what a person reads is never the model's own\n * account of itself.\n */\nexport const AI_AUTOMATION_UNSUPPORTED = [\n 'not-an-automation',\n 'no-trigger',\n 'no-step',\n 'needs-crm',\n 'needs-webhooks',\n 'needs-bookings',\n] as const\n\nexport type AiAutomationUnsupported = (typeof AI_AUTOMATION_UNSUPPORTED)[number]\n\n/** The need an unsupported reason names, for the reasons that name one. */\nexport const AI_AUTOMATION_UNSUPPORTED_NEED: Readonly<\n Partial<Record<AiAutomationUnsupported, AiAutomationNeed>>\n> = {\n 'needs-crm': 'crm',\n 'needs-webhooks': 'webhooks',\n 'needs-bookings': 'bookings',\n}\n\nexport const AI_AUTOMATION_UNSUPPORTED_COPY: Readonly<Record<AiAutomationUnsupported, string>> = {\n 'not-an-automation':\n 'This description is not an automation — something that happens, and what to do then — so nothing was drafted.',\n 'no-trigger':\n 'Nothing on the platform can start an automation like this one, so nothing was drafted. Describe it as something that happens on your site, such as a form being submitted.',\n 'no-step': 'An automation cannot do what this description asks yet, so nothing was drafted.',\n 'needs-crm':\n 'This automation needs the CRM, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-webhooks':\n 'This automation needs webhooks, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-bookings':\n 'This automation needs bookings, which this workspace’s plan does not include, so nothing was drafted.',\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\n/** The id the workflows plugin is registered under. */\nexport const AI_WORKFLOWS_PLUGIN_ID = 'workflows'\n\n/** The resource the workflows plugin writes a drafted automation under. */\nexport const AI_AUTOMATION_RESOURCE = 'automation'\n\nexport const AI_WORKFLOW_NO_SITE_COPY = 'This automation job names a site this workspace does not have.'\nexport const AI_WORKFLOW_UNAVAILABLE_COPY = 'Automations are not available on this site.'\nexport const AI_WORKFLOW_GONE_COPY = 'That automation no longer exists.'\nexport const AI_WORKFLOW_RUN_GONE_COPY = 'That run is no longer in the automation’s history.'\nexport const AI_WORKFLOW_RUN_NOT_FAILED_COPY = 'That run did not fail, so there is nothing to explain.'\nexport const AI_WORKFLOW_NO_DRAFT_COPY =\n 'The AI could not draft an automation from this description. Try describing what happens, and what should happen then.'\nexport const AI_WORKFLOW_NO_EXPLANATION_COPY =\n 'The AI could not explain this automation. Try again.'\nexport const AI_WORKFLOW_SAVE_FAILURE_COPY =\n 'The automation was drafted but could not be saved. Try the job again.'\n"],"names":["checkEntitlement","HOST_EVENT_LABELS","HOST_EVENT_TYPES","AI_WORKFLOW_JOB_MODES","AI_WORKFLOW_TARGET_TYPES","DOCUMENT_ID","parseAiWorkflowJobInputs","inputs","mode","includes","targetType","targetId","test","target","runId","AI_AUTOMATION_NEED_LABELS","crm","webhooks","bookings","aiAutomationCapabilities","org","billing","AI_AUTOMATION_TRIGGERS","AI_AUTOMATION_TRIGGER_NEEDS","booking","taskCompleted","contactStageChanged","dealStageChanged","dealWon","dealLost","AI_AUTOMATION_STEP_TYPES","AI_AUTOMATION_STEP_NEEDS","webhookPost","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","aiAutomationTriggerLabel","event","AI_AUTOMATION_UNSUPPORTED","AI_AUTOMATION_UNSUPPORTED_NEED","AI_AUTOMATION_UNSUPPORTED_COPY","AI_WORKFLOWS_PLUGIN_ID","AI_AUTOMATION_RESOURCE","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,gBAAgB,QAAQ,2CAA0C;AAC3E,SACEC,iBAAiB,EACjBC,gBAAgB,QAEX,mCAAkC;AAGzC;;;;;;;;;;;;;;;;;;CAkBC,GAED,OAAO,MAAMC,wBAAwB;IAAC;IAAS;IAAW;CAAW,CAAS;AAG9E,iFAAiF,GACjF,OAAO,MAAMC,2BAA2B;IAAC;IAAU;CAAW,CAAS;AAQvE,MAAMC,cAAc;AAEpB,0FAA0F,GAC1F,OAAO,SAASC,yBACdC,MAA4D;;IAE5D,MAAMC,eAAOD,0BAAAA,MAAQ,CAAC,OAAO,mBAAI;IACjC,IAAI,CAAC,AAACJ,sBAA6CM,QAAQ,CAACD,OAAO;QACjE,OAAO;IACT;IACA,IAAIA,SAAS,SAAS,OAAO;QAAEA;IAAK;IACpC,MAAME,aAAaH,0BAAAA,MAAQ,CAAC,aAAa;IACzC,MAAMI,WAAWJ,0BAAAA,MAAQ,CAAC,WAAW;IACrC,IACE,CAAC,AAACH,yBAAgDK,QAAQ,CAACC,eAC3D,OAAOC,aAAa,YACpB,CAACN,YAAYO,IAAI,CAACD,WAClB;QACA,OAAO;IACT;IACA,MAAME,SAAS;QAAEH,YAAYA;QAAoCC;IAAS;IAC1E,IAAIH,SAAS,WAAW,OAAO;QAAEA;OAASK;IAC1C,MAAMC,QAAQP,0BAAAA,MAAQ,CAAC,QAAQ;IAC/B,IAAI,OAAOO,UAAU,YAAY,CAACT,YAAYO,IAAI,CAACE,QAAQ,OAAO;IAClE,OAAO;QAAEN,MAAM;OAAeK;QAAQC;;AACxC;AASA,sCAAsC,GACtC,OAAO,MAAMC,4BAAwE;IACnFC,KAAK;IACLC,UAAU;IACVC,UAAU;AACZ,EAAC;AAED;;;;CAIC,GACD,OAAO,SAASC,yBACdC,GAAoF;IAEpF,MAAMC,UAAUD;IAChB,OAAO;QACLJ,KAAKhB,iBAAiBqB,SAAS;QAC/BJ,UAAUjB,iBAAiBqB,SAAS;QACpCH,UAAUlB,iBAAiBqB,SAAS;IACtC;AACF;AAEA,6EAA6E;AAE7E;;;;;CAKC,GACD,OAAO,MAAMC,yBAAmDpB,iBAAgB;AAEhF,8DAA8D,GAC9D,OAAO,MAAMqB,8BAA0F;IACrGC,SAAS;IACTC,eAAe;IACfC,qBAAqB;IACrBC,kBAAkB;IAClBC,SAAS;IACTC,UAAU;AACZ,EAAC;AAED;;;;;;CAMC,GACD,OAAO,MAAMC,2BAA2B;IACtC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAiD;AAIlD,mEAAmE,GACnE,OAAO,MAAMC,2BAA8F;IACzGC,aAAa;IACbC,iBAAiB;IACjBC,eAAe;IACfC,oBAAoB;IACpBC,eAAe;IACfC,gBAAgB;AAClB,EAAC;AAED,wDAAwD,GACxD,OAAO,SAASC,yBAAyBC,KAAa;QAC7CtC;IAAP,QAAOA,2BAAAA,iBAAiB,CAACsC,MAAuB,YAAzCtC,2BAA6CsC;AACtD;AAEA,6EAA6E;AAE7E;;;;CAIC,GACD,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAIV,yEAAyE,GACzE,OAAO,MAAMC,iCAET;IACF,aAAa;IACb,kBAAkB;IAClB,kBAAkB;AACpB,EAAC;AAED,OAAO,MAAMC,iCAAoF;IAC/F,qBACE;IACF,cACE;IACF,WAAW;IACX,aACE;IACF,kBACE;IACF,kBACE;AACJ,EAAC;AAED,6EAA6E;AAE7E,qDAAqD,GACrD,OAAO,MAAMC,yBAAyB,YAAW;AAEjD,yEAAyE,GACzE,OAAO,MAAMC,yBAAyB,aAAY;AAElD,OAAO,MAAMC,2BAA2B,iEAAgE;AACxG,OAAO,MAAMC,+BAA+B,8CAA6C;AACzF,OAAO,MAAMC,wBAAwB,oCAAmC;AACxE,OAAO,MAAMC,4BAA4B,qDAAoD;AAC7F,OAAO,MAAMC,kCAAkC,yDAAwD;AACvG,OAAO,MAAMC,4BACX,wHAAuH;AACzH,OAAO,MAAMC,kCACX,uDAAsD;AACxD,OAAO,MAAMC,gCACX,wEAAuE"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-workflow-job.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\nimport type { HostActionStepType } from '@aglyn/aglyn/app-utils/actions'\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n HOST_EVENT_LABELS,\n HOST_EVENT_TYPES,\n type HostEventType,\n} from '@aglyn/aglyn/app-utils/host-events'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\n\n/**\n * A `workflow` job (AGL-2919): an automation drafted from a description, or an\n * existing automation explained — what it does, or why one of its runs failed.\n *\n * One kind, three modes, because the three share everything a job carries:\n * the site, the gates, the meter and the automation vocabulary. `inputs.mode`\n * picks the mode, and a job created without one drafts.\n *\n * - `draft` writes a new automation to the site's Actions through the\n * workflows plugin's draft writer, OFF, built only from the triggers and\n * steps below that this workspace can run. What the description names that\n * the site does not have is left as a placeholder, never guessed.\n * - `explain` reads a saved automation — an action or a workflow — and\n * answers in plain words what it does.\n * - `diagnose` reads a saved automation and one of its FAILED runs, as the\n * run history recorded it, and answers why it failed and what to change.\n *\n * Nothing in any mode switches an automation on, runs one, or changes one.\n */\n\nexport const AI_WORKFLOW_JOB_MODES = ['draft', 'explain', 'diagnose'] as const\nexport type AiWorkflowJobMode = (typeof AI_WORKFLOW_JOB_MODES)[number]\n\n/** What an explained automation is: an action, or a workflow of function calls. */\nexport const AI_WORKFLOW_TARGET_TYPES = ['action', 'workflow'] as const\nexport type AiWorkflowTargetType = (typeof AI_WORKFLOW_TARGET_TYPES)[number]\n\nexport type AiWorkflowJobInputs =\n | { mode: 'draft' }\n | { mode: 'explain'; targetType: AiWorkflowTargetType; targetId: string }\n | { mode: 'diagnose'; targetType: AiWorkflowTargetType; targetId: string; runId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiWorkflowJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiWorkflowJobInputs | string {\n const mode = inputs?.['mode'] ?? 'draft'\n if (!(AI_WORKFLOW_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be draft, explain or diagnose'\n }\n if (mode === 'draft') return { mode }\n const targetType = inputs?.['targetType']\n const targetId = inputs?.['targetId']\n if (\n !(AI_WORKFLOW_TARGET_TYPES as readonly unknown[]).includes(targetType) ||\n typeof targetId !== 'string' ||\n !DOCUMENT_ID.test(targetId)\n ) {\n return 'Pick the automation to explain'\n }\n const target = { targetType: targetType as AiWorkflowTargetType, targetId }\n if (mode === 'explain') return { mode, ...target }\n const runId = inputs?.['runId']\n if (typeof runId !== 'string' || !DOCUMENT_ID.test(runId)) return 'Pick the run to explain'\n return { mode: 'diagnose', ...target, runId }\n}\n\n// ── What a workspace can run ──────────────────────────────────────────────\n\n/** A part of the platform some triggers and steps need beyond the actions builder itself. */\nexport type AiAutomationNeed = 'crm' | 'webhooks' | 'bookings'\n\nexport type AiAutomationCapabilities = Readonly<Record<AiAutomationNeed, boolean>>\n\n/** Each need as a sentence names it. */\nexport const AI_AUTOMATION_NEED_LABELS: Readonly<Record<AiAutomationNeed, string>> = {\n crm: 'the CRM',\n webhooks: 'webhooks',\n bookings: 'bookings',\n}\n\n/**\n * What this workspace can run, from the entitlements the executor itself\n * reads before it runs a step: `crm` for the CRM steps, `webhooks` for a\n * webhook post, and `bookings` for the booking trigger's door.\n */\nexport function aiAutomationCapabilities(\n org: Partial<AglynOrgBilling> | Readonly<Record<string, unknown>> | null | undefined,\n): AiAutomationCapabilities {\n const billing = org as Partial<AglynOrgBilling> | null | undefined\n return {\n crm: checkEntitlement(billing, 'crm'),\n webhooks: checkEntitlement(billing, 'webhooks'),\n bookings: checkEntitlement(billing, 'bookings'),\n }\n}\n\n// ── The vocabulary ────────────────────────────────────────────────────────\n\n/**\n * The triggers a drafted automation may start on: every host event, which is\n * every event a server door emits. The on-page events (a click, a scroll) are\n * left out: they watch one element of one page, which a description cannot\n * name, and they belong to the page's own interactions.\n */\nexport const AI_AUTOMATION_TRIGGERS: readonly HostEventType[] = HOST_EVENT_TYPES\n\n/** The triggers only a door of a part of the platform emits. */\nexport const AI_AUTOMATION_TRIGGER_NEEDS: Readonly<Partial<Record<HostEventType, AiAutomationNeed>>> = {\n booking: 'bookings',\n taskCompleted: 'crm',\n contactStageChanged: 'crm',\n dealStageChanged: 'crm',\n dealWon: 'crm',\n dealLost: 'crm',\n}\n\n/**\n * The steps a drafted automation may take: the server steps and the flow\n * steps, which act on the person an event names. The on-page steps — show an\n * element, open a menu, run a script — are left out for the reason the\n * on-page triggers are: they need a selector on a page a description cannot\n * name.\n */\nexport const AI_AUTOMATION_STEP_TYPES = [\n 'sendEmail',\n 'notifyAdmins',\n 'enrollList',\n 'assignCampaign',\n 'runWorkflow',\n 'datasetAppend',\n 'updateDataset',\n 'webhookPost',\n 'siteAlert',\n 'wait',\n 'waitForEvent',\n 'exitFlow',\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const satisfies readonly HostActionStepType[]\n\nexport type AiAutomationStepType = (typeof AI_AUTOMATION_STEP_TYPES)[number]\n\n/** The steps the executor refuses without a part of the platform. */\nexport const AI_AUTOMATION_STEP_NEEDS: Readonly<Partial<Record<AiAutomationStepType, AiAutomationNeed>>> = {\n webhookPost: 'webhooks',\n setContactStage: 'crm',\n addContactTag: 'crm',\n assignContactOwner: 'crm',\n createCrmTask: 'crm',\n logCrmActivity: 'crm',\n}\n\n/** The trigger's name as the Actions editor labels it. */\nexport function aiAutomationTriggerLabel(event: string): string {\n return HOST_EVENT_LABELS[event as HostEventType] ?? event\n}\n\n// ── A description the vocabulary cannot build ─────────────────────────────\n\n/**\n * Why a description was answered with no automation. Each is a fixed reason\n * with a fixed sentence, so what a person reads is never the model's own\n * account of itself.\n */\nexport const AI_AUTOMATION_UNSUPPORTED = [\n 'not-an-automation',\n 'no-trigger',\n 'no-step',\n 'needs-crm',\n 'needs-webhooks',\n 'needs-bookings',\n] as const\n\nexport type AiAutomationUnsupported = (typeof AI_AUTOMATION_UNSUPPORTED)[number]\n\n/** The need an unsupported reason names, for the reasons that name one. */\nexport const AI_AUTOMATION_UNSUPPORTED_NEED: Readonly<\n Partial<Record<AiAutomationUnsupported, AiAutomationNeed>>\n> = {\n 'needs-crm': 'crm',\n 'needs-webhooks': 'webhooks',\n 'needs-bookings': 'bookings',\n}\n\nexport const AI_AUTOMATION_UNSUPPORTED_COPY: Readonly<Record<AiAutomationUnsupported, string>> = {\n 'not-an-automation':\n 'This description is not an automation — something that happens, and what to do then — so nothing was drafted.',\n 'no-trigger':\n 'Nothing on the platform can start an automation like this one, so nothing was drafted. Describe it as something that happens on your site, such as a form being submitted.',\n 'no-step': 'An automation cannot do what this description asks yet, so nothing was drafted.',\n 'needs-crm':\n 'This automation needs the CRM, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-webhooks':\n 'This automation needs webhooks, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-bookings':\n 'This automation needs bookings, which this workspace’s plan does not include, so nothing was drafted.',\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\n/** The resource the workflows plugin writes a drafted automation under. */\nexport const AI_AUTOMATION_RESOURCE = 'automation'\n\nexport const AI_WORKFLOW_NO_SITE_COPY = 'This automation job names a site this workspace does not have.'\nexport const AI_WORKFLOW_UNAVAILABLE_COPY = 'Automations are not available on this site.'\nexport const AI_WORKFLOW_GONE_COPY = 'That automation no longer exists.'\nexport const AI_WORKFLOW_RUN_GONE_COPY = 'That run is no longer in the automation’s history.'\nexport const AI_WORKFLOW_RUN_NOT_FAILED_COPY = 'That run did not fail, so there is nothing to explain.'\nexport const AI_WORKFLOW_NO_DRAFT_COPY =\n 'The AI could not draft an automation from this description. Try describing what happens, and what should happen then.'\nexport const AI_WORKFLOW_NO_EXPLANATION_COPY =\n 'The AI could not explain this automation. Try again.'\nexport const AI_WORKFLOW_SAVE_FAILURE_COPY =\n 'The automation was drafted but could not be saved. Try the job again.'\n"],"names":["checkEntitlement","HOST_EVENT_LABELS","HOST_EVENT_TYPES","AI_WORKFLOW_JOB_MODES","AI_WORKFLOW_TARGET_TYPES","DOCUMENT_ID","parseAiWorkflowJobInputs","inputs","mode","includes","targetType","targetId","test","target","runId","AI_AUTOMATION_NEED_LABELS","crm","webhooks","bookings","aiAutomationCapabilities","org","billing","AI_AUTOMATION_TRIGGERS","AI_AUTOMATION_TRIGGER_NEEDS","booking","taskCompleted","contactStageChanged","dealStageChanged","dealWon","dealLost","AI_AUTOMATION_STEP_TYPES","AI_AUTOMATION_STEP_NEEDS","webhookPost","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","aiAutomationTriggerLabel","event","AI_AUTOMATION_UNSUPPORTED","AI_AUTOMATION_UNSUPPORTED_NEED","AI_AUTOMATION_UNSUPPORTED_COPY","AI_AUTOMATION_RESOURCE","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;AAGD,SAASA,gBAAgB,QAAQ,2CAA0C;AAC3E,SACEC,iBAAiB,EACjBC,gBAAgB,QAEX,qCAAoC;AAG3C;;;;;;;;;;;;;;;;;;CAkBC,GAED,OAAO,MAAMC,wBAAwB;IAAC;IAAS;IAAW;CAAW,CAAS;AAG9E,iFAAiF,GACjF,OAAO,MAAMC,2BAA2B;IAAC;IAAU;CAAW,CAAS;AAQvE,MAAMC,cAAc;AAEpB,0FAA0F,GAC1F,OAAO,SAASC,yBACdC,MAA4D;;IAE5D,MAAMC,eAAOD,0BAAAA,MAAQ,CAAC,OAAO,mBAAI;IACjC,IAAI,CAAC,AAACJ,sBAA6CM,QAAQ,CAACD,OAAO;QACjE,OAAO;IACT;IACA,IAAIA,SAAS,SAAS,OAAO;QAAEA;IAAK;IACpC,MAAME,aAAaH,0BAAAA,MAAQ,CAAC,aAAa;IACzC,MAAMI,WAAWJ,0BAAAA,MAAQ,CAAC,WAAW;IACrC,IACE,CAAC,AAACH,yBAAgDK,QAAQ,CAACC,eAC3D,OAAOC,aAAa,YACpB,CAACN,YAAYO,IAAI,CAACD,WAClB;QACA,OAAO;IACT;IACA,MAAME,SAAS;QAAEH,YAAYA;QAAoCC;IAAS;IAC1E,IAAIH,SAAS,WAAW,OAAO;QAAEA;OAASK;IAC1C,MAAMC,QAAQP,0BAAAA,MAAQ,CAAC,QAAQ;IAC/B,IAAI,OAAOO,UAAU,YAAY,CAACT,YAAYO,IAAI,CAACE,QAAQ,OAAO;IAClE,OAAO;QAAEN,MAAM;OAAeK;QAAQC;;AACxC;AASA,sCAAsC,GACtC,OAAO,MAAMC,4BAAwE;IACnFC,KAAK;IACLC,UAAU;IACVC,UAAU;AACZ,EAAC;AAED;;;;CAIC,GACD,OAAO,SAASC,yBACdC,GAAoF;IAEpF,MAAMC,UAAUD;IAChB,OAAO;QACLJ,KAAKhB,iBAAiBqB,SAAS;QAC/BJ,UAAUjB,iBAAiBqB,SAAS;QACpCH,UAAUlB,iBAAiBqB,SAAS;IACtC;AACF;AAEA,6EAA6E;AAE7E;;;;;CAKC,GACD,OAAO,MAAMC,yBAAmDpB,iBAAgB;AAEhF,8DAA8D,GAC9D,OAAO,MAAMqB,8BAA0F;IACrGC,SAAS;IACTC,eAAe;IACfC,qBAAqB;IACrBC,kBAAkB;IAClBC,SAAS;IACTC,UAAU;AACZ,EAAC;AAED;;;;;;CAMC,GACD,OAAO,MAAMC,2BAA2B;IACtC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAiD;AAIlD,mEAAmE,GACnE,OAAO,MAAMC,2BAA8F;IACzGC,aAAa;IACbC,iBAAiB;IACjBC,eAAe;IACfC,oBAAoB;IACpBC,eAAe;IACfC,gBAAgB;AAClB,EAAC;AAED,wDAAwD,GACxD,OAAO,SAASC,yBAAyBC,KAAa;QAC7CtC;IAAP,QAAOA,2BAAAA,iBAAiB,CAACsC,MAAuB,YAAzCtC,2BAA6CsC;AACtD;AAEA,6EAA6E;AAE7E;;;;CAIC,GACD,OAAO,MAAMC,4BAA4B;IACvC;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAIV,yEAAyE,GACzE,OAAO,MAAMC,iCAET;IACF,aAAa;IACb,kBAAkB;IAClB,kBAAkB;AACpB,EAAC;AAED,OAAO,MAAMC,iCAAoF;IAC/F,qBACE;IACF,cACE;IACF,WAAW;IACX,aACE;IACF,kBACE;IACF,kBACE;AACJ,EAAC;AAED,6EAA6E;AAE7E,yEAAyE,GACzE,OAAO,MAAMC,yBAAyB,aAAY;AAElD,OAAO,MAAMC,2BAA2B,iEAAgE;AACxG,OAAO,MAAMC,+BAA+B,8CAA6C;AACzF,OAAO,MAAMC,wBAAwB,oCAAmC;AACxE,OAAO,MAAMC,4BAA4B,qDAAoD;AAC7F,OAAO,MAAMC,kCAAkC,yDAAwD;AACvG,OAAO,MAAMC,4BACX,wHAAuH;AACzH,OAAO,MAAMC,kCACX,uDAAsD;AACxD,OAAO,MAAMC,gCACX,wEAAuE"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types';
18
+ import type { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements';
19
+ /**
20
+ * THE AI PLUGIN'S PLAN BAND (AGL-3080): how many AI credits each plan
21
+ * includes a month. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest
22
+ * generator (`register.planEntitlements`), so `/pricing`, the plan comparison
23
+ * and every gate read the same figure they always have; this is where it is
24
+ * written, beside the reasoning that sized it.
25
+ */
26
+ declare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {
27
+ interface PluginEntitlementQuotas {
28
+ /**
29
+ * AI credits per calendar month — the band assist spends against.
30
+ *
31
+ * A CREDIT IS A UNIT OF MODEL COST, not a message. One assist action can
32
+ * cost two orders of magnitude more than another: a question is a few
33
+ * thousand tokens, while generating a screen carries the node tree, the
34
+ * component catalog and the theme tokens in, structured markup out, and
35
+ * iterates. A message allowance would price those the same, so one
36
+ * workspace's ten screen builds would outspend another's thousand
37
+ * questions and both would read as "within allowance".
38
+ *
39
+ * The unit is defined by `ASSIST_CREDIT_COST_USD` and the conversion
40
+ * lives in `assist-credits.ts`. Only that module turns credits into
41
+ * dollars; everything a customer sees counts credits, because the dollar
42
+ * figure behind them is our provider bill and not a price.
43
+ *
44
+ * ## HOW THE BAND IS SIZED, and why it is not a share of the price
45
+ *
46
+ * A credit is a dollar of provider spend at `ASSIST_CREDIT_COST_USD`, so
47
+ * the band IS a liability figure: the plan's whole assist give is
48
+ * `assistCreditsPerMonth / 1000` dollars. Credits past it are not a give
49
+ * — since AGL-2653 they are SOLD at `extraAssistCreditsUsdPer1k`, unless
50
+ * the org's `assistOverage.hardCap` asks `reserveAssistMessage` to refuse
51
+ * at the band instead. Sizing it as a share of the subscription price
52
+ * says nothing about whether the tier can afford it, because the price is
53
+ * also carrying storage, bandwidth, form submissions, dataset storage,
54
+ * API requests, contacts and email.
55
+ *
56
+ * So each band is sized against what those SEVEN other terms leave.
57
+ * Every paid band takes between a quarter and a third of that remainder,
58
+ * which keeps the tier's worst case positive and keeps the ladder's
59
+ * shape: the margin each tier holds at 100% of every band stays in
60
+ * proportion to what it held before assist was sold at all.
61
+ * `tier-margin-floor.spec.ts` is the model, and it pins both the rule and
62
+ * the resulting figures.
63
+ *
64
+ * The Aglyn AI add-on (AGL-2896) adds `AI_ADDON_CREDITS_PER_MONTH[plan]`
65
+ * to this band in `resolveOrgEntitlements` — one pool, widened, rather
66
+ * than a second meter.
67
+ */
68
+ assistCreditsPerMonth?: number;
69
+ }
70
+ }
71
+ /**
72
+ * AI credits an Enterprise agreement includes per month, before the contract
73
+ * says otherwise.
74
+ *
75
+ * ## Never `UNLIMITED`, and the reason is not stylistic
76
+ *
77
+ * `UNLIMITED` is `Number.POSITIVE_INFINITY`, `JSON.stringify(Infinity)` is
78
+ * `null`, and `Number(null)` is `0`. An unbounded assist band therefore
79
+ * serialises to a band of ZERO through every route that does not also send
80
+ * the explicit flag `restoreQuotaLimit` rebuilds from — which would hand the
81
+ * only customers with a signed contract the one budget that refuses
82
+ * everything. A finite number crosses the wire as itself.
83
+ *
84
+ * The band is also a real liability rather than a capacity we already own.
85
+ * Storage and page views are infrastructure metered into the deal; assist is
86
+ * a per-token charge from a third party, and generative building spends it in
87
+ * units two orders of magnitude apart.
88
+ *
89
+ * ## Why this number
90
+ *
91
+ * Enterprise carries no list price, so the band cannot be sized the way every
92
+ * self-serve tier's is — against what that tier's other cost terms leave out
93
+ * of a known price. It is anchored to the top of the ladder instead: 116,000
94
+ * credits is twice Agency's band, the rule every Enterprise fallback follows
95
+ * since the 2026-09-07 pricing decision (see the `enterprise` row of
96
+ * `PLAN_ENTITLEMENTS`). In cost that is $116 of provider spend a month, or
97
+ * under 9% of any deal priced at or above the self-serve top. A deal below
98
+ * that is sold as Agency, not written as an Enterprise agreement.
99
+ *
100
+ * ## It is a DEFAULT, and a contract raises it
101
+ *
102
+ * `resolveOrgEntitlements` applies a per-org
103
+ * `entitlements.assistCreditsPerMonth` override ahead of this, so a deal that
104
+ * buys more assist buys it on that org without moving the figure every other
105
+ * agreement is measured against — the same mechanism the other contracted
106
+ * bands use.
107
+ */
108
+ export declare const ENTERPRISE_ASSIST_CREDITS_PER_MONTH = 116000;
109
+ /**
110
+ * The Free AI taste (AGL-2925): the credits a Free workspace draws on each
111
+ * month, behind a hard wall.
112
+ *
113
+ * ## Why it exists
114
+ *
115
+ * So the AI landing pages can say "generate your first page free" and mean
116
+ * it. Three hundred credits is one or two generated sections or a handful of
117
+ * copy rewrites — enough to see what the assistant does, not enough to build
118
+ * a site on. The band is a WALL: `PLAN_PRICING.free
119
+ * .extraAssistCreditsUsdPer1k` stays `null`, so `assistBandRefuses` answers
120
+ * true and the reservation refuses at 100% with no overage, no switch and no
121
+ * invoice. Nothing about a Free workspace can produce a charge.
122
+ *
123
+ * ## What it costs, and why that is the number
124
+ *
125
+ * A credit is `ASSIST_CREDIT_COST_USD` of provider spend, so the band costs
126
+ * at most **$0.30 per Free workspace per month** — asserted by
127
+ * `apps/console/specs/tier-margin-floor.spec.ts`. It is the one AI band with
128
+ * no invoice behind it, which is why the same issue closes every multiplier
129
+ * a script could use before the flag flips:
130
+ *
131
+ * - it is metered per ACCOUNT as well as per workspace, so the three free
132
+ * workspaces an account may hold (AGL-2265) share one 300-credit
133
+ * allowance rather than tripling it (`reserveAssistMessage`);
134
+ * - a fresh account waits `AI_FREE_MIN_ACCOUNT_AGE_HOURS` before it can
135
+ * spend, and every free request is bounded per IP, per uid and per
136
+ * account per day;
137
+ * - a platform-wide daily ceiling on free spend pauses the taste for
138
+ * everyone until the UTC day rolls, paid workspaces untouched.
139
+ *
140
+ * The account allowance reads THIS constant rather than the workspace's
141
+ * resolved band, because a staff override that widens one workspace's band
142
+ * is a decision about that workspace, not about how much one person may draw
143
+ * across all of theirs.
144
+ */
145
+ export declare const FREE_AI_TASTE_CREDITS_PER_MONTH = 300;
146
+ /** The AI credits each plan includes a month — see the key's own docblock. */
147
+ export declare const AI_CREDITS_BY_PLAN: Readonly<Record<OrgPlan, number>>;
148
+ /** The AI plugin's plan figures, for the manifest generator. */
149
+ export declare function aiPlanEntitlements(): PluginPlanEntitlementsDeclaration;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ // Types only: the manifest generator loads this module on its own to compile
17
+ // the plan figures, before the catalog file core reads them from exists in
18
+ // its new shape, so nothing here may reach a core module at runtime.
19
+ /**
20
+ * AI credits an Enterprise agreement includes per month, before the contract
21
+ * says otherwise.
22
+ *
23
+ * ## Never `UNLIMITED`, and the reason is not stylistic
24
+ *
25
+ * `UNLIMITED` is `Number.POSITIVE_INFINITY`, `JSON.stringify(Infinity)` is
26
+ * `null`, and `Number(null)` is `0`. An unbounded assist band therefore
27
+ * serialises to a band of ZERO through every route that does not also send
28
+ * the explicit flag `restoreQuotaLimit` rebuilds from — which would hand the
29
+ * only customers with a signed contract the one budget that refuses
30
+ * everything. A finite number crosses the wire as itself.
31
+ *
32
+ * The band is also a real liability rather than a capacity we already own.
33
+ * Storage and page views are infrastructure metered into the deal; assist is
34
+ * a per-token charge from a third party, and generative building spends it in
35
+ * units two orders of magnitude apart.
36
+ *
37
+ * ## Why this number
38
+ *
39
+ * Enterprise carries no list price, so the band cannot be sized the way every
40
+ * self-serve tier's is — against what that tier's other cost terms leave out
41
+ * of a known price. It is anchored to the top of the ladder instead: 116,000
42
+ * credits is twice Agency's band, the rule every Enterprise fallback follows
43
+ * since the 2026-09-07 pricing decision (see the `enterprise` row of
44
+ * `PLAN_ENTITLEMENTS`). In cost that is $116 of provider spend a month, or
45
+ * under 9% of any deal priced at or above the self-serve top. A deal below
46
+ * that is sold as Agency, not written as an Enterprise agreement.
47
+ *
48
+ * ## It is a DEFAULT, and a contract raises it
49
+ *
50
+ * `resolveOrgEntitlements` applies a per-org
51
+ * `entitlements.assistCreditsPerMonth` override ahead of this, so a deal that
52
+ * buys more assist buys it on that org without moving the figure every other
53
+ * agreement is measured against — the same mechanism the other contracted
54
+ * bands use.
55
+ */ export const ENTERPRISE_ASSIST_CREDITS_PER_MONTH = 116000;
56
+ /**
57
+ * The Free AI taste (AGL-2925): the credits a Free workspace draws on each
58
+ * month, behind a hard wall.
59
+ *
60
+ * ## Why it exists
61
+ *
62
+ * So the AI landing pages can say "generate your first page free" and mean
63
+ * it. Three hundred credits is one or two generated sections or a handful of
64
+ * copy rewrites — enough to see what the assistant does, not enough to build
65
+ * a site on. The band is a WALL: `PLAN_PRICING.free
66
+ * .extraAssistCreditsUsdPer1k` stays `null`, so `assistBandRefuses` answers
67
+ * true and the reservation refuses at 100% with no overage, no switch and no
68
+ * invoice. Nothing about a Free workspace can produce a charge.
69
+ *
70
+ * ## What it costs, and why that is the number
71
+ *
72
+ * A credit is `ASSIST_CREDIT_COST_USD` of provider spend, so the band costs
73
+ * at most **$0.30 per Free workspace per month** — asserted by
74
+ * `apps/console/specs/tier-margin-floor.spec.ts`. It is the one AI band with
75
+ * no invoice behind it, which is why the same issue closes every multiplier
76
+ * a script could use before the flag flips:
77
+ *
78
+ * - it is metered per ACCOUNT as well as per workspace, so the three free
79
+ * workspaces an account may hold (AGL-2265) share one 300-credit
80
+ * allowance rather than tripling it (`reserveAssistMessage`);
81
+ * - a fresh account waits `AI_FREE_MIN_ACCOUNT_AGE_HOURS` before it can
82
+ * spend, and every free request is bounded per IP, per uid and per
83
+ * account per day;
84
+ * - a platform-wide daily ceiling on free spend pauses the taste for
85
+ * everyone until the UTC day rolls, paid workspaces untouched.
86
+ *
87
+ * The account allowance reads THIS constant rather than the workspace's
88
+ * resolved band, because a staff override that widens one workspace's band
89
+ * is a decision about that workspace, not about how much one person may draw
90
+ * across all of theirs.
91
+ */ export const FREE_AI_TASTE_CREDITS_PER_MONTH = 300;
92
+ /** The AI credits each plan includes a month — see the key's own docblock. */ export const AI_CREDITS_BY_PLAN = {
93
+ // The Free AI taste (AGL-2925): a real band, and a wall. See the constant
94
+ // for what it buys, what it costs and the precautions that keep one person
95
+ // from drawing it three times over.
96
+ free: FREE_AI_TASTE_CREDITS_PER_MONTH,
97
+ // The first paid rung of the assist ladder (AGL-3203). Starter banded at 0
98
+ // until 2026-09-20, which put a PAYING workspace below the Free taste's 300
99
+ // on the one axis a visitor compares straight down the column — the
100
+ // `/pricing` row read `300 / mo` for Free and `—` for Starter. A paid plan
101
+ // that includes less of something than the free plan is not a packaging
102
+ // subtlety; it is the comparison table arguing against the upgrade.
103
+ //
104
+ // 750 is deliberately small: 2.5x the taste, enough that the assistant is a
105
+ // thing the tier HAS rather than a thing it is shown, and $0.75 of provider
106
+ // spend a month at `ASSIST_CREDIT_COST_USD` against a $16 annual price — a
107
+ // twentieth of the $8.81 the tier's other metered bands already cost, and
108
+ // the widest margin on the ladder absorbs it with room left
109
+ // (`tier-margin-floor.spec.ts` carries the arithmetic).
110
+ //
111
+ // It is NOT a wall, unlike Free's: `PLAN_PRICING.starter
112
+ // .extraAssistCreditsUsdPer1k` is $3.00, so past 750 the tier meters and
113
+ // bills like every paid plan above it. The two fields move together by rule
114
+ // — a positive band with no rate beside it is usage past a bound that is
115
+ // silently free, which `plan-entitlements.spec.ts` forbids.
116
+ //
117
+ // `features.aiAssist` is TRUE, and AGL-3203 shipping it false was the
118
+ // defect AGL-3207 closes. The reasoning then was "the band is credits, not
119
+ // the guided rung, exactly as on Free" — but on Free the band IS spendable,
120
+ // through `aiGenerative`, which is the door the taste exists for. Starter
121
+ // carried neither flag, so it was the only row on the ladder sold a band
122
+ // with nothing to spend it through: 750 credits reachable only by buying
123
+ // the $9 add-on that was also the only thing that made them usable. "No
124
+ // plan bands at zero any more" was true of the number and false of the
125
+ // capability.
126
+ //
127
+ // It opens `aiAssist` rather than `aiGenerative` because that is the shape
128
+ // of every paid rung above it — the included band funds the guided
129
+ // assistant and generation is the add-on. Opening the generative door here
130
+ // instead would hand the FIRST paid rung a door Pro through Agency have to
131
+ // buy.
132
+ //
133
+ // No cost moves: `tier-margin-floor.spec.ts` already prices this tier's
134
+ // assist term at 100% burn of the 750 credits, 75¢/mo. A door does not
135
+ // widen a band; it only makes spend that was already modeled reachable.
136
+ //
137
+ // The AI add-on adds `AI_ADDON_CREDITS_PER_MONTH.starter` on top and
138
+ // switches `aiGenerative` on as well.
139
+ starter: 750,
140
+ // $2.75 of provider spend, against the $8.35 the tier's other seven cost
141
+ // terms leave out of $56. See the key's docblock for why the remainder and
142
+ // not the price is what sizes this.
143
+ pro: 2750,
144
+ // $7.50, against the $22.69 the other seven terms leave out of $139.
145
+ business: 7500,
146
+ // $10.00, against the $30.62 the other seven terms leave out of $249.
147
+ scale: 10000,
148
+ // $13.00, against the $39.74 the other seven terms leave out of $399 — the
149
+ // thinnest remainder on the ladder, and the tier that binds the whole
150
+ // assist ladder's share of it.
151
+ advanced: 13000,
152
+ // $58.00, against the $176.71 the other seven terms leave out of $1,299.
153
+ agency: 58000,
154
+ enterprise: ENTERPRISE_ASSIST_CREDITS_PER_MONTH
155
+ };
156
+ /** The AI plugin's plan figures, for the manifest generator. */ export function aiPlanEntitlements() {
157
+ return {
158
+ quotas: [
159
+ {
160
+ key: 'assistCreditsPerMonth',
161
+ label: 'AI credits / mo',
162
+ byPlan: AI_CREDITS_BY_PLAN
163
+ }
164
+ ]
165
+ };
166
+ }
167
+
168
+ //# sourceMappingURL=plan-entitlements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/plugins/ai/src/lib/plan-entitlements.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// Types only: the manifest generator loads this module on its own to compile\n// the plan figures, before the catalog file core reads them from exists in\n// its new shape, so nothing here may reach a core module at runtime.\nimport type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements'\n\n/**\n * THE AI PLUGIN'S PLAN BAND (AGL-3080): how many AI credits each plan\n * includes a month. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest\n * generator (`register.planEntitlements`), so `/pricing`, the plan comparison\n * and every gate read the same figure they always have; this is where it is\n * written, beside the reasoning that sized it.\n */\ndeclare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {\n interface PluginEntitlementQuotas {\n /**\n * AI credits per calendar month — the band assist spends against.\n *\n * A CREDIT IS A UNIT OF MODEL COST, not a message. One assist action can\n * cost two orders of magnitude more than another: a question is a few\n * thousand tokens, while generating a screen carries the node tree, the\n * component catalog and the theme tokens in, structured markup out, and\n * iterates. A message allowance would price those the same, so one\n * workspace's ten screen builds would outspend another's thousand\n * questions and both would read as \"within allowance\".\n *\n * The unit is defined by `ASSIST_CREDIT_COST_USD` and the conversion\n * lives in `assist-credits.ts`. Only that module turns credits into\n * dollars; everything a customer sees counts credits, because the dollar\n * figure behind them is our provider bill and not a price.\n *\n * ## HOW THE BAND IS SIZED, and why it is not a share of the price\n *\n * A credit is a dollar of provider spend at `ASSIST_CREDIT_COST_USD`, so\n * the band IS a liability figure: the plan's whole assist give is\n * `assistCreditsPerMonth / 1000` dollars. Credits past it are not a give\n * — since AGL-2653 they are SOLD at `extraAssistCreditsUsdPer1k`, unless\n * the org's `assistOverage.hardCap` asks `reserveAssistMessage` to refuse\n * at the band instead. Sizing it as a share of the subscription price\n * says nothing about whether the tier can afford it, because the price is\n * also carrying storage, bandwidth, form submissions, dataset storage,\n * API requests, contacts and email.\n *\n * So each band is sized against what those SEVEN other terms leave.\n * Every paid band takes between a quarter and a third of that remainder,\n * which keeps the tier's worst case positive and keeps the ladder's\n * shape: the margin each tier holds at 100% of every band stays in\n * proportion to what it held before assist was sold at all.\n * `tier-margin-floor.spec.ts` is the model, and it pins both the rule and\n * the resulting figures.\n *\n * The Aglyn AI add-on (AGL-2896) adds `AI_ADDON_CREDITS_PER_MONTH[plan]`\n * to this band in `resolveOrgEntitlements` — one pool, widened, rather\n * than a second meter.\n */\n assistCreditsPerMonth?: number\n }\n}\n\n/**\n * AI credits an Enterprise agreement includes per month, before the contract\n * says otherwise.\n *\n * ## Never `UNLIMITED`, and the reason is not stylistic\n *\n * `UNLIMITED` is `Number.POSITIVE_INFINITY`, `JSON.stringify(Infinity)` is\n * `null`, and `Number(null)` is `0`. An unbounded assist band therefore\n * serialises to a band of ZERO through every route that does not also send\n * the explicit flag `restoreQuotaLimit` rebuilds from — which would hand the\n * only customers with a signed contract the one budget that refuses\n * everything. A finite number crosses the wire as itself.\n *\n * The band is also a real liability rather than a capacity we already own.\n * Storage and page views are infrastructure metered into the deal; assist is\n * a per-token charge from a third party, and generative building spends it in\n * units two orders of magnitude apart.\n *\n * ## Why this number\n *\n * Enterprise carries no list price, so the band cannot be sized the way every\n * self-serve tier's is — against what that tier's other cost terms leave out\n * of a known price. It is anchored to the top of the ladder instead: 116,000\n * credits is twice Agency's band, the rule every Enterprise fallback follows\n * since the 2026-09-07 pricing decision (see the `enterprise` row of\n * `PLAN_ENTITLEMENTS`). In cost that is $116 of provider spend a month, or\n * under 9% of any deal priced at or above the self-serve top. A deal below\n * that is sold as Agency, not written as an Enterprise agreement.\n *\n * ## It is a DEFAULT, and a contract raises it\n *\n * `resolveOrgEntitlements` applies a per-org\n * `entitlements.assistCreditsPerMonth` override ahead of this, so a deal that\n * buys more assist buys it on that org without moving the figure every other\n * agreement is measured against — the same mechanism the other contracted\n * bands use.\n */\nexport const ENTERPRISE_ASSIST_CREDITS_PER_MONTH = 116_000\n\n/**\n * The Free AI taste (AGL-2925): the credits a Free workspace draws on each\n * month, behind a hard wall.\n *\n * ## Why it exists\n *\n * So the AI landing pages can say \"generate your first page free\" and mean\n * it. Three hundred credits is one or two generated sections or a handful of\n * copy rewrites — enough to see what the assistant does, not enough to build\n * a site on. The band is a WALL: `PLAN_PRICING.free\n * .extraAssistCreditsUsdPer1k` stays `null`, so `assistBandRefuses` answers\n * true and the reservation refuses at 100% with no overage, no switch and no\n * invoice. Nothing about a Free workspace can produce a charge.\n *\n * ## What it costs, and why that is the number\n *\n * A credit is `ASSIST_CREDIT_COST_USD` of provider spend, so the band costs\n * at most **$0.30 per Free workspace per month** — asserted by\n * `apps/console/specs/tier-margin-floor.spec.ts`. It is the one AI band with\n * no invoice behind it, which is why the same issue closes every multiplier\n * a script could use before the flag flips:\n *\n * - it is metered per ACCOUNT as well as per workspace, so the three free\n * workspaces an account may hold (AGL-2265) share one 300-credit\n * allowance rather than tripling it (`reserveAssistMessage`);\n * - a fresh account waits `AI_FREE_MIN_ACCOUNT_AGE_HOURS` before it can\n * spend, and every free request is bounded per IP, per uid and per\n * account per day;\n * - a platform-wide daily ceiling on free spend pauses the taste for\n * everyone until the UTC day rolls, paid workspaces untouched.\n *\n * The account allowance reads THIS constant rather than the workspace's\n * resolved band, because a staff override that widens one workspace's band\n * is a decision about that workspace, not about how much one person may draw\n * across all of theirs.\n */\nexport const FREE_AI_TASTE_CREDITS_PER_MONTH = 300\n\n/** The AI credits each plan includes a month — see the key's own docblock. */\nexport const AI_CREDITS_BY_PLAN: Readonly<Record<OrgPlan, number>> = {\n // The Free AI taste (AGL-2925): a real band, and a wall. See the constant\n // for what it buys, what it costs and the precautions that keep one person\n // from drawing it three times over.\n free: FREE_AI_TASTE_CREDITS_PER_MONTH,\n // The first paid rung of the assist ladder (AGL-3203). Starter banded at 0\n // until 2026-09-20, which put a PAYING workspace below the Free taste's 300\n // on the one axis a visitor compares straight down the column — the\n // `/pricing` row read `300 / mo` for Free and `—` for Starter. A paid plan\n // that includes less of something than the free plan is not a packaging\n // subtlety; it is the comparison table arguing against the upgrade.\n //\n // 750 is deliberately small: 2.5x the taste, enough that the assistant is a\n // thing the tier HAS rather than a thing it is shown, and $0.75 of provider\n // spend a month at `ASSIST_CREDIT_COST_USD` against a $16 annual price — a\n // twentieth of the $8.81 the tier's other metered bands already cost, and\n // the widest margin on the ladder absorbs it with room left\n // (`tier-margin-floor.spec.ts` carries the arithmetic).\n //\n // It is NOT a wall, unlike Free's: `PLAN_PRICING.starter\n // .extraAssistCreditsUsdPer1k` is $3.00, so past 750 the tier meters and\n // bills like every paid plan above it. The two fields move together by rule\n // — a positive band with no rate beside it is usage past a bound that is\n // silently free, which `plan-entitlements.spec.ts` forbids.\n //\n // `features.aiAssist` is TRUE, and AGL-3203 shipping it false was the\n // defect AGL-3207 closes. The reasoning then was \"the band is credits, not\n // the guided rung, exactly as on Free\" — but on Free the band IS spendable,\n // through `aiGenerative`, which is the door the taste exists for. Starter\n // carried neither flag, so it was the only row on the ladder sold a band\n // with nothing to spend it through: 750 credits reachable only by buying\n // the $9 add-on that was also the only thing that made them usable. \"No\n // plan bands at zero any more\" was true of the number and false of the\n // capability.\n //\n // It opens `aiAssist` rather than `aiGenerative` because that is the shape\n // of every paid rung above it — the included band funds the guided\n // assistant and generation is the add-on. Opening the generative door here\n // instead would hand the FIRST paid rung a door Pro through Agency have to\n // buy.\n //\n // No cost moves: `tier-margin-floor.spec.ts` already prices this tier's\n // assist term at 100% burn of the 750 credits, 75¢/mo. A door does not\n // widen a band; it only makes spend that was already modeled reachable.\n //\n // The AI add-on adds `AI_ADDON_CREDITS_PER_MONTH.starter` on top and\n // switches `aiGenerative` on as well.\n starter: 750,\n // $2.75 of provider spend, against the $8.35 the tier's other seven cost\n // terms leave out of $56. See the key's docblock for why the remainder and\n // not the price is what sizes this.\n pro: 2_750,\n // $7.50, against the $22.69 the other seven terms leave out of $139.\n business: 7_500,\n // $10.00, against the $30.62 the other seven terms leave out of $249.\n scale: 10_000,\n // $13.00, against the $39.74 the other seven terms leave out of $399 — the\n // thinnest remainder on the ladder, and the tier that binds the whole\n // assist ladder's share of it.\n advanced: 13_000,\n // $58.00, against the $176.71 the other seven terms leave out of $1,299.\n agency: 58_000,\n enterprise: ENTERPRISE_ASSIST_CREDITS_PER_MONTH,\n}\n\n/** The AI plugin's plan figures, for the manifest generator. */\nexport function aiPlanEntitlements(): PluginPlanEntitlementsDeclaration {\n return {\n quotas: [\n {\n key: 'assistCreditsPerMonth',\n label: 'AI credits / mo',\n byPlan: AI_CREDITS_BY_PLAN,\n },\n ],\n }\n}\n"],"names":["ENTERPRISE_ASSIST_CREDITS_PER_MONTH","FREE_AI_TASTE_CREDITS_PER_MONTH","AI_CREDITS_BY_PLAN","free","starter","pro","business","scale","advanced","agency","enterprise","aiPlanEntitlements","quotas","key","label","byPlan"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,6EAA6E;AAC7E,2EAA2E;AAC3E,qEAAqE;AAyDrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCC,GACD,OAAO,MAAMA,sCAAsC,OAAO;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCC,GACD,OAAO,MAAMC,kCAAkC,IAAG;AAElD,4EAA4E,GAC5E,OAAO,MAAMC,qBAAwD;IACnE,0EAA0E;IAC1E,2EAA2E;IAC3E,oCAAoC;IACpCC,MAAMF;IACN,2EAA2E;IAC3E,4EAA4E;IAC5E,oEAAoE;IACpE,2EAA2E;IAC3E,wEAAwE;IACxE,oEAAoE;IACpE,EAAE;IACF,4EAA4E;IAC5E,4EAA4E;IAC5E,2EAA2E;IAC3E,0EAA0E;IAC1E,4DAA4D;IAC5D,wDAAwD;IACxD,EAAE;IACF,yDAAyD;IACzD,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,4DAA4D;IAC5D,EAAE;IACF,sEAAsE;IACtE,2EAA2E;IAC3E,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,yEAAyE;IACzE,wEAAwE;IACxE,uEAAuE;IACvE,cAAc;IACd,EAAE;IACF,2EAA2E;IAC3E,mEAAmE;IACnE,2EAA2E;IAC3E,2EAA2E;IAC3E,OAAO;IACP,EAAE;IACF,wEAAwE;IACxE,uEAAuE;IACvE,wEAAwE;IACxE,EAAE;IACF,qEAAqE;IACrE,sCAAsC;IACtCG,SAAS;IACT,yEAAyE;IACzE,2EAA2E;IAC3E,oCAAoC;IACpCC,KAAK;IACL,qEAAqE;IACrEC,UAAU;IACV,sEAAsE;IACtEC,OAAO;IACP,2EAA2E;IAC3E,sEAAsE;IACtE,+BAA+B;IAC/BC,UAAU;IACV,yEAAyE;IACzEC,QAAQ;IACRC,YAAYV;AACd,EAAC;AAED,8DAA8D,GAC9D,OAAO,SAASW;IACd,OAAO;QACLC,QAAQ;YACN;gBACEC,KAAK;gBACLC,OAAO;gBACPC,QAAQb;YACV;SACD;IACH;AACF"}
package/src/lib/plugin.js CHANGED
@@ -89,7 +89,7 @@ import { registerAiDeclarations } from "./declarations.js";
89
89
  ],
90
90
  widgets: [
91
91
  {
92
- slot: 'assistPanel',
92
+ slot: 'consoleDock',
93
93
  widgetId: 'ai-assist-dock',
94
94
  title: 'Assistant',
95
95
  Component: AssistPanelOnHost