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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +3 -3
  2. package/package.json +17 -17
  3. package/src/lib/activity/ai-activity-actions.js +7 -1
  4. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  5. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  7. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  8. package/src/lib/components/ai-assist-provider.component.js +9 -10
  9. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  10. package/src/lib/components/ai-automation-zones.d.ts +38 -0
  11. package/src/lib/components/ai-automation-zones.js +5 -1
  12. package/src/lib/components/ai-automation-zones.js.map +1 -1
  13. package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
  14. package/src/lib/components/ai-brief-dialog.component.js +87 -8
  15. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  16. package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
  17. package/src/lib/components/ai-campaign-create.component.js +270 -0
  18. package/src/lib/components/ai-campaign-create.component.js.map +1 -0
  19. package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
  20. package/src/lib/components/ai-describe-automation.component.js +15 -6
  21. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  22. package/src/lib/components/ai-describe-button.component.d.ts +20 -8
  23. package/src/lib/components/ai-describe-button.component.js +19 -36
  24. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  25. package/src/lib/components/ai-describe-email.component.d.ts +31 -0
  26. package/src/lib/components/ai-describe-email.component.js +22 -0
  27. package/src/lib/components/ai-describe-email.component.js.map +1 -0
  28. package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
  29. package/src/lib/components/ai-describe-org-automation.component.js +238 -0
  30. package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
  31. package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
  32. package/src/lib/components/ai-experiment-cards.component.js +104 -18
  33. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  34. package/src/lib/components/ai-explain-automation.component.js +1 -5
  35. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  36. package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
  37. package/src/lib/components/ai-funnel-zones.component.js +178 -0
  38. package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
  39. package/src/lib/components/ai-insight-card.component.d.ts +42 -0
  40. package/src/lib/components/ai-insight-card.component.js +137 -0
  41. package/src/lib/components/ai-insight-card.component.js.map +1 -0
  42. package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
  43. package/src/lib/components/ai-insight-dialog.component.js +9 -3
  44. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  45. package/src/lib/components/ai-job-plan.component.d.ts +7 -2
  46. package/src/lib/components/ai-job-plan.component.js +38 -6
  47. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  48. package/src/lib/components/ai-job-requests.d.ts +17 -1
  49. package/src/lib/components/ai-job-requests.js +39 -3
  50. package/src/lib/components/ai-job-requests.js.map +1 -1
  51. package/src/lib/components/ai-logic-zones.d.ts +64 -0
  52. package/src/lib/components/ai-logic-zones.js +18 -0
  53. package/src/lib/components/ai-logic-zones.js.map +1 -0
  54. package/src/lib/components/ai-logic.component.d.ts +77 -0
  55. package/src/lib/components/ai-logic.component.js +553 -0
  56. package/src/lib/components/ai-logic.component.js.map +1 -0
  57. package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
  58. package/src/lib/components/ai-marketing-insight.component.js +80 -0
  59. package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
  60. package/src/lib/components/ai-media-create.component.d.ts +55 -0
  61. package/src/lib/components/ai-media-create.component.js +433 -0
  62. package/src/lib/components/ai-media-create.component.js.map +1 -0
  63. package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
  64. package/src/lib/components/ai-overlay-cards.component.js +505 -0
  65. package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
  66. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  67. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  68. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  69. package/src/lib/components/ai-product-zones.d.ts +5 -0
  70. package/src/lib/components/ai-product-zones.js +1 -1
  71. package/src/lib/components/ai-product-zones.js.map +1 -1
  72. package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
  73. package/src/lib/components/ai-products-create-button.component.js +67 -0
  74. package/src/lib/components/ai-products-create-button.component.js.map +1 -0
  75. package/src/lib/components/ai-products-hub-card.component.js +100 -81
  76. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  77. package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
  78. package/src/lib/components/ai-revise-automation.component.js +207 -0
  79. package/src/lib/components/ai-revise-automation.component.js.map +1 -0
  80. package/src/lib/components/ai-site-build-page.component.js +160 -24
  81. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  82. package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
  83. package/src/lib/components/ai-upsell-dialog.component.js +177 -0
  84. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
  85. package/src/lib/components/assist-build-card.component.d.ts +42 -0
  86. package/src/lib/components/assist-build-card.component.js +183 -0
  87. package/src/lib/components/assist-build-card.component.js.map +1 -0
  88. package/src/lib/components/assist-edit-canvas.d.ts +2 -0
  89. package/src/lib/components/assist-edit-canvas.js +56 -1
  90. package/src/lib/components/assist-edit-canvas.js.map +1 -1
  91. package/src/lib/components/assist-jobs-drawer.component.js +48 -3
  92. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  93. package/src/lib/components/assist-panel.component.js +51 -2
  94. package/src/lib/components/assist-panel.component.js.map +1 -1
  95. package/src/lib/components/use-ai-job-run.d.ts +3 -14
  96. package/src/lib/components/use-ai-job-run.js +0 -50
  97. package/src/lib/components/use-ai-job-run.js.map +1 -1
  98. package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
  99. package/src/lib/jobs/ai-build-capabilities.js +256 -0
  100. package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
  101. package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
  102. package/src/lib/jobs/ai-build-unit-outcome.js +140 -0
  103. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
  104. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  105. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  106. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  107. package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
  108. package/src/lib/jobs/ai-job-build-step.js +668 -0
  109. package/src/lib/jobs/ai-job-build-step.js.map +1 -0
  110. package/src/lib/jobs/ai-job-draft-ids.js +9 -1
  111. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  112. package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
  113. package/src/lib/jobs/ai-job-drafts.js +25 -4
  114. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  115. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  116. package/src/lib/jobs/ai-job-generation.js +13 -2
  117. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  118. package/src/lib/jobs/ai-job-insight-step.js +8 -1
  119. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  120. package/src/lib/jobs/ai-job-layout-language.d.ts +73 -0
  121. package/src/lib/jobs/ai-job-layout-language.js +194 -0
  122. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  123. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  124. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  125. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  126. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  127. package/src/lib/jobs/ai-job-layout-step.js +105 -5
  128. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  129. package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
  130. package/src/lib/jobs/ai-job-logic-step.js +395 -0
  131. package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
  132. package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
  133. package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
  134. package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
  135. package/src/lib/jobs/ai-job-page-language.d.ts +149 -0
  136. package/src/lib/jobs/ai-job-page-language.js +355 -0
  137. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  138. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  139. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  140. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  141. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  142. package/src/lib/jobs/ai-job-page-step.js +111 -20
  143. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  144. package/src/lib/jobs/ai-job-plan-step.d.ts +27 -2
  145. package/src/lib/jobs/ai-job-plan-step.js +87 -13
  146. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  147. package/src/lib/jobs/ai-job-site-step.d.ts +19 -1
  148. package/src/lib/jobs/ai-job-site-step.js +220 -61
  149. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  150. package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
  151. package/src/lib/jobs/ai-job-text-step.js +12 -0
  152. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  153. package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
  154. package/src/lib/jobs/ai-job-workflow-step.js +320 -15
  155. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  156. package/src/lib/jobs/ai-jobs.d.ts +80 -2
  157. package/src/lib/jobs/ai-jobs.js +391 -18
  158. package/src/lib/jobs/ai-jobs.js.map +1 -1
  159. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  160. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  161. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  162. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  163. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  164. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  165. package/src/lib/jobs/ai-logic-records.d.ts +40 -0
  166. package/src/lib/jobs/ai-logic-records.js +55 -0
  167. package/src/lib/jobs/ai-logic-records.js.map +1 -0
  168. package/src/lib/jobs/ai-site-publish.d.ts +7 -0
  169. package/src/lib/jobs/ai-site-publish.js +9 -0
  170. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  171. package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
  172. package/src/lib/jobs/ai-workflow-records.js +40 -1
  173. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  174. package/src/lib/layout-language/ai-layout-compiler.d.ts +145 -0
  175. package/src/lib/layout-language/ai-layout-compiler.js +1148 -0
  176. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  177. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  178. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  179. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  180. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  181. package/src/lib/layout-language/ai-layout-frame.js +457 -0
  182. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  183. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  184. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  185. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  186. package/src/lib/layout-language/ai-layout-language.d.ts +161 -0
  187. package/src/lib/layout-language/ai-layout-language.js +446 -0
  188. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  189. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  190. package/src/lib/layout-language/ai-layout-links.js +109 -0
  191. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  192. package/src/lib/layout-language/ai-layout-store.d.ts +39 -0
  193. package/src/lib/layout-language/ai-layout-store.js +69 -0
  194. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  195. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  196. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  197. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  198. package/src/lib/lazy-widget.d.ts +45 -0
  199. package/src/lib/lazy-widget.js +53 -0
  200. package/src/lib/lazy-widget.js.map +1 -0
  201. package/src/lib/model/ai-automation-draft.d.ts +18 -0
  202. package/src/lib/model/ai-automation-draft.js +86 -0
  203. package/src/lib/model/ai-automation-draft.js.map +1 -1
  204. package/src/lib/model/ai-build-job.d.ts +201 -0
  205. package/src/lib/model/ai-build-job.js +472 -0
  206. package/src/lib/model/ai-build-job.js.map +1 -0
  207. package/src/lib/model/ai-build-plan.d.ts +61 -0
  208. package/src/lib/model/ai-build-plan.js +154 -9
  209. package/src/lib/model/ai-build-plan.js.map +1 -1
  210. package/src/lib/model/ai-build-progress.d.ts +73 -0
  211. package/src/lib/model/ai-build-progress.js +170 -0
  212. package/src/lib/model/ai-build-progress.js.map +1 -0
  213. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  214. package/src/lib/model/ai-host-publish-context.js +104 -0
  215. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  216. package/src/lib/model/ai-insight.d.ts +20 -10
  217. package/src/lib/model/ai-insight.js +72 -9
  218. package/src/lib/model/ai-insight.js.map +1 -1
  219. package/src/lib/model/ai-job-activity.d.ts +22 -0
  220. package/src/lib/model/ai-job-activity.js +25 -2
  221. package/src/lib/model/ai-job-activity.js.map +1 -1
  222. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  223. package/src/lib/model/ai-job-failure-copy.js +56 -1
  224. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  225. package/src/lib/model/ai-job-notice.d.ts +3 -1
  226. package/src/lib/model/ai-job-notice.js +31 -9
  227. package/src/lib/model/ai-job-notice.js.map +1 -1
  228. package/src/lib/model/ai-jobs.types.d.ts +99 -2
  229. package/src/lib/model/ai-jobs.types.js +3 -1
  230. package/src/lib/model/ai-jobs.types.js.map +1 -1
  231. package/src/lib/model/ai-logic-job.d.ts +108 -0
  232. package/src/lib/model/ai-logic-job.js +327 -0
  233. package/src/lib/model/ai-logic-job.js.map +1 -0
  234. package/src/lib/model/ai-media-credits.d.ts +52 -0
  235. package/src/lib/model/ai-media-credits.js +61 -0
  236. package/src/lib/model/ai-media-credits.js.map +1 -0
  237. package/src/lib/model/ai-overlay-copy.d.ts +114 -0
  238. package/src/lib/model/ai-overlay-copy.js +175 -0
  239. package/src/lib/model/ai-overlay-copy.js.map +1 -0
  240. package/src/lib/model/ai-site-build-progress.d.ts +25 -4
  241. package/src/lib/model/ai-site-build-progress.js +194 -11
  242. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  243. package/src/lib/model/ai-site-job.d.ts +8 -0
  244. package/src/lib/model/ai-site-job.js +9 -0
  245. package/src/lib/model/ai-site-job.js.map +1 -1
  246. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  247. package/src/lib/model/ai-site-start.d.ts +5 -2
  248. package/src/lib/model/ai-site-start.js +5 -2
  249. package/src/lib/model/ai-site-start.js.map +1 -1
  250. package/src/lib/model/ai-svg.d.ts +69 -0
  251. package/src/lib/model/ai-svg.js +265 -0
  252. package/src/lib/model/ai-svg.js.map +1 -0
  253. package/src/lib/model/ai-usage-by-user.d.ts +3 -2
  254. package/src/lib/model/ai-usage-by-user.js.map +1 -1
  255. package/src/lib/model/ai-workflow-job.d.ts +92 -4
  256. package/src/lib/model/ai-workflow-job.js +124 -7
  257. package/src/lib/model/ai-workflow-job.js.map +1 -1
  258. package/src/lib/model/assist-build.d.ts +58 -0
  259. package/src/lib/model/assist-build.js +52 -0
  260. package/src/lib/model/assist-build.js.map +1 -0
  261. package/src/lib/model/assist-edit.d.ts +48 -3
  262. package/src/lib/model/assist-edit.js +48 -4
  263. package/src/lib/model/assist-edit.js.map +1 -1
  264. package/src/lib/plugin-config.js +2 -0
  265. package/src/lib/plugin-config.js.map +1 -1
  266. package/src/lib/plugin.js +308 -47
  267. package/src/lib/plugin.js.map +1 -1
  268. package/src/lib/providers/catalog.d.ts +82 -5
  269. package/src/lib/providers/catalog.js +83 -12
  270. package/src/lib/providers/catalog.js.map +1 -1
  271. package/src/lib/providers/image-contract.d.ts +116 -0
  272. package/src/lib/providers/image-contract.js +74 -0
  273. package/src/lib/providers/image-contract.js.map +1 -0
  274. package/src/lib/providers/model-choice.js +11 -0
  275. package/src/lib/providers/model-choice.js.map +1 -1
  276. package/src/lib/providers/routing.js +18 -0
  277. package/src/lib/providers/routing.js.map +1 -1
  278. package/src/lib/providers/vertex-image.d.ts +121 -0
  279. package/src/lib/providers/vertex-image.js +312 -0
  280. package/src/lib/providers/vertex-image.js.map +1 -0
  281. package/src/lib/runtime/ai-doctrine-validators.d.ts +101 -0
  282. package/src/lib/runtime/ai-doctrine-validators.js +525 -4
  283. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  284. package/src/lib/runtime/ai-doctrine.js +21 -6
  285. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  286. package/src/lib/runtime/ai-eval-live.js +4 -0
  287. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  288. package/src/lib/runtime/ai-eval.d.ts +15 -1
  289. package/src/lib/runtime/ai-eval.js +52 -1
  290. package/src/lib/runtime/ai-eval.js.map +1 -1
  291. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  292. package/src/lib/runtime/ai-icon-library.js +254 -0
  293. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  294. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  295. package/src/lib/runtime/ai-node-tree.js +30 -4
  296. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  297. package/src/lib/runtime/ai-palette.d.ts +6 -0
  298. package/src/lib/runtime/ai-palette.generated.js +70 -0
  299. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  300. package/src/lib/runtime/ai-palette.js.map +1 -1
  301. package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
  302. package/src/lib/runtime/experiment-variant-copy.js +102 -0
  303. package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
  304. package/src/lib/server/ai-experiment-versions.d.ts +51 -0
  305. package/src/lib/server/ai-experiment-versions.js +270 -0
  306. package/src/lib/server/ai-experiment-versions.js.map +1 -0
  307. package/src/lib/server/ai-jobs-resume.js +74 -4
  308. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  309. package/src/lib/server/ai-media-image.d.ts +66 -0
  310. package/src/lib/server/ai-media-image.js +521 -0
  311. package/src/lib/server/ai-media-image.js.map +1 -0
  312. package/src/lib/server/ai-media-svg.d.ts +91 -0
  313. package/src/lib/server/ai-media-svg.js +186 -0
  314. package/src/lib/server/ai-media-svg.js.map +1 -0
  315. package/src/lib/server/assist-build.d.ts +54 -0
  316. package/src/lib/server/assist-build.js +121 -0
  317. package/src/lib/server/assist-build.js.map +1 -0
  318. package/src/lib/server/assist-chat.js +108 -12
  319. package/src/lib/server/assist-chat.js.map +1 -1
  320. package/src/lib/server/assist-edit.js +213 -3
  321. package/src/lib/server/assist-edit.js.map +1 -1
  322. package/src/lib/server/assist-view-context.js +242 -4
  323. package/src/lib/server/assist-view-context.js.map +1 -1
  324. package/src/lib/server.js +26 -0
  325. package/src/lib/server.js.map +1 -1
  326. package/src/lib/tools/ai-logic-tool.d.ts +27 -0
  327. package/src/lib/tools/ai-logic-tool.js +207 -0
  328. package/src/lib/tools/ai-logic-tool.js.map +1 -0
  329. package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
  330. package/src/lib/tools/ai-overlay-tool.js +107 -0
  331. package/src/lib/tools/ai-overlay-tool.js.map +1 -0
  332. package/src/lib/tools/ai-workflow-tool.d.ts +17 -2
  333. package/src/lib/tools/ai-workflow-tool.js +47 -14
  334. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  335. package/src/lib/usage/assist-job-refund.d.ts +28 -1
  336. package/src/lib/usage/assist-job-refund.js +44 -9
  337. package/src/lib/usage/assist-job-refund.js.map +1 -1
  338. package/src/lib/usage/assist-usage.d.ts +5 -0
  339. package/src/lib/usage/assist-usage.js +2 -0
  340. package/src/lib/usage/assist-usage.js.map +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-build-step.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 { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport { resolveEffectivePlan } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n AI_BUILD_LIMITS,\n AI_BUILD_PUBLISH_INPUT,\n aiArticle,\n aiBuildCreditEstimate,\n aiBuildDegradation,\n aiBuildInitialLedger,\n aiBuildItemDelivered,\n aiBuildItemOpen,\n aiBuildNextUnit,\n aiBuildOrder,\n aiBuildPlacedItemLines,\n aiBuildPlanShapeRefusal,\n aiBuildUnits,\n type AiBuildOps,\n type AiBuildUnit,\n} from '../model/ai-build-job'\nimport { isAiPlanNewRef, type AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport type {\n AiJob,\n AiJobItemFailure,\n AiJobItemLedger,\n AiJobKind,\n AiJobOutput,\n AiJobPlan,\n} from '../model/ai-jobs.types'\nimport { AI_SITE_MAX_SECTIONS } from '../model/ai-site-job'\nimport { FREE_AI_TASTE_CREDITS_PER_MONTH } from '../plan-entitlements'\nimport { aiModelForStep } from '../providers/routing'\nimport {\n AI_BUILD_UNIT_EMPTY_COPY,\n aiBuildBuiltRefs,\n aiUnitErrorRetryable,\n aiUnitFailure,\n aiUnitSpend,\n} from './ai-build-unit-outcome'\nimport { assistCreditsFromUsd } from '../usage/assist-credits'\nimport { ASSIST_RETURNED_USD_FIELD, assistSpendAfterReturnsUsd } from '../usage/assist-credit-returns'\nimport { freeAccountUsageRef, freeAssistAccount, type AssistMeteredOrg } from '../usage/assist-free-taste'\nimport { aiBuildOps, type AiBuildOpsContext } from './ai-build-capabilities'\nimport {\n aiJobAdmissionRefusal,\n registerAiJobAdmission,\n type AiJobAdmission,\n type AiJobAdmissionContext,\n} from './ai-job-admission'\nimport { aiOriginJobId } from './ai-job-draft-ids'\nimport { readAiDraftNodes } from './ai-job-drafts'\nimport { aiConfirmedPlan, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_PAGE_STEP_MINIMUM_MS } from './ai-job-page-budget'\nimport {\n aiPluginDraftAdmissionRefusal,\n aiPluginDraftWriter,\n type AiPluginDraftWriterLookup,\n} from './ai-job-plugin-drafts'\nimport { aiSitePageWritten, aiSiteResolvedRef } from './ai-job-site-step'\nimport { AI_LAYOUT_SITE_PAGES_INPUT, aiLayoutSitePagesOfPlan } from './ai-job-layout-site-pages'\nimport { AI_LAYOUT_FORM_PAGE_INPUT, AI_LAYOUT_LANGUAGE_INPUT, aiLayoutFormPageOfPlan } from './ai-job-page-language'\nimport {\n AI_JOB_BRIEF_MAX_CHARS,\n type AiJobItemOutcome,\n type AiJobStepOutcome,\n type AiJobStepRunner,\n} from './ai-job-text-step'\nimport { aiJobPublishesBuild, aiPublishGuidedSite } from './ai-site-publish'\nimport {\n AI_PLANNED_JOB_KINDS,\n aiJobStepRunMinimumMs,\n aiJobStepRunnerFor,\n registerAiJobStep,\n registerAiJobStepPasses,\n} from './ai-jobs'\n\n/**\n * The build step (AGL-3616): the generation step of a `build` job, run once\n * the member confirmed the plan their request produced.\n *\n * Like the site scaffold it generalizes, it builds nothing itself and asks no\n * model of its own. Each pass takes the next unit in dependency order and\n * hands it to what builds that kind of thing — a page, layout, form,\n * component, email design or template to this plugin's own step, under a job\n * derived for the unit; another plugin's resource to that plugin's writer on\n * the resource-draft seam, with the content the owner's capability derives\n * from the item's arguments. One unit, one pass, one recorded spend.\n *\n * ── Settled item by item ────────────────────────────────────────────────\n *\n * Where a unit stands is read from the job's ITEM LEDGER, never from counting\n * outputs: the machine writes each pass's row in the transaction that\n * records its spend (`recordStep`). A unit that fails is a FAILED ROW, not a\n * failed job: the pass reports it, the machine gives back that item's spend\n * where the failure was ours, and the next pass builds the next unit. What\n * depended on it is built without it, or skipped where it cannot stand\n * without it (`aiBuildDegradation`). The machine ends the job when nothing is\n * left: `done` when anything was delivered, `failed` — its planning given\n * back too — when nothing was.\n *\n * ── Drafts, unless asked and confirmed ───────────────────────────────────\n *\n * Every unit writes the draft its own kind writes, which nothing publishes.\n * Only a build whose request asked to publish AND whose member ticked the\n * plan card's box puts its built pages live, once, on its last pass, through\n * the guided start's publish.\n */\n\n/** What a build answers with no confirmed plan to build. */\nexport const AI_BUILD_NO_PLAN_COPY = 'This build has no confirmed plan to build.'\n\n/** What a unit says when nothing on this site builds its operation any more. */\nexport const aiBuildUnavailableCopy = (noun: string): string =>\n `Not built: ${aiArticle(noun)} cannot be made on this site right now.`\n\n\n/** What a page that was reported built but holds none of its plan says. */\nexport const AI_BUILD_PAGE_NOT_WRITTEN_COPY = 'The page was not written from its plan.'\n\n\n/**\n * The most passes a build's step may take: every unit at a page's worst — its\n * sections and its listing — so a runner that never finishes is still\n * bounded while the largest plan is not. Try again starts the count over.\n */\nexport const AI_BUILD_MAX_PASSES = AI_BUILD_LIMITS.units * (AI_SITE_MAX_SECTIONS + 1)\n\n/** The job kind a creation of the build is built by. */\nconst CREATION_JOB_KINDS: Partial<Record<AiBuildPlanCreateKind, AiJobKind>> = {\n layout: 'layout',\n form: 'form',\n component: 'component',\n email: 'email',\n}\n\n/** The job kind a unit is built by, where an AI runner builds it. */\nexport function aiBuildUnitJobKind(unit: AiBuildUnit, ops: AiBuildOps): AiJobKind | null {\n if (unit.screen) return 'page'\n if (unit.creation) return CREATION_JOB_KINDS[unit.creation.kind] ?? null\n const runnerKind = ops.get(unit.op)?.runnerKind\n return runnerKind ? (runnerKind as AiJobKind) : null\n}\n\n/** The id a unit's job, and so its draft, is named by. */\nexport function aiBuildUnitJobId(job: Pick<AiJob, '$id'>, unit: AiBuildUnit): string {\n return unit.creation?.id ?? unit.screen?.id ?? unit.item?.id ?? `${job.$id}-${unit.slot}`\n}\n\n/**\n * The job a unit is built under (generalizing `aiSiteUnitJob`): this job's\n * org, site, creator and model; the kind that builds the unit; a plan\n * narrowed to the unit, its `new:` references resolved to what was built and\n * the failed ones left out; and a brief that says what the unit is, what it\n * is built without, and which blocks its sections place.\n */\nexport function aiBuildUnitJob(\n job: AiJob,\n unit: AiBuildUnit,\n context: {\n kind: AiJobKind\n units: readonly AiBuildUnit[]\n ledger: readonly AiJobItemLedger[]\n ops: AiBuildOps\n briefLines?: readonly string[]\n },\n): AiJob {\n const plan = job.plan as AiJobPlan\n const built = aiBuildBuiltRefs(context.units, context.ledger, job.outputs ?? [])\n const labels = { ...(plan.labels ?? {}) }\n for (const entry of built.values()) labels[entry.id] = entry.label\n const brief = [job.brief.trim()]\n const unitPlan: AiJobPlan = { ...plan, labels, reuse: [...plan.reuse], create: [], screens: [] }\n delete unitPlan.items\n const placedByScreens = new Set(plan.screens.flatMap((screen) => screen.sections.flatMap((section) => section.uses)))\n if (unit.creation) {\n unitPlan.reuse = plan.reuse.filter((entry) => !placedByScreens.has(entry.id))\n unitPlan.create = [{ ...unit.creation, duplicateOf: aiSiteResolvedRef(unit.creation.duplicateOf, built) }]\n brief.push(`Build the ${unit.creation.kind} “${unit.creation.name}”: ${unit.creation.why}`)\n }\n if (unit.screen) {\n const screen = unit.screen\n for (const entry of built.values()) {\n unitPlan.reuse.push({ kind: entry.kind, id: entry.id, purpose: `the ${entry.kind} this request built` })\n }\n let builtLayout: string | null = null\n for (const entry of built.values()) if (entry.kind === 'layout') builtLayout = builtLayout ?? entry.id\n unitPlan.screens = [\n {\n ...screen,\n // A page whose layout failed renders inside the site's own: the page\n // step falls back to it when the plan names none.\n layout: aiSiteResolvedRef(screen.layout, built) ?? (isAiPlanNewRef(screen.layout) ? builtLayout : null),\n template: aiSiteResolvedRef(screen.template, built),\n duplicateOf: aiSiteResolvedRef(screen.duplicateOf, built),\n record: screen.record\n ? { ...screen.record, dataset: aiSiteResolvedRef(screen.record.dataset, built) ?? screen.record.dataset }\n : null,\n sections: screen.sections.map((section) => ({\n ...section,\n uses: section.uses\n .map((ref) => aiSiteResolvedRef(ref, built))\n .filter((ref): ref is string => !!ref),\n })),\n },\n ]\n brief.push(`Build the page “${screen.title}”, at ${screen.slug}.`)\n brief.push(...aiBuildPlacedItemLines(unit, { units: context.units, ledger: context.ledger, ops: context.ops }))\n }\n const item = unit.item\n if (item) {\n const noun = context.ops.get(item.op)?.noun ?? item.op\n brief.push(`Build the ${noun} “${item.name}”${item.why ? `: ${item.why}` : '.'}`)\n // A planned kind's step builds the plan's first creation of its kind.\n unitPlan.create = AI_PLANNED_JOB_KINDS.includes(context.kind)\n ? [{ kind: context.kind as AiBuildPlanCreateKind, name: item.name, why: item.why, duplicateOf: null, fields: [], ...(item.id ? { id: item.id } : {}) }]\n : []\n }\n brief.push(...(context.briefLines ?? []))\n const inputs: Record<string, unknown> = {\n ...job.inputs,\n ...(item?.args ?? {}),\n originJobId: aiOriginJobId(job),\n }\n delete inputs[AI_BUILD_PUBLISH_INPUT]\n // A build's pages and layouts are designed in the layout language and\n // compiled (AGL-3660), told the pages the build plans and where its form is.\n if (context.kind === 'page' || context.kind === 'layout') {\n inputs[AI_LAYOUT_LANGUAGE_INPUT] = true\n const pages = aiLayoutSitePagesOfPlan(plan.screens)\n if (pages.length) inputs[AI_LAYOUT_SITE_PAGES_INPUT] = pages\n const formPage = aiLayoutFormPageOfPlan(plan)\n if (formPage) inputs[AI_LAYOUT_FORM_PAGE_INPUT] = formPage\n }\n return {\n ...job,\n $id: aiBuildUnitJobId(job, unit),\n kind: context.kind,\n steps: [],\n outputs: [],\n items: null,\n plan: item && !AI_PLANNED_JOB_KINDS.includes(context.kind) ? null : unitPlan,\n inputs,\n brief: brief.join('\\n').slice(0, AI_JOB_BRIEF_MAX_CHARS),\n }\n}\n\n/** The unit's failure, for its row: ours or not, why, in customer-safe words. */\nfunction failed(\n slot: string,\n failure: AiJobItemFailure,\n extra: Partial<AiJobItemOutcome> = {},\n): AiJobItemOutcome {\n return { slot, status: 'failed', failure, ...extra }\n}\n\nexport interface AiJobBuildStepDeps {\n /** The runner registry; specs hand in fakes. */\n runnerFor?: typeof aiJobStepRunnerFor\n /** The draft writer registry; specs hand in fakes. */\n writerFor?: AiPluginDraftWriterLookup\n /** The operations this site may use; the registry and its gates otherwise. */\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n /** A runner kind's own admission, asked before its item runs; the registry's otherwise. */\n admissionFor?: typeof aiJobAdmissionRefusal\n /** Another plugin's admission for its writer; the seam's otherwise. */\n pluginAdmission?: typeof aiPluginDraftAdmissionRefusal\n /** The draft reader a built page is checked through. */\n readNodes?: typeof readAiDraftNodes\n /** The publish a build asked for and confirmed. */\n publish?: typeof aiPublishGuidedSite\n}\n\n/** Whether a build's workspace spends the Free taste. */\nexport function aiBuildFreeTaste(org: object | null | undefined): boolean {\n return Boolean(org) && resolveEffectivePlan(org as never) === 'free'\n}\n\n/** The site document a gate reads, or `null`. */\nasync function readHost(firestore: FirebaseFirestore.Firestore, hostId: string | null) {\n if (!hostId) return null\n return ((await firestore.collection('hosts').doc(hostId).get()).data() ?? null) as Record<string, unknown> | null\n}\n\nexport function createAiJobBuildStep(deps: AiJobBuildStepDeps = {}): AiJobStepRunner {\n const runnerFor = deps.runnerFor ?? aiJobStepRunnerFor\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const admissionFor = deps.admissionFor ?? aiJobAdmissionRefusal\n const pluginAdmission = deps.pluginAdmission ?? aiPluginDraftAdmissionRefusal\n const readNodes = deps.readNodes ?? readAiDraftNodes\n const publish = deps.publish ?? aiPublishGuidedSite\n\n return async (context): Promise<AiJobStepOutcome> => {\n const { job, firestore } = context\n const model = context.modelFor?.('job.plan') ?? aiModelForStep('job.plan')\n const plan = aiConfirmedPlan(job)\n if (!plan) return { ...aiUnspentOutcome(model), failure: AI_BUILD_NO_PLAN_COPY }\n const org = (context.org ?? null) as Partial<AglynOrgBilling> | null\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const starting = !job.items?.length\n const ledger = starting ? aiBuildInitialLedger(units) : (job.items as AiJobItemLedger[])\n const init = starting ? { items: ledger } : {}\n const unit = aiBuildNextUnit(ordered, ledger)\n const rows = new Map(ledger.map((row) => [row.slot, row]))\n const host = await readHost(firestore, job.hostId ?? null)\n const ops = await opsFor({\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n org,\n host,\n freeTaste: aiBuildFreeTaste(org),\n })\n\n /** Whether units other than this one are still to build. */\n const othersOpen = (slot: string) =>\n ordered.some((one) => one.slot !== slot && aiBuildItemOpen(rows.get(one.slot) ?? { status: 'pending' }))\n\n /** The build's last pass puts its pages live, where it was asked and confirmed. */\n const finish = async (outcome: AiJobStepOutcome, extraPages: readonly AiJobOutput[] = []): Promise<AiJobStepOutcome> => {\n if (!aiJobPublishesBuild(job) || job.sitePublish || !job.hostId) return outcome\n const pageIds = new Set(\n units\n .filter((one) => one.screen && aiBuildItemDelivered(rows.get(one.slot) ?? { status: 'pending' }))\n .flatMap((one) => rows.get(one.slot)?.outputs ?? []),\n )\n const pages = [\n ...(job.outputs ?? []).filter((output) => output.resource === 'screen' && pageIds.has(output.id)),\n ...extraPages,\n ]\n if (!pages.length) return outcome\n const sitePublish = await publish(firestore, { job, outputs: pages, now: context.now }).catch((error: unknown) => {\n console.error('ai build publish threw', { orgId: job.orgId, jobId: job.$id, error })\n return null\n })\n return sitePublish ? { ...outcome, sitePublish } : outcome\n }\n\n if (!unit) return finish({ ...aiUnspentOutcome(model), ...init })\n\n const settle = (item: AiJobItemOutcome, spent: AiJobStepOutcome = aiUnspentOutcome(model)): AiJobStepOutcome => ({\n ...spent,\n ...init,\n item,\n ...(item.status === 'running' || othersOpen(unit.slot) ? { continue: true } : {}),\n })\n\n // What its dependencies came to.\n const degradation = aiBuildDegradation(unit, { units, ledger, ops })\n if (degradation.skip) {\n return settle({ slot: unit.slot, status: 'skipped', note: degradation.skip, degradedBy: degradation.degradedBy })\n }\n const capability = ops.get(unit.op)\n const noun = capability?.noun ?? unit.op\n if (!capability) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const degraded = degradation.degradedBy.length > 0\n const note = degradation.notes.length ? degradation.notes.join(' ') : null\n\n // Another plugin's resource: its writer, with the content its capability derives.\n if (capability.draftResource) {\n const item = unit.item\n const writer = writerFor(capability.draftResource)\n if (!item || !writer || !job.hostId) {\n return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n }\n const admission: AiJobAdmissionContext = {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId,\n inputs: {},\n org,\n uid: job.createdBy,\n }\n const refusal = await pluginAdmission(admission, {\n kind: 'build',\n drafts: [{ resource: capability.draftResource, label: capability.noun }],\n writerFor,\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n const dependencies: Record<string, { op: string; id: string }> = {}\n for (const slot of unit.deps) {\n const dep = units.find((one) => one.slot === slot)\n const row = rows.get(slot)\n if (!dep || !row || !aiBuildItemDelivered(row) || !row.outputs.length) continue\n dependencies[`new:${dep.label}`] = { op: dep.op, id: row.outputs[0] }\n }\n const content = capability.draftContent?.({ name: item.name, args: item.args }, { hostId: job.hostId, dependencies }) ?? item.args\n const checked = writer.check(content, { hostId: job.hostId })\n if (checked.ok === false) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: checked.problems[0] ?? AI_BUILD_UNIT_EMPTY_COPY }),\n )\n }\n const id = aiBuildUnitJobId(job, unit)\n const written = await writer.write({\n orgId: job.orgId,\n hostId: job.hostId,\n uid: job.createdBy,\n org,\n now: context.now,\n id,\n name: item.name,\n content,\n })\n if (written.ok === false) {\n return settle(failed(unit.slot, { ours: false, reason: 'review', message: written.error }))\n }\n const output: AiJobOutput = {\n resource: 'draft',\n id: written.id,\n versionId: written.versionId,\n hostId: job.hostId,\n label: written.name,\n draftResource: capability.draftResource,\n note: `A draft, in ${capability.where}.`,\n }\n return settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: [written.id],\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n { ...aiUnspentOutcome(model), outputs: [output] },\n )\n }\n\n // This plugin's own runner, under a job derived for the unit.\n const kind = aiBuildUnitJobKind(unit, ops)\n const runner = kind ? runnerFor(kind) : null\n if (!kind || !runner) return settle({ slot: unit.slot, status: 'skipped', note: aiBuildUnavailableCopy(noun) })\n const derived = aiBuildUnitJob(job, unit, { kind, units, ledger, ops, briefLines: degradation.briefLines })\n // An item's kind is asked its own admission first (a campaign's plan,\n // a template's subject), once, before its first pass.\n if (unit.item && rows.get(unit.slot)?.status !== 'running') {\n const refusal = await admissionFor(kind, {\n firestore,\n orgId: job.orgId,\n hostId: job.hostId ?? null,\n inputs: derived.inputs,\n org,\n uid: job.createdBy,\n }).catch((error: unknown) => {\n console.error('ai build item admission failed', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return { status: 403 as const, error: AI_BUILD_UNIT_EMPTY_COPY }\n })\n if (refusal) return settle({ slot: unit.slot, status: 'skipped', note: `Not built: ${refusal.error}` })\n }\n\n let outcome: AiJobStepOutcome\n try {\n outcome = await runner({ ...context, job: derived })\n } catch (error) {\n // A provider that will answer later is the machine's to retry; anything\n // else is this item's failure, and the build goes on.\n if (aiUnitErrorRetryable(error)) throw error\n console.error('ai build unit threw', { orgId: job.orgId, jobId: job.$id, slot: unit.slot, error })\n return settle(failed(unit.slot, { ours: true, reason: 'provider', message: AI_BUILD_UNIT_EMPTY_COPY }))\n }\n const spent = aiUnitSpend(outcome)\n const ids = outcome.outputs.map((output) => output.id)\n const stopped = aiUnitFailure(unit.slot, outcome)\n if (stopped) return settle(stopped, spent)\n // A page counts as built only when its plan's sections are in it (AGL-3596).\n if (unit.screen && job.hostId) {\n const page = outcome.outputs.find((output) => output.resource === 'screen')\n const written =\n page !== undefined &&\n (await aiSitePageWritten(\n firestore,\n { hostId: job.hostId, unitJobId: derived.$id, screen: unit.screen, draftId: page.id },\n readNodes,\n ))\n if (!written) {\n return settle(\n failed(unit.slot, { ours: true, reason: 'step-failure', message: AI_BUILD_PAGE_NOT_WRITTEN_COPY }),\n { ...spent, outputs: outcome.outputs.filter((output) => output.resource !== 'screen') },\n )\n }\n }\n const done = settle(\n {\n slot: unit.slot,\n status: degraded ? 'degraded' : 'succeeded',\n outputs: ids,\n note,\n ...(degraded ? { degradedBy: degradation.degradedBy } : {}),\n },\n spent,\n )\n if (done.continue) return done\n // The last unit: what was built goes live where it was asked and confirmed.\n rows.set(unit.slot, { ...(rows.get(unit.slot) as AiJobItemLedger), status: degraded ? 'degraded' : 'succeeded', outputs: ids })\n return finish(done, unit.screen ? outcome.outputs.filter((output) => output.resource === 'screen') : [])\n }\n}\n\nexport const runAiJobBuildStep = createAiJobBuildStep()\n\n/**\n * The least time a build's next pass needs: what the step that builds its\n * next unit registers for the job derived for it; a page pass's when the\n * next unit is another plugin's draft, which asks no model.\n */\nexport function aiBuildJobRunMinimumMs(job: AiJob): number {\n const plan = aiConfirmedPlan(job)\n if (!plan) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const units = aiBuildUnits(plan)\n const ordered = aiBuildOrder(units) ?? units\n const ledger = job.items?.length ? job.items : aiBuildInitialLedger(units)\n const unit = aiBuildNextUnit(ordered, ledger)\n if (!unit) return AI_JOB_PAGE_STEP_MINIMUM_MS\n const kind: AiJobKind | null = unit.screen\n ? 'page'\n : unit.creation\n ? (CREATION_JOB_KINDS[unit.creation.kind] ?? null)\n : (AI_BUILD_ITEM_RUNNER_KINDS[unit.op] ?? null)\n if (!kind) return AI_JOB_PAGE_STEP_MINIMUM_MS\n return aiJobStepRunMinimumMs(aiBuildUnitJob(job, unit, { kind, units, ledger, ops: new Map() }))\n}\n\n/** The runner kinds this plugin's own item operations use, for the time a pass needs without the registry. */\nconst AI_BUILD_ITEM_RUNNER_KINDS: Readonly<Record<string, AiJobKind>> = {\n template: 'template',\n campaign: 'campaign',\n workflow: 'workflow',\n}\n\n/**\n * Why a Free build cannot be confirmed: its estimate is more than the owner's\n * Free allowance has left this month. Read off the account month the meter\n * writes, net of give-backs; a read that fails admits, since the reservation\n * still refuses at the wall.\n */\nexport async function aiBuildFreeBalanceRefusal(input: {\n firestore: FirebaseFirestore.Firestore\n org: object | null\n estimate: number\n now: Date\n}): Promise<string | null> {\n const account = freeAssistAccount(input.org as AssistMeteredOrg | null)\n if (!account?.accountUid) return null\n try {\n const month = input.now.toISOString().slice(0, 7)\n const snapshot = await freeAccountUsageRef(input.firestore, account.accountUid, month).get()\n const spent = assistCreditsFromUsd(\n assistSpendAfterReturnsUsd(snapshot.get('estCostUsd'), snapshot.get(ASSIST_RETURNED_USD_FIELD)),\n )\n const left = Math.max(0, FREE_AI_TASTE_CREDITS_PER_MONTH - spent)\n return input.estimate > left\n ? `This plan is estimated at ${input.estimate} credits, and your Free AI credits have ${left} left this month. Ask for less, or upgrade for more.`\n : null\n } catch (error) {\n console.error('ai build free balance read failed', { error })\n return null\n }\n}\n\nexport interface AiBuildAdmissionDeps {\n opsFor?: (context: AiBuildOpsContext) => Promise<AiBuildOps>\n freeBalanceRefusal?: typeof aiBuildFreeBalanceRefusal\n ownerOf?: typeof resolveOrgIdForHost\n now?: () => Date\n}\n\n/**\n * A build is admitted for a site of the job's own org where a page can be\n * built; and confirmed only for a plan it can build here — its shape, its\n * operations as this site has them, and on the Free taste an estimate the\n * month's Free credits still cover.\n */\nexport function createAiBuildJobAdmission(deps: AiBuildAdmissionDeps = {}): AiJobAdmission {\n const opsFor = deps.opsFor ?? ((context) => aiBuildOps(context))\n const freeBalanceRefusal = deps.freeBalanceRefusal ?? aiBuildFreeBalanceRefusal\n const ownerOf = deps.ownerOf ?? resolveOrgIdForHost\n return async (context) => {\n if (!context.hostId) return { status: 400, error: 'Open the site to build on before asking Assist to build' }\n const owner = await ownerOf(context.hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n if (!aiJobStepRunnerFor('page')) return { status: 400, error: 'Building from a request is not available yet.' }\n const plan = context.plan\n if (!plan) return null\n const org = context.org as Partial<AglynOrgBilling> | null\n const freeTaste = aiBuildFreeTaste(org)\n const host = await readHost(context.firestore, context.hostId)\n const ops = await opsFor({ orgId: context.orgId, hostId: context.hostId, org, host, freeTaste })\n const shape = aiBuildPlanShapeRefusal(plan, { freeTaste, ops })\n if (shape) return { status: 400, error: shape }\n if (freeTaste) {\n const refusal = await freeBalanceRefusal({\n firestore: context.firestore,\n org,\n estimate: aiBuildCreditEstimate(plan, { ops }),\n now: deps.now?.() ?? new Date(),\n })\n if (refusal) return { status: 403, error: refusal }\n }\n return null\n }\n}\n\nexport const aiBuildJobAdmission = createAiBuildJobAdmission()\n\n/**\n * Registers the build with the least time a pass needs, the time its next\n * pass needs, the passes its largest plan may take, and its admission.\n */\nexport function registerAiBuildJob(): void {\n registerAiJobStep('build', runAiJobBuildStep, {\n minimumMs: AI_JOB_PAGE_STEP_MINIMUM_MS,\n minimumMsFor: aiBuildJobRunMinimumMs,\n })\n registerAiJobStepPasses('build', AI_BUILD_MAX_PASSES)\n registerAiJobAdmission('build', aiBuildJobAdmission)\n}\n"],"names":["resolveEffectivePlan","resolveOrgIdForHost","AI_BUILD_LIMITS","AI_BUILD_PUBLISH_INPUT","aiArticle","aiBuildCreditEstimate","aiBuildDegradation","aiBuildInitialLedger","aiBuildItemDelivered","aiBuildItemOpen","aiBuildNextUnit","aiBuildOrder","aiBuildPlacedItemLines","aiBuildPlanShapeRefusal","aiBuildUnits","isAiPlanNewRef","AI_SITE_MAX_SECTIONS","FREE_AI_TASTE_CREDITS_PER_MONTH","aiModelForStep","AI_BUILD_UNIT_EMPTY_COPY","aiBuildBuiltRefs","aiUnitErrorRetryable","aiUnitFailure","aiUnitSpend","assistCreditsFromUsd","ASSIST_RETURNED_USD_FIELD","assistSpendAfterReturnsUsd","freeAccountUsageRef","freeAssistAccount","aiBuildOps","aiJobAdmissionRefusal","registerAiJobAdmission","aiOriginJobId","readAiDraftNodes","aiConfirmedPlan","aiUnspentOutcome","AI_JOB_PAGE_STEP_MINIMUM_MS","aiPluginDraftAdmissionRefusal","aiPluginDraftWriter","aiSitePageWritten","aiSiteResolvedRef","AI_LAYOUT_SITE_PAGES_INPUT","aiLayoutSitePagesOfPlan","AI_LAYOUT_FORM_PAGE_INPUT","AI_LAYOUT_LANGUAGE_INPUT","aiLayoutFormPageOfPlan","AI_JOB_BRIEF_MAX_CHARS","aiJobPublishesBuild","aiPublishGuidedSite","AI_PLANNED_JOB_KINDS","aiJobStepRunMinimumMs","aiJobStepRunnerFor","registerAiJobStep","registerAiJobStepPasses","AI_BUILD_NO_PLAN_COPY","aiBuildUnavailableCopy","noun","AI_BUILD_PAGE_NOT_WRITTEN_COPY","AI_BUILD_MAX_PASSES","units","CREATION_JOB_KINDS","layout","form","component","email","aiBuildUnitJobKind","unit","ops","screen","creation","kind","runnerKind","get","op","aiBuildUnitJobId","job","id","item","$id","slot","aiBuildUnitJob","context","plan","built","ledger","outputs","labels","entry","values","label","brief","trim","unitPlan","reuse","create","screens","items","placedByScreens","Set","flatMap","sections","section","uses","filter","has","duplicateOf","push","name","why","purpose","builtLayout","template","record","dataset","map","ref","title","slug","includes","fields","briefLines","inputs","args","originJobId","pages","length","formPage","steps","join","slice","failed","failure","extra","status","aiBuildFreeTaste","org","Boolean","readHost","firestore","hostId","collection","doc","data","createAiJobBuildStep","deps","runnerFor","writerFor","opsFor","admissionFor","pluginAdmission","readNodes","publish","rows","model","modelFor","ordered","starting","init","Map","row","host","orgId","freeTaste","othersOpen","some","one","finish","outcome","extraPages","sitePublish","pageIds","output","resource","now","catch","error","console","jobId","settle","spent","continue","degradation","skip","note","degradedBy","capability","degraded","notes","draftResource","writer","admission","uid","createdBy","refusal","drafts","dependencies","dep","find","content","draftContent","checked","check","ok","ours","reason","message","problems","written","write","versionId","where","runner","derived","ids","stopped","page","undefined","unitJobId","draftId","done","set","runAiJobBuildStep","aiBuildJobRunMinimumMs","AI_BUILD_ITEM_RUNNER_KINDS","campaign","workflow","aiBuildFreeBalanceRefusal","input","account","accountUid","month","toISOString","snapshot","left","Math","max","estimate","createAiBuildJobAdmission","freeBalanceRefusal","ownerOf","owner","shape","Date","aiBuildJobAdmission","registerAiBuildJob","minimumMs","minimumMsFor"],"mappings":"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"}
@@ -119,6 +119,10 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
119
119
  },
120
120
  component: {
121
121
  screens: false
122
+ },
123
+ // A build names every draft its plan decides, its items' too (AGL-3616).
124
+ build: {
125
+ screens: true
122
126
  }
123
127
  };
124
128
  /**
@@ -137,7 +141,11 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
137
141
  screens: units.screens ? plan.screens.map((screen)=>_extends({}, screen, {
138
142
  id: createResourceUid()
139
143
  })) : plan.screens
140
- });
144
+ }, plan.items ? {
145
+ items: plan.items.map((item)=>_extends({}, item, {
146
+ id: createResourceUid()
147
+ }))
148
+ } : {});
141
149
  }
142
150
  /**
143
151
  * The job the member started, whichever unit is running (AGL-3596): a unit's
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-draft-ids.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 { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport type { AiBuildPlan, AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport type { AiJob, AiJobDraftSlot, AiJobKind } from '../model/ai-jobs.types'\n\n/**\n * The ids a job's drafts are written under (AGL-3079).\n *\n * A draft a job writes is the resource a member would otherwise have made in\n * the console, so it is named the way the console names one: by\n * `createResourceUid()`. Each id is minted ONCE and recorded on the job before\n * the draft is written. That record is what lets a step run again safely: a\n * run cut off after its write reads the same id, finds the draft, and reports\n * it rather than writing a second.\n *\n * Where an id is recorded follows what decides the draft exists:\n *\n * - A draft the job's KIND always writes — a layout job's layout, a page\n * job's screen, a campaign's design and campaign — is minted when the job\n * is created, on the step that writes it (`AiJobStep.draftIds`).\n * - A draft the PLAN decides — what a page job's plan creates, a scaffold's\n * layout, form and pages — is minted when the plan is kept, on the plan's\n * own entry (`AiBuildPlanCreate.id`, `AiBuildPlanScreen.id`). Those are\n * built one unit a pass by the step that owns their kind, under a job named\n * by the entry's id (`aiSiteUnitJob`).\n *\n * A job whose step records no id names its draft by the job's own id, which\n * is the id every draft of such a job was written under.\n */\n\n/** The drafts each kind's step writes of its own, whatever its plan says. */\nexport const AI_JOB_DRAFT_SLOTS: Readonly<Partial<Record<AiJobKind, readonly AiJobDraftSlot[]>>> = {\n page: ['screen'],\n layout: ['layout'],\n template: ['template'],\n form: ['form'],\n component: ['component'],\n email: ['email'],\n campaign: ['email', 'campaign'],\n workflow: ['workflow'],\n // The welcome email a scaffold builds; its pages and creations are its plan's.\n site: ['email'],\n}\n\n/** Fresh ids for the drafts a job of this kind writes of its own; `null` for a kind that writes none. */\nexport function aiMintJobDraftIds(kind: AiJobKind): Partial<Record<AiJobDraftSlot, string>> | null {\n const slots = AI_JOB_DRAFT_SLOTS[kind]\n if (!slots?.length) return null\n return Object.fromEntries(slots.map((slot) => [slot, createResourceUid()]))\n}\n\n/** The id recorded on the job's steps for its draft of `slot`; `null` where none is. */\nexport function aiRecordedJobDraftId(job: Pick<AiJob, 'steps'>, slot: AiJobDraftSlot): string | null {\n for (const step of job.steps ?? []) {\n const id = step.draftIds?.[slot]\n if (typeof id === 'string' && id) return id\n }\n return null\n}\n\n/**\n * The id the job writes its draft of `slot` under, and finds it by when the\n * step runs again: the id recorded on its step, else the job's own id — which\n * names a unit's job by its plan entry's id, and a job whose step records none.\n */\nexport function aiJobDraftId(job: Pick<AiJob, '$id' | 'steps'>, slot: AiJobDraftSlot): string {\n return aiRecordedJobDraftId(job, slot) ?? job.$id\n}\n\n/** The creations a job writes as documents: a theme change is a proposal, and no job writes a dataset. */\nconst DRAFT_CREATION_KINDS: ReadonlySet<AiBuildPlanCreateKind> = new Set<AiBuildPlanCreateKind>([\n 'component',\n 'form',\n 'layout',\n 'template',\n 'email',\n])\n\n/** The kinds whose step builds what their plan names, one unit a draft, and whether that includes its screens. */\nconst UNIT_PLAN_KINDS: Readonly<Partial<Record<AiJobKind, { screens: boolean }>>> = {\n // A page job's own screen is its kind's draft; its plan's creations are units.\n page: { screens: false },\n site: { screens: true },\n // So are the components and forms a template, layout or component job\n // builds before its own record (AGL-3143 §15).\n template: { screens: false },\n layout: { screens: false },\n component: { screens: false },\n}\n\n/**\n * The plan as a job of `kind` keeps it: for a kind that builds what its plan\n * names, a fresh id on every creation it writes as a document and, for a\n * scaffold, on every screen. An id the plan already carried is replaced —\n * a plan reused from another job names that job's drafts — so the drafts\n * this job builds are its own. Any other kind's plan is kept as it is.\n */\nexport function aiPlanWithDraftIds<T extends AiBuildPlan>(kind: AiJobKind, plan: T): T {\n const units = UNIT_PLAN_KINDS[kind]\n if (!units) return plan\n return {\n ...plan,\n create: plan.create.map((entry) =>\n DRAFT_CREATION_KINDS.has(entry.kind) ? { ...entry, id: createResourceUid() } : entry,\n ),\n screens: units.screens ? plan.screens.map((screen) => ({ ...screen, id: createResourceUid() })) : plan.screens,\n }\n}\n\n/**\n * The job the member started, whichever unit is running (AGL-3596): a unit's\n * derived job carries it as `inputs.originJobId`, and a job a member started\n * is its own. What a draft is stamped with as `aiJobId`.\n */\nexport function aiOriginJobId(job: Pick<AiJob, '$id' | 'inputs'>): string {\n const origin = job.inputs?.['originJobId']\n return typeof origin === 'string' && origin ? origin : job.$id\n}\n"],"names":["createResourceUid","AI_JOB_DRAFT_SLOTS","page","layout","template","form","component","email","campaign","workflow","site","aiMintJobDraftIds","kind","slots","length","Object","fromEntries","map","slot","aiRecordedJobDraftId","job","step","steps","id","draftIds","aiJobDraftId","$id","DRAFT_CREATION_KINDS","Set","UNIT_PLAN_KINDS","screens","aiPlanWithDraftIds","plan","units","create","entry","has","screen","aiOriginJobId","origin","inputs"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,QAAQ,6CAA4C;AAI9E;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GAED,2EAA2E,GAC3E,OAAO,MAAMC,qBAAsF;IACjGC,MAAM;QAAC;KAAS;IAChBC,QAAQ;QAAC;KAAS;IAClBC,UAAU;QAAC;KAAW;IACtBC,MAAM;QAAC;KAAO;IACdC,WAAW;QAAC;KAAY;IACxBC,OAAO;QAAC;KAAQ;IAChBC,UAAU;QAAC;QAAS;KAAW;IAC/BC,UAAU;QAAC;KAAW;IACtB,+EAA+E;IAC/EC,MAAM;QAAC;KAAQ;AACjB,EAAC;AAED,uGAAuG,GACvG,OAAO,SAASC,kBAAkBC,IAAe;IAC/C,MAAMC,QAAQZ,kBAAkB,CAACW,KAAK;IACtC,IAAI,EAACC,yBAAAA,MAAOC,MAAM,GAAE,OAAO;IAC3B,OAAOC,OAAOC,WAAW,CAACH,MAAMI,GAAG,CAAC,CAACC,OAAS;YAACA;YAAMlB;SAAoB;AAC3E;AAEA,sFAAsF,GACtF,OAAO,SAASmB,qBAAqBC,GAAyB,EAAEF,IAAoB;QAC/DE;IAAnB,KAAK,MAAMC,SAAQD,aAAAA,IAAIE,KAAK,YAATF,aAAa,EAAE,CAAE;YACvBC;QAAX,MAAME,MAAKF,iBAAAA,KAAKG,QAAQ,qBAAbH,cAAe,CAACH,KAAK;QAChC,IAAI,OAAOK,OAAO,YAAYA,IAAI,OAAOA;IAC3C;IACA,OAAO;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASE,aAAaL,GAAiC,EAAEF,IAAoB;QAC3EC;IAAP,QAAOA,wBAAAA,qBAAqBC,KAAKF,iBAA1BC,wBAAmCC,IAAIM,GAAG;AACnD;AAEA,wGAAwG,GACxG,MAAMC,uBAA2D,IAAIC,IAA2B;IAC9F;IACA;IACA;IACA;IACA;CACD;AAED,gHAAgH,GAChH,MAAMC,kBAA8E;IAClF,+EAA+E;IAC/E3B,MAAM;QAAE4B,SAAS;IAAM;IACvBpB,MAAM;QAAEoB,SAAS;IAAK;IACtB,sEAAsE;IACtE,+CAA+C;IAC/C1B,UAAU;QAAE0B,SAAS;IAAM;IAC3B3B,QAAQ;QAAE2B,SAAS;IAAM;IACzBxB,WAAW;QAAEwB,SAAS;IAAM;AAC9B;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,mBAA0CnB,IAAe,EAAEoB,IAAO;IAChF,MAAMC,QAAQJ,eAAe,CAACjB,KAAK;IACnC,IAAI,CAACqB,OAAO,OAAOD;IACnB,OAAO,aACFA;QACHE,QAAQF,KAAKE,MAAM,CAACjB,GAAG,CAAC,CAACkB,QACvBR,qBAAqBS,GAAG,CAACD,MAAMvB,IAAI,IAAI,aAAKuB;gBAAOZ,IAAIvB;iBAAwBmC;QAEjFL,SAASG,MAAMH,OAAO,GAAGE,KAAKF,OAAO,CAACb,GAAG,CAAC,CAACoB,SAAY,aAAKA;gBAAQd,IAAIvB;kBAA0BgC,KAAKF,OAAO;;AAElH;AAEA;;;;CAIC,GACD,OAAO,SAASQ,cAAclB,GAAkC;QAC/CA;IAAf,MAAMmB,UAASnB,cAAAA,IAAIoB,MAAM,qBAAVpB,WAAY,CAAC,cAAc;IAC1C,OAAO,OAAOmB,WAAW,YAAYA,SAASA,SAASnB,IAAIM,GAAG;AAChE"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-draft-ids.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 { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport type { AiBuildPlan, AiBuildPlanCreateKind } from '../model/ai-build-plan'\nimport type { AiJob, AiJobDraftSlot, AiJobKind } from '../model/ai-jobs.types'\n\n/**\n * The ids a job's drafts are written under (AGL-3079).\n *\n * A draft a job writes is the resource a member would otherwise have made in\n * the console, so it is named the way the console names one: by\n * `createResourceUid()`. Each id is minted ONCE and recorded on the job before\n * the draft is written. That record is what lets a step run again safely: a\n * run cut off after its write reads the same id, finds the draft, and reports\n * it rather than writing a second.\n *\n * Where an id is recorded follows what decides the draft exists:\n *\n * - A draft the job's KIND always writes — a layout job's layout, a page\n * job's screen, a campaign's design and campaign — is minted when the job\n * is created, on the step that writes it (`AiJobStep.draftIds`).\n * - A draft the PLAN decides — what a page job's plan creates, a scaffold's\n * layout, form and pages — is minted when the plan is kept, on the plan's\n * own entry (`AiBuildPlanCreate.id`, `AiBuildPlanScreen.id`). Those are\n * built one unit a pass by the step that owns their kind, under a job named\n * by the entry's id (`aiSiteUnitJob`).\n *\n * A job whose step records no id names its draft by the job's own id, which\n * is the id every draft of such a job was written under.\n */\n\n/** The drafts each kind's step writes of its own, whatever its plan says. */\nexport const AI_JOB_DRAFT_SLOTS: Readonly<Partial<Record<AiJobKind, readonly AiJobDraftSlot[]>>> = {\n page: ['screen'],\n layout: ['layout'],\n template: ['template'],\n form: ['form'],\n component: ['component'],\n email: ['email'],\n campaign: ['email', 'campaign'],\n workflow: ['workflow'],\n // The welcome email a scaffold builds; its pages and creations are its plan's.\n site: ['email'],\n}\n\n/** Fresh ids for the drafts a job of this kind writes of its own; `null` for a kind that writes none. */\nexport function aiMintJobDraftIds(kind: AiJobKind): Partial<Record<AiJobDraftSlot, string>> | null {\n const slots = AI_JOB_DRAFT_SLOTS[kind]\n if (!slots?.length) return null\n return Object.fromEntries(slots.map((slot) => [slot, createResourceUid()]))\n}\n\n/** The id recorded on the job's steps for its draft of `slot`; `null` where none is. */\nexport function aiRecordedJobDraftId(job: Pick<AiJob, 'steps'>, slot: AiJobDraftSlot): string | null {\n for (const step of job.steps ?? []) {\n const id = step.draftIds?.[slot]\n if (typeof id === 'string' && id) return id\n }\n return null\n}\n\n/**\n * The id the job writes its draft of `slot` under, and finds it by when the\n * step runs again: the id recorded on its step, else the job's own id — which\n * names a unit's job by its plan entry's id, and a job whose step records none.\n */\nexport function aiJobDraftId(job: Pick<AiJob, '$id' | 'steps'>, slot: AiJobDraftSlot): string {\n return aiRecordedJobDraftId(job, slot) ?? job.$id\n}\n\n/** The creations a job writes as documents: a theme change is a proposal, and no job writes a dataset. */\nconst DRAFT_CREATION_KINDS: ReadonlySet<AiBuildPlanCreateKind> = new Set<AiBuildPlanCreateKind>([\n 'component',\n 'form',\n 'layout',\n 'template',\n 'email',\n])\n\n/** The kinds whose step builds what their plan names, one unit a draft, and whether that includes its screens. */\nconst UNIT_PLAN_KINDS: Readonly<Partial<Record<AiJobKind, { screens: boolean }>>> = {\n // A page job's own screen is its kind's draft; its plan's creations are units.\n page: { screens: false },\n site: { screens: true },\n // So are the components and forms a template, layout or component job\n // builds before its own record (AGL-3143 §15).\n template: { screens: false },\n layout: { screens: false },\n component: { screens: false },\n // A build names every draft its plan decides, its items' too (AGL-3616).\n build: { screens: true },\n}\n\n/**\n * The plan as a job of `kind` keeps it: for a kind that builds what its plan\n * names, a fresh id on every creation it writes as a document and, for a\n * scaffold, on every screen. An id the plan already carried is replaced —\n * a plan reused from another job names that job's drafts — so the drafts\n * this job builds are its own. Any other kind's plan is kept as it is.\n */\nexport function aiPlanWithDraftIds<T extends AiBuildPlan>(kind: AiJobKind, plan: T): T {\n const units = UNIT_PLAN_KINDS[kind]\n if (!units) return plan\n return {\n ...plan,\n create: plan.create.map((entry) =>\n DRAFT_CREATION_KINDS.has(entry.kind) ? { ...entry, id: createResourceUid() } : entry,\n ),\n screens: units.screens ? plan.screens.map((screen) => ({ ...screen, id: createResourceUid() })) : plan.screens,\n ...(plan.items ? { items: plan.items.map((item) => ({ ...item, id: createResourceUid() })) } : {}),\n }\n}\n\n/**\n * The job the member started, whichever unit is running (AGL-3596): a unit's\n * derived job carries it as `inputs.originJobId`, and a job a member started\n * is its own. What a draft is stamped with as `aiJobId`.\n */\nexport function aiOriginJobId(job: Pick<AiJob, '$id' | 'inputs'>): string {\n const origin = job.inputs?.['originJobId']\n return typeof origin === 'string' && origin ? origin : job.$id\n}\n"],"names":["createResourceUid","AI_JOB_DRAFT_SLOTS","page","layout","template","form","component","email","campaign","workflow","site","aiMintJobDraftIds","kind","slots","length","Object","fromEntries","map","slot","aiRecordedJobDraftId","job","step","steps","id","draftIds","aiJobDraftId","$id","DRAFT_CREATION_KINDS","Set","UNIT_PLAN_KINDS","screens","build","aiPlanWithDraftIds","plan","units","create","entry","has","screen","items","item","aiOriginJobId","origin","inputs"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,QAAQ,6CAA4C;AAI9E;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GAED,2EAA2E,GAC3E,OAAO,MAAMC,qBAAsF;IACjGC,MAAM;QAAC;KAAS;IAChBC,QAAQ;QAAC;KAAS;IAClBC,UAAU;QAAC;KAAW;IACtBC,MAAM;QAAC;KAAO;IACdC,WAAW;QAAC;KAAY;IACxBC,OAAO;QAAC;KAAQ;IAChBC,UAAU;QAAC;QAAS;KAAW;IAC/BC,UAAU;QAAC;KAAW;IACtB,+EAA+E;IAC/EC,MAAM;QAAC;KAAQ;AACjB,EAAC;AAED,uGAAuG,GACvG,OAAO,SAASC,kBAAkBC,IAAe;IAC/C,MAAMC,QAAQZ,kBAAkB,CAACW,KAAK;IACtC,IAAI,EAACC,yBAAAA,MAAOC,MAAM,GAAE,OAAO;IAC3B,OAAOC,OAAOC,WAAW,CAACH,MAAMI,GAAG,CAAC,CAACC,OAAS;YAACA;YAAMlB;SAAoB;AAC3E;AAEA,sFAAsF,GACtF,OAAO,SAASmB,qBAAqBC,GAAyB,EAAEF,IAAoB;QAC/DE;IAAnB,KAAK,MAAMC,SAAQD,aAAAA,IAAIE,KAAK,YAATF,aAAa,EAAE,CAAE;YACvBC;QAAX,MAAME,MAAKF,iBAAAA,KAAKG,QAAQ,qBAAbH,cAAe,CAACH,KAAK;QAChC,IAAI,OAAOK,OAAO,YAAYA,IAAI,OAAOA;IAC3C;IACA,OAAO;AACT;AAEA;;;;CAIC,GACD,OAAO,SAASE,aAAaL,GAAiC,EAAEF,IAAoB;QAC3EC;IAAP,QAAOA,wBAAAA,qBAAqBC,KAAKF,iBAA1BC,wBAAmCC,IAAIM,GAAG;AACnD;AAEA,wGAAwG,GACxG,MAAMC,uBAA2D,IAAIC,IAA2B;IAC9F;IACA;IACA;IACA;IACA;CACD;AAED,gHAAgH,GAChH,MAAMC,kBAA8E;IAClF,+EAA+E;IAC/E3B,MAAM;QAAE4B,SAAS;IAAM;IACvBpB,MAAM;QAAEoB,SAAS;IAAK;IACtB,sEAAsE;IACtE,+CAA+C;IAC/C1B,UAAU;QAAE0B,SAAS;IAAM;IAC3B3B,QAAQ;QAAE2B,SAAS;IAAM;IACzBxB,WAAW;QAAEwB,SAAS;IAAM;IAC5B,yEAAyE;IACzEC,OAAO;QAAED,SAAS;IAAK;AACzB;AAEA;;;;;;CAMC,GACD,OAAO,SAASE,mBAA0CpB,IAAe,EAAEqB,IAAO;IAChF,MAAMC,QAAQL,eAAe,CAACjB,KAAK;IACnC,IAAI,CAACsB,OAAO,OAAOD;IACnB,OAAO,aACFA;QACHE,QAAQF,KAAKE,MAAM,CAAClB,GAAG,CAAC,CAACmB,QACvBT,qBAAqBU,GAAG,CAACD,MAAMxB,IAAI,IAAI,aAAKwB;gBAAOb,IAAIvB;iBAAwBoC;QAEjFN,SAASI,MAAMJ,OAAO,GAAGG,KAAKH,OAAO,CAACb,GAAG,CAAC,CAACqB,SAAY,aAAKA;gBAAQf,IAAIvB;kBAA0BiC,KAAKH,OAAO;OAC1GG,KAAKM,KAAK,GAAG;QAAEA,OAAON,KAAKM,KAAK,CAACtB,GAAG,CAAC,CAACuB,OAAU,aAAKA;gBAAMjB,IAAIvB;;IAAwB,IAAI,CAAC;AAEpG;AAEA;;;;CAIC,GACD,OAAO,SAASyC,cAAcrB,GAAkC;QAC/CA;IAAf,MAAMsB,UAAStB,cAAAA,IAAIuB,MAAM,qBAAVvB,WAAY,CAAC,cAAc;IAC1C,OAAO,OAAOsB,WAAW,YAAYA,SAASA,SAAStB,IAAIM,GAAG;AAChE"}
@@ -56,9 +56,10 @@ import type { AiJobAdmissionRefusal } from './ai-job-admission';
56
56
  * host's routing map, which counts an unrouted screen too (AGL-1445); and a
57
57
  * form needs the plan to include the route's `entitlement` first, refused in
58
58
  * the route's own words, after which every form document counts against
59
- * `formsPerHost`. A reusable component counts against no allowance at all:
60
- * the route admits one on a plan with the `reusableComponents` entitlement
61
- * and refuses it otherwise, and so does the writer.
59
+ * `formsPerHost`. A reusable component counts against `componentsPerHost`
60
+ * (AGL-3615) — Free 1, every paid plan unlimited — over its LIVE documents,
61
+ * since a deleted component keeps its document with `deletedAt` stamped; on
62
+ * an unlimited plan nothing is counted.
62
63
  *
63
64
  * ── Applied to nothing ───────────────────────────────────────────────────
64
65
  *
@@ -102,7 +103,7 @@ export declare const AI_DRAFT_ENTITLEMENT_REFUSAL = "This feature is not include
102
103
  export interface AiDraftBand {
103
104
  collection: 'layouts' | 'templates' | 'forms' | 'components' | 'screens';
104
105
  /** The allowance the route counts the kind against, where it counts one. */
105
- quotaKey?: 'sharedLayoutsPerHost' | 'templatesPerHost' | 'formsPerHost' | 'screensPerHost';
106
+ quotaKey?: 'sharedLayoutsPerHost' | 'templatesPerHost' | 'formsPerHost' | 'componentsPerHost' | 'screensPerHost';
106
107
  /** The feature the plan must include before any document of the kind counts. */
107
108
  entitlement?: keyof OrgFeatureFlags;
108
109
  /** The route's plural label, in the refusal it gives. */
@@ -127,6 +128,15 @@ type RoutingMap = Record<string, unknown> | null | undefined;
127
128
  * the route asks it before it counts.
128
129
  */
129
130
  export declare function aiDraftBandRefusal(kind: AiDraftKind, rows: ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>>, org: Partial<AglynOrgBilling> | null, routingMap?: RoutingMap): string | null;
131
+ /**
132
+ * Whether a job builds a page's repeats as reusable components (AGL-3071,
133
+ * AGL-3615): only where the site's component allowance is unlimited. A
134
+ * finite one — Free's 1 — cannot hold every repeat a page or a site plan
135
+ * draws, and the doctrine's rule 1 would then demand components the plan may
136
+ * not create; so there the repeats are drawn inline and the allowance is the
137
+ * member's to spend, as it was while Free had no components at all.
138
+ */
139
+ export declare function aiBuildsWithComponents(org: Partial<AglynOrgBilling> | null): boolean;
130
140
  /** The draft kinds a plan's creations land as, whose band a plan is told about (AGL-3030). */
131
141
  declare const AI_PLAN_DRAFT_KINDS: readonly ["component", "form", "layout", "template"];
132
142
  type AiPlanDraftKind = (typeof AI_PLAN_DRAFT_KINDS)[number];
@@ -110,9 +110,12 @@ import { aiUnrestrictedPlanCapabilities } from "../model/ai-plan-capabilities.js
110
110
  quotaKey: 'formsPerHost',
111
111
  label: 'forms'
112
112
  },
113
+ // A reusable component is counted against `componentsPerHost` alone — Free
114
+ // 1, every paid plan unlimited — and is not behind `reusableComponents`
115
+ // (AGL-3615), the way a saved form left it.
113
116
  component: {
114
117
  collection: 'components',
115
- entitlement: 'reusableComponents',
118
+ quotaKey: 'componentsPerHost',
116
119
  label: 'reusable components'
117
120
  },
118
121
  screen: {
@@ -159,10 +162,28 @@ function subdomainOf(host) {
159
162
  id: row.id,
160
163
  kind: row.kind,
161
164
  deletedAt: row.deletedAt
162
- })), routingMap != null ? routingMap : undefined).size : rows.length;
165
+ })), routingMap != null ? routingMap : undefined).size : liveRows(kind, rows).length;
163
166
  const quota = checkQuota(org, band.quotaKey, used);
164
167
  return quota.allowed ? null : `Your plan includes ${quota.limit} ${band.label} — upgrade in Billing for more`;
165
168
  }
169
+ /**
170
+ * The rows a kind's allowance counts: every one, except a component's, where
171
+ * a deletion stamps `deletedAt` and keeps the document — the route counts the
172
+ * live ones, so a Free site that deleted its one component may make another
173
+ * (AGL-3615).
174
+ */ function liveRows(kind, rows) {
175
+ return kind === 'component' ? rows.filter((row)=>row.deletedAt == null) : rows;
176
+ }
177
+ /**
178
+ * Whether a job builds a page's repeats as reusable components (AGL-3071,
179
+ * AGL-3615): only where the site's component allowance is unlimited. A
180
+ * finite one — Free's 1 — cannot hold every repeat a page or a site plan
181
+ * draws, and the doctrine's rule 1 would then demand components the plan may
182
+ * not create; so there the repeats are drawn inline and the allowance is the
183
+ * member's to spend, as it was while Free had no components at all.
184
+ */ export function aiBuildsWithComponents(org) {
185
+ return !Number.isFinite(resolveOrgEntitlements(org).componentsPerHost);
186
+ }
166
187
  /** The draft kinds a plan's creations land as, whose band a plan is told about (AGL-3030). */ const AI_PLAN_DRAFT_KINDS = [
167
188
  'component',
168
189
  'form',
@@ -193,7 +214,7 @@ function subdomainOf(host) {
193
214
  };
194
215
  // The route's own arithmetic decides; the count beside it is what is left.
195
216
  const refusal = aiDraftBandRefusal(kind, rows, org);
196
- const used = kind === 'template' ? rows.filter((row)=>row.sourceType !== undefined && row.sourceType !== 'starter').length : rows.length;
217
+ const used = kind === 'template' ? rows.filter((row)=>row.sourceType !== undefined && row.sourceType !== 'starter').length : liveRows(kind, rows).length;
197
218
  return refusal ? {
198
219
  allowed: false,
199
220
  left: 0,
@@ -231,7 +252,7 @@ function subdomainOf(host) {
231
252
  const unrestricted = aiUnrestrictedPlanCapabilities();
232
253
  const datasets = checkDatasetQuota(org, 0).limit > 0;
233
254
  return _extends({
234
- reusableComponents: checkEntitlement(org, 'reusableComponents'),
255
+ reusableComponents: aiBuildsWithComponents(org),
235
256
  create: _extends({}, unrestricted.create, {
236
257
  component: planCreation('component', org, (_rows_component = rows.component) != null ? _rows_component : null),
237
258
  form: planCreation('form', org, (_rows_form = rows.form) != null ? _rows_form : null),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-drafts.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 { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { uniqueDuplicateName } from '@aglyn/aglyn/app-utils/duplicate-resource'\nimport {\n FORM_COMPONENT_ID,\n newFormListFields,\n normalizeFormSlug,\n type FormFieldDecl,\n type FormRouting,\n} from '@aglyn/aglyn/app-utils/forms'\nimport { artifactCreateListKeys } from '@aglyn/aglyn/app-utils/artifact-list-keys'\nimport {\n checkDatasetQuota,\n checkEntitlement,\n checkQuota,\n resolveEffectivePlan,\n resolveOrgEntitlements,\n} from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n billableScreenIds,\n normalizeScreenSlug,\n reservedScreenRouteSegment,\n SCREEN_ROOT_PATH,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport type {\n AglynOrgBilling,\n OrgFeatureFlags,\n} from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/platform.types'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n aiUnrestrictedPlanCapabilities,\n type AiPlanCapabilities,\n type AiPlanCreation,\n} from '../model/ai-plan-capabilities'\nimport type { AiJobAdmissionRefusal } from './ai-job-admission'\n\n/**\n * Where a generation job's layout, page template, form, reusable component\n * or page lands (AGL-2909, AGL-2913, AGL-2908, AGL-2907): a new draft, made\n * as the console's host resources route makes one.\n *\n * ── The document the create route makes ─────────────────────────────────\n *\n * `AI_DRAFT_FIELDS` is that route's allow-list for each kind, and a spec\n * reads the route's source to hold the two together. Nothing outside the\n * list is written except the route's own stamps: `createdAt`, `updatedAt`,\n * `createdBy`, the list keys every artifact create carries\n * (`artifactCreateListKeys`: the name keys, a component's or template's stored\n * kind, a template's library row), and a template's `source`, which is\n * `authored` — a template a member's job generated is theirs, never a starter\n * or a listing. A layout's or a screen's first version carries the keys the\n * versions route seeds one with. A form is the document the Forms page's\n * Create sends: its design canvas-shaped under `rootId` with the form node\n * bound to the form's id and captioned with its name, a slug read from that\n * name, and no version — the form's page mints the first one when someone\n * opens it. Where Create sends an empty `fields`, a generated form sends the\n * declaration read off its own design, so the two agree from the start. A\n * component carries its tree and the props it declares on its own document,\n * as Use template writes one, and gets its first version where every\n * component does: when a member opens it.\n *\n * ── Counted like a create ────────────────────────────────────────────────\n *\n * The band is met INSIDE the transaction that writes (AGL-2231), with the\n * route's arithmetic: every layout document counts against\n * `sharedLayoutsPerHost`; a template counts against `templatesPerHost` when it\n * records a source other than a platform starter — the route's `!=` query,\n * which never matches a document with no `source.type` at all; a screen counts\n * against `screensPerHost` by `billableScreenIds`, over the screens and the\n * host's routing map, which counts an unrouted screen too (AGL-1445); and a\n * form needs the plan to include the route's `entitlement` first, refused in\n * the route's own words, after which every form document counts against\n * `formsPerHost`. A reusable component counts against no allowance at all:\n * the route admits one on a plan with the `reusableComponents` entitlement\n * and refuses it otherwise, and so does the writer.\n *\n * ── Applied to nothing ───────────────────────────────────────────────────\n *\n * A layout renders only around a screen or layout that names it, or as the\n * built-in page layout the host document names; a template is inert until a\n * member uses it; a form renders only where a page places it; a component\n * renders only where an instance places it, and a new one has no instance;\n * and a screen serves nothing until the host's routing map names it, which is\n * what publishing writes (`publishScreenRoute`). So the writer touches the new\n * documents and nothing else — no host document, no routing map, no\n * collection, store setting, other layout or component — and never a\n * `publishedAt` or a publish schedule. What the job hands back is a link to\n * the draft, never a binding.\n *\n * ── One draft per recorded id ────────────────────────────────────────────\n *\n * The caller names the document id: the console resource id the job recorded\n * for the draft before its first write (`ai-job-draft-ids.ts`). A step run\n * again after its draft was written — a process cut off before the machine\n * recorded the step — reads the same id, finds that draft and reports it,\n * rather than writing a second.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport type AiDraftKind = 'layout' | 'template' | 'form' | 'component' | 'screen'\n\n/** The draft kinds that keep their tree in versions, as the besigner opens them. */\nexport type AiVersionedDraftKind = Extract<AiDraftKind, 'layout' | 'screen'>\n\n/** The console host resources route's allow-list for each kind. */\nexport const AI_DRAFT_FIELDS: Readonly<Record<AiDraftKind, readonly string[]>> = {\n layout: ['displayName', 'description', 'versionId'],\n template: [\n 'kind',\n 'displayName',\n 'description',\n 'placeholders',\n 'nodes',\n 'rootId',\n 'props',\n 'slug',\n 'seo',\n // A component template's own kind (AGL-3287); a job never sends one.\n 'componentKind',\n ],\n form: [\n 'displayName',\n 'slug',\n 'fields',\n 'consentFieldName',\n 'routing',\n 'legacyMatch',\n 'rootId',\n 'nodes',\n ],\n // `kind` marks a reusable email block (AGL-3287); a job makes page\n // components and never sends one.\n component: ['displayName', 'description', 'rootId', 'nodes', 'props', 'kind'],\n // `kind` is on the route's list for the email composer; a page has none, and\n // the writer never sends one.\n screen: ['displayName', 'description', 'slug', 'seo', 'kind', 'versionId'],\n}\n\n/** The keys a layout's first version is seeded with, all on the versions route's list. */\nexport const AI_DRAFT_VERSION_FIELDS: readonly string[] = ['layoutId', 'hostId', 'displayName', 'nodes']\n\n/**\n * The keys a screen's first version is seeded with, all on the versions\n * route's list: the screen it belongs to, the site, the label, the tree, and\n * the layout it renders inside when the plan names one the site has.\n */\nexport const AI_DRAFT_SCREEN_VERSION_FIELDS: readonly string[] = [\n 'screenId',\n 'hostId',\n 'displayName',\n 'nodes',\n 'layoutId',\n]\n\n/** The label on a generated draft's first version, as on one Use template makes. */\nexport const AI_DRAFT_VERSION_NAME = 'Initial version'\n\n/** The route's refusal when the site's plan does not include the kind at all. */\nexport const AI_DRAFT_ENTITLEMENT_REFUSAL = 'This feature is not included in your plan — see Billing'\n\nexport interface AiDraftBand {\n collection: 'layouts' | 'templates' | 'forms' | 'components' | 'screens'\n /** The allowance the route counts the kind against, where it counts one. */\n quotaKey?:\n | 'sharedLayoutsPerHost'\n | 'templatesPerHost'\n | 'formsPerHost'\n | 'screensPerHost'\n /** The feature the plan must include before any document of the kind counts. */\n entitlement?: keyof OrgFeatureFlags\n /** The route's plural label, in the refusal it gives. */\n label: string\n}\n\n/** Each kind's collection, counter, feature and label, as the create route declares them. */\nexport const AI_DRAFT_BANDS: Readonly<Record<AiDraftKind, AiDraftBand>> = {\n layout: { collection: 'layouts', quotaKey: 'sharedLayoutsPerHost', label: 'shared layouts' },\n template: { collection: 'templates', quotaKey: 'templatesPerHost', label: 'templates' },\n // A saved form is counted against `formsPerHost` alone — Free 1 through\n // the ceiling — and is not behind `reusableComponents` (AGL-3597).\n form: { collection: 'forms', quotaKey: 'formsPerHost', label: 'forms' },\n component: {\n collection: 'components',\n entitlement: 'reusableComponents',\n label: 'reusable components',\n },\n screen: { collection: 'screens', quotaKey: 'screensPerHost', label: 'pages' },\n}\n\ninterface SiblingRow {\n id: string\n name: string\n slug: string\n kind: unknown\n sourceType: unknown\n deletedAt: unknown\n deleted: boolean\n}\n\n/** A host's routing map: screen id → the path it is published at. */\ntype RoutingMap = Record<string, unknown> | null | undefined\n\nfunction draftCollection(firestore: Firestore, hostId: string, kind: AiDraftKind) {\n return firestore.collection('hosts').doc(hostId).collection(AI_DRAFT_BANDS[kind].collection)\n}\n\n/** The fields the band, the name, the address and a deletion are read from. */\nfunction siblingsOf(collection: FirebaseFirestore.CollectionReference) {\n return collection.select('displayName', 'slug', 'kind', 'source.type', 'deletedAt')\n}\n\nfunction siblingRows(snapshot: FirebaseFirestore.QuerySnapshot): SiblingRow[] {\n return snapshot.docs.map((doc) => ({\n id: doc.id,\n name: String(doc.get('displayName') ?? ''),\n slug: String(doc.get('slug') ?? ''),\n kind: doc.get('kind'),\n sourceType: doc.get('source.type'),\n deletedAt: doc.get('deletedAt'),\n deleted: doc.get('deletedAt') != null,\n }))\n}\n\nfunction subdomainOf(host: FirebaseFirestore.DocumentSnapshot): string | null {\n const subdomain = host.get('subdomain')\n return typeof subdomain === 'string' && subdomain ? subdomain : null\n}\n\n/**\n * The route's refusal when the site's plan does not include the kind, or\n * holds no more of it; `null` while it does. The feature is asked first, as\n * the route asks it before it counts.\n */\nexport function aiDraftBandRefusal(\n kind: AiDraftKind,\n rows: ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>>,\n org: Partial<AglynOrgBilling> | null,\n routingMap?: RoutingMap,\n): string | null {\n const band = AI_DRAFT_BANDS[kind]\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) {\n return AI_DRAFT_ENTITLEMENT_REFUSAL\n }\n if (!band.quotaKey) return null\n const used =\n kind === 'template'\n ? rows.filter((row) => row.sourceType !== undefined && row.sourceType !== 'starter').length\n : kind === 'screen'\n ? billableScreenIds(\n rows.map((row) => ({ id: row.id, kind: row.kind, deletedAt: row.deletedAt })),\n (routingMap ?? undefined) as never,\n ).size\n : rows.length\n const quota = checkQuota(org, band.quotaKey, used)\n return quota.allowed\n ? null\n : `Your plan includes ${quota.limit} ${band.label} — upgrade in Billing for more`\n}\n\n/** The draft kinds a plan's creations land as, whose band a plan is told about (AGL-3030). */\nconst AI_PLAN_DRAFT_KINDS = ['component', 'form', 'layout', 'template'] as const\n\ntype AiPlanDraftKind = (typeof AI_PLAN_DRAFT_KINDS)[number]\n\n/** Every draft kind's plural label made singular, for a band of one. */\nfunction bandLabel(label: string, count: number): string {\n return count === 1 ? label.replace(/s$/, '') : label\n}\n\n/**\n * Whether the site may take `used + 1` of a kind, in the create route's\n * arithmetic: the feature first, then the allowance. `null` for a kind whose\n * allowance is unlimited or counts nothing, which needs no rows read.\n */\nfunction planCreation(\n kind: AiPlanDraftKind,\n org: Partial<AglynOrgBilling> | null,\n rows: ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>> | null,\n): AiPlanCreation {\n const band = AI_DRAFT_BANDS[kind]\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) {\n return {\n allowed: false,\n left: 0,\n reason: `this workspace's plan does not include ${kind === 'form' ? 'saved forms' : band.label}`,\n }\n }\n const limit = band.quotaKey ? resolveOrgEntitlements(org)[band.quotaKey] : Number.POSITIVE_INFINITY\n if (!rows || !Number.isFinite(limit)) return { allowed: true, left: null, reason: null }\n // The route's own arithmetic decides; the count beside it is what is left.\n const refusal = aiDraftBandRefusal(kind, rows, org)\n const used =\n kind === 'template'\n ? rows.filter((row) => row.sourceType !== undefined && row.sourceType !== 'starter').length\n : rows.length\n return refusal\n ? {\n allowed: false,\n left: 0,\n reason: `this site already holds the ${limit} ${bandLabel(band.label, limit)} its plan includes`,\n }\n : { allowed: true, left: Math.max(0, limit - used), reason: null }\n}\n\n/** Whether a kind's rows are counted at all on this plan: included, and under a finite allowance. */\nfunction countsRows(kind: AiPlanDraftKind, org: Partial<AglynOrgBilling> | null): boolean {\n const band = AI_DRAFT_BANDS[kind]\n if (!band.quotaKey) return false\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) return false\n return Number.isFinite(resolveOrgEntitlements(org)[band.quotaKey])\n}\n\n/**\n * What a plan may create on this site (AGL-3030), from the workspace's plan\n * and the rows each counted kind already holds — pure, so the specs and the\n * eval harness hold the arithmetic without a Firestore. A kind whose rows\n * are not given is counted as having room, which is what an unlimited\n * allowance means.\n *\n * A theme change is a proposal a member applies, never a draft, so it counts\n * against nothing. A dataset is org data that no job writes; a plan may name\n * one only where the workspace's plan includes datasets at all. An email\n * design is written by the email plugin, whose own gate decides.\n *\n * A workspace on the Free plan spends the Free taste, a wall of monthly\n * credits, so its plan is held to the sections that wall pays for (AGL-3070).\n * The effective plan decides, as it decides the taste's own allowance: a\n * workspace whose subscription died is Free again.\n */\nexport function aiPlanCapabilitiesFrom(\n org: Partial<AglynOrgBilling> | null,\n rows: Partial<Record<AiPlanDraftKind, ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>>>> = {},\n): AiPlanCapabilities {\n const unrestricted = aiUnrestrictedPlanCapabilities()\n const datasets = checkDatasetQuota(org, 0).limit > 0\n return {\n reusableComponents: checkEntitlement(org, 'reusableComponents'),\n create: {\n ...unrestricted.create,\n component: planCreation('component', org, rows.component ?? null),\n form: planCreation('form', org, rows.form ?? null),\n layout: planCreation('layout', org, rows.layout ?? null),\n template: planCreation('template', org, rows.template ?? null),\n dataset: datasets\n ? unrestricted.create.dataset\n : { allowed: false, left: 0, reason: \"this workspace's plan does not include datasets\" },\n },\n ...(resolveEffectivePlan(org) === 'free' ? { freeTaste: true } : {}),\n }\n}\n\n/**\n * What a plan may create on this site, read: the rows of each kind whose\n * allowance the workspace's plan counts, as the draft writer reads them, and\n * nothing for a kind the plan leaves out or leaves unlimited.\n */\nexport async function readAiPlanCapabilities(\n firestore: Firestore,\n input: { hostId: string; org: Partial<AglynOrgBilling> | null },\n): Promise<AiPlanCapabilities> {\n const counted = AI_PLAN_DRAFT_KINDS.filter((kind) => countsRows(kind, input.org))\n const snapshots = await Promise.all(\n counted.map((kind) => siblingsOf(draftCollection(firestore, input.hostId, kind)).get()),\n )\n return aiPlanCapabilitiesFrom(\n input.org,\n Object.fromEntries(counted.map((kind, index) => [kind, siblingRows(snapshots[index])])),\n )\n}\n\n/**\n * Whether the site can take one more draft of the kind: a read, for a door\n * that asks before it spends. The write asks again inside its transaction.\n */\nexport async function aiDraftAllowanceRefusal(\n firestore: Firestore,\n input: { kind: AiDraftKind; hostId: string; org: Partial<AglynOrgBilling> | null },\n): Promise<string | null> {\n const [snapshot, host] = await Promise.all([\n siblingsOf(draftCollection(firestore, input.hostId, input.kind)).get(),\n input.kind === 'screen'\n ? firestore.collection('hosts').doc(input.hostId).get()\n : Promise.resolve(null),\n ])\n return aiDraftBandRefusal(\n input.kind,\n siblingRows(snapshot),\n input.org,\n host?.get('screens') as RoutingMap,\n )\n}\n\n/**\n * A draft screen's one-segment address: the one asked for, else one from its\n * name, never a reserved segment, and never an address a live screen keeps\n * or the routing map already serves — so the member's publish at it later\n * takes nothing from a live page. The root is asked for only while no screen\n * holds it.\n *\n * Except the placeholder home page a site is created with (AGL-3408), which\n * holds `/` only until a real home page is published there: the guided start\n * is offered to exactly the sites that still have it, and the home page it\n * drafts has to be able to ask for the root. Publishing it replaces the\n * placeholder in the same write.\n */\nexport function aiDraftScreenSlug(\n requested: string | null | undefined,\n name: string,\n rows: ReadonlyArray<Pick<SiblingRow, 'slug' | 'deleted'> & { id?: string }>,\n routingMap: RoutingMap,\n defaultHomeScreenId?: string | null,\n): string {\n const placeholder = (id: string | undefined, path: unknown) =>\n Boolean(defaultHomeScreenId) && id === defaultHomeScreenId && path === SCREEN_ROOT_PATH\n const taken = new Set<string>([\n ...rows\n .filter((row) => !row.deleted && row.slug && !placeholder(row.id, row.slug))\n .map((row) => row.slug),\n ...Object.entries(routingMap ?? {})\n .filter(([id, path]) => typeof path === 'string' && !placeholder(id, path))\n .map(([, path]) => path as string),\n ])\n const usable = (value: string | undefined) =>\n value && !reservedScreenRouteSegment(value) && !(value === SCREEN_ROOT_PATH && taken.has(value))\n ? value\n : undefined\n const base = usable(normalizeScreenSlug(requested ?? undefined)) ?? usable(normalizeScreenSlug(name)) ?? 'page'\n if (base === SCREEN_ROOT_PATH) return base\n let slug = base\n let attempt = 2\n while (taken.has(slug)) slug = `${base}-${attempt++}`\n return slug\n}\n\nexport interface AiDraftRecord {\n id: string\n /** A layout's or a screen's first version; `null` for every other kind, which the writer gives none. */\n versionId: string | null\n name: string\n hostSubdomain: string | null\n /** A screen's search listing as the draft holds it now; absent on other kinds. */\n seo?: { title?: string; description?: string } | null\n /** Whether a member deleted the draft since it was written. */\n deleted?: boolean\n}\n\nfunction recordOf(\n id: string,\n draft: FirebaseFirestore.DocumentSnapshot,\n host: FirebaseFirestore.DocumentSnapshot,\n): AiDraftRecord {\n const versionId = draft.get('versionId')\n const seo = draft.get('seo')\n return {\n id,\n versionId: typeof versionId === 'string' && versionId ? versionId : null,\n name: String(draft.get('displayName') ?? ''),\n hostSubdomain: subdomainOf(host),\n ...(seo && typeof seo === 'object' ? { seo: seo as AiDraftRecord['seo'] } : {}),\n ...(draft.get('deletedAt') != null ? { deleted: true } : {}),\n }\n}\n\n/** The draft a job already wrote under `id`, when a run of its step got that far. */\nexport async function readAiDraft(\n firestore: Firestore,\n input: { kind: AiDraftKind; hostId: string; id: string },\n): Promise<AiDraftRecord | null> {\n const draft = await draftCollection(firestore, input.hostId, input.kind).doc(input.id).get()\n if (!draft.exists) return null\n return recordOf(input.id, draft, await firestore.collection('hosts').doc(input.hostId).get())\n}\n\n/**\n * The tree a template draft holds now, decoded; `null` when there is none.\n *\n * A template is not a versioned draft — its nodes live on the document\n * itself, which is why `readAiDraftNodes` refuses its kind.\n */\nexport async function readAiTemplateDraftNodes(\n firestore: Firestore,\n input: { hostId: string; id: string },\n): Promise<NodesMap | null> {\n const draft = await draftCollection(firestore, input.hostId, 'template').doc(input.id).get()\n if (!draft.exists) return null\n return decodeStoredNodes<NodesMap>(draft.get('nodes')) ?? null\n}\n\n/**\n * The property names a component draft declares now; `null` when there is no\n * such draft. Read from the draft document rather than its version, because a\n * component's props live beside its tree and a copy carries its source's.\n */\nexport async function readAiDraftComponentProps(\n firestore: Firestore,\n input: { hostId: string; id: string },\n): Promise<string[] | null> {\n const draft = await draftCollection(firestore, input.hostId, 'component').doc(input.id).get()\n if (!draft.exists) return null\n const props = draft.get('props')\n if (!Array.isArray(props)) return []\n return props\n .map((prop) => (prop && typeof prop === 'object' ? (prop as Record<string, unknown>)['name'] : null))\n .filter((name): name is string => typeof name === 'string' && name.length > 0)\n}\n\n/** The tree a versioned draft's first version holds now, decoded; `null` when there is none. */\nexport async function readAiDraftNodes(\n firestore: Firestore,\n input: { kind: AiVersionedDraftKind; hostId: string; id: string },\n): Promise<{ versionId: string; nodes: NodesMap } | null> {\n const draftRef = draftCollection(firestore, input.hostId, input.kind).doc(input.id)\n const draft = await draftRef.get()\n const versionId = draft.exists ? draft.get('versionId') : null\n if (typeof versionId !== 'string' || !versionId) return null\n const version = await draftRef.collection('versions').doc(versionId).get()\n const nodes = version.exists ? decodeStoredNodes<NodesMap>(version.get('nodes')) : null\n return nodes ? { versionId, nodes } : null\n}\n\nexport interface AiDraftInput {\n kind: AiDraftKind\n hostId: string\n /** The draft's document id: the id the job recorded for this draft. */\n id: string\n /** Whose work the draft is: the job's creator, whose brief it built. */\n uid: string\n org: Partial<AglynOrgBilling> | null\n /** The name asked for; a live sibling already called that gets a number. */\n name: string\n nodes: NodesMap\n /** A page template's suggested address, or a screen's one-segment address. */\n slug?: string | null\n /** A form's declaration and the ids its design hangs from; required for a form. */\n form?: AiFormDraftDeclaration\n /** A component's root, which the node map holds; required for a component. */\n rootId?: string\n /** The properties a component declares, which its tree binds. */\n props?: readonly ReusableComponentProp[]\n /** A screen's layout, for its first version to render inside; a layout the site has. */\n layoutId?: string | null\n /**\n * The job the member started that wrote this draft (AGL-3596), stamped as\n * `aiJobId` on a page's or a layout's document and first version, so staff\n * can tell a generated page from a copy someone made. A scaffold's unit\n * names the site job, not the unit's derived id.\n */\n aiJobId?: string | null\n now: Date\n}\n\n/** What a form draft declares beside its design, read off that design. */\nexport interface AiFormDraftDeclaration {\n /** The canvas root `nodes` is stored under, as the Forms page's Create stores it. */\n rootId: string\n /** The form node, which carries the form's id and is captioned with the draft's name. */\n formNodeId: string\n fields: FormFieldDecl[]\n consentFieldName: string | null\n routing: FormRouting | null\n}\n\nexport type AiDraftWrite =\n | (AiDraftRecord & { ok: true; replayed: boolean })\n | { ok: false; status: 403 | 404; error: string }\n\n/** Keep only the keys the create route admits for the kind. */\nfunction allowListed(kind: AiDraftKind, data: Record<string, unknown>): Record<string, unknown> {\n const allowed = new Set(AI_DRAFT_FIELDS[kind])\n return Object.fromEntries(\n Object.entries(data).filter(([key, value]) => allowed.has(key) && value !== undefined),\n )\n}\n\n/** The node map with its form node captioned with `name`, as Create captions a new form. */\nfunction withFormCaption(nodes: NodesMap, formNodeId: string, name: string): NodesMap {\n const node = nodes[formNodeId]\n if (!node || node.componentId !== FORM_COMPONENT_ID) return nodes\n return { ...nodes, [formNodeId]: { ...node, props: { ...(node.props ?? {}), formName: name } } }\n}\n\n/**\n * Write one draft: the band met, the name made unique among the live\n * siblings, the node map stored as msgpack, all in one transaction. A\n * refusal is returned, never thrown; a Firestore failure propagates, so the\n * machine's retry applies.\n */\nexport async function writeAiDraft(firestore: Firestore, input: AiDraftInput): Promise<AiDraftWrite> {\n const packed = encodeStoredNodes(input.nodes)\n if (!packed) throw new Error('an AI draft needs a node map')\n if (input.kind === 'form' && !input.form) throw new Error('an AI form draft needs its declaration')\n if (input.kind === 'component' && !(input.rootId && input.nodes[input.rootId])) {\n throw new Error('a component draft needs a root its node map holds')\n }\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const collection = draftCollection(firestore, input.hostId, input.kind)\n const draftRef = collection.doc(input.id)\n return firestore.runTransaction(async (tx): Promise<AiDraftWrite> => {\n // Every read before any write, which Firestore requires.\n const [host, existing, siblings] = await Promise.all([\n tx.get(hostRef),\n tx.get(draftRef),\n tx.get(siblingsOf(collection)),\n ])\n if (!host.exists) return { ok: false, status: 404, error: 'Unknown site' }\n if (existing.exists) {\n return { ok: true, replayed: true, ...recordOf(input.id, existing, host) }\n }\n const rows = siblingRows(siblings)\n // Read, never written: the routing map decides which screens count.\n const routingMap = host.get('screens') as RoutingMap\n const refusal = aiDraftBandRefusal(input.kind, rows, input.org, routingMap)\n if (refusal) return { ok: false, status: 403, error: refusal }\n const defaultHomeScreenId = host.get('defaultHomeScreenId') as string | null | undefined\n // A screen draft that takes the root the placeholder home page holds\n // replaces that page when it is published (AGL-3408), so it is named as\n // the page it replaces, \"Home\", rather than as a copy of it (AGL-3596).\n const slug =\n input.kind === 'screen'\n ? aiDraftScreenSlug(input.slug, input.name, rows, routingMap, defaultHomeScreenId)\n : null\n const replacesPlaceholder = (row: SiblingRow) =>\n slug === SCREEN_ROOT_PATH &&\n Boolean(defaultHomeScreenId) &&\n row.id === defaultHomeScreenId &&\n (routingMap ?? {})[row.id] === SCREEN_ROOT_PATH\n const name = uniqueDuplicateName(\n input.name,\n rows.filter((row) => !row.deleted && !replacesPlaceholder(row)).map((row) => row.name),\n )\n const stamps = { createdAt: input.now, updatedAt: input.now, createdBy: input.uid }\n const marker = input.aiJobId ? { aiJobId: input.aiJobId } : {}\n const nodes = Buffer.from(packed)\n let versionId: string | null = null\n if (input.kind === 'layout') {\n versionId = createResourceUid()\n tx.create(draftRef, {\n ...allowListed('layout', { displayName: name, versionId }),\n ...artifactCreateListKeys('layouts', { displayName: name }),\n ...stamps,\n ...marker,\n })\n tx.create(draftRef.collection('versions').doc(versionId), {\n layoutId: input.id,\n hostId: input.hostId,\n displayName: AI_DRAFT_VERSION_NAME,\n nodes,\n ...stamps,\n ...marker,\n })\n } else if (input.kind === 'screen') {\n versionId = createResourceUid()\n tx.create(draftRef, {\n ...allowListed('screen', { displayName: name, slug, versionId }),\n ...artifactCreateListKeys('screens', { displayName: name }),\n ...stamps,\n ...marker,\n })\n tx.create(draftRef.collection('versions').doc(versionId), {\n screenId: input.id,\n hostId: input.hostId,\n displayName: AI_DRAFT_VERSION_NAME,\n nodes,\n ...(input.layoutId ? { layoutId: input.layoutId } : {}),\n ...stamps,\n ...marker,\n })\n } else if (input.kind === 'template') {\n tx.create(draftRef, {\n ...allowListed('template', {\n kind: 'page',\n displayName: name,\n nodes,\n slug: input.slug || undefined,\n }),\n source: { type: 'authored' },\n ...artifactCreateListKeys('templates', {\n kind: 'page',\n displayName: name,\n source: { type: 'authored' },\n }),\n ...stamps,\n })\n } else if (input.kind === 'component') {\n tx.create(draftRef, {\n ...allowListed('component', {\n displayName: name,\n rootId: input.rootId,\n nodes,\n props: input.props?.length ? [...input.props] : undefined,\n }),\n ...artifactCreateListKeys('components', { displayName: name }),\n ...stamps,\n })\n } else {\n const form = input.form as AiFormDraftDeclaration\n const captioned = encodeStoredNodes(withFormCaption(input.nodes, form.formNodeId, name)) ?? packed\n const slug = normalizeFormSlug(name) || input.id\n tx.create(draftRef, {\n ...allowListed('form', {\n displayName: name,\n slug,\n fields: form.fields,\n consentFieldName: form.consentFieldName || undefined,\n routing: form.routing ?? undefined,\n rootId: form.rootId,\n nodes: Buffer.from(captioned),\n }),\n // What the Forms list queries (AGL-3330), as a console create writes it.\n ...newFormListFields({ id: input.id, displayName: name, slug, routing: form.routing }),\n ...stamps,\n })\n }\n return {\n ok: true,\n replayed: false,\n id: input.id,\n versionId,\n name,\n hostSubdomain: subdomainOf(host),\n }\n })\n}\n\nexport type AiDraftNodesUpdate =\n | { ok: true; changed: boolean }\n | { ok: false; status: 404; error: string }\n\n/**\n * Change the tree of a versioned draft the job wrote, in one transaction:\n * read the first version as it is stored NOW — a member may have opened and\n * saved it — hand it to `update`, and store what comes back with a fresh\n * `updatedAt`, which is what the besigner's save guard compares. `update`\n * answering `null` changes nothing: a pass run again after its write.\n */\nexport async function updateAiDraftNodes(\n firestore: Firestore,\n input: {\n kind: AiVersionedDraftKind\n hostId: string\n id: string\n now: Date\n update: (nodes: NodesMap) => NodesMap | null\n },\n): Promise<AiDraftNodesUpdate> {\n const draftRef = draftCollection(firestore, input.hostId, input.kind).doc(input.id)\n return firestore.runTransaction(async (tx): Promise<AiDraftNodesUpdate> => {\n const draft = await tx.get(draftRef)\n const versionId = draft.exists && draft.get('deletedAt') == null ? draft.get('versionId') : null\n if (typeof versionId !== 'string' || !versionId) {\n return { ok: false, status: 404, error: 'The draft is no longer on the site' }\n }\n const versionRef = draftRef.collection('versions').doc(versionId)\n const version = await tx.get(versionRef)\n const nodes = version.exists ? decodeStoredNodes<NodesMap>(version.get('nodes')) : null\n if (!nodes) return { ok: false, status: 404, error: 'The draft is no longer on the site' }\n const next = input.update(nodes)\n if (!next) return { ok: true, changed: false }\n const packed = encodeStoredNodes(next)\n if (!packed) throw new Error('an AI draft needs a node map')\n tx.update(versionRef, { nodes: Buffer.from(packed), updatedAt: input.now })\n return { ok: true, changed: true }\n })\n}\n\n/**\n * Put a search title and description on a draft screen the job wrote, each\n * only where the draft has none: a value a member typed since is theirs.\n */\nexport async function writeAiDraftScreenSeo(\n firestore: Firestore,\n input: {\n hostId: string\n id: string\n seo: { title?: string | null; description?: string | null }\n now: Date\n },\n): Promise<void> {\n const ref = draftCollection(firestore, input.hostId, 'screen').doc(input.id)\n await firestore.runTransaction(async (tx) => {\n const screen = await tx.get(ref)\n if (!screen.exists || screen.get('deletedAt') != null) return\n const patch: Record<string, unknown> = {}\n for (const key of ['title', 'description'] as const) {\n const value = String(input.seo[key] ?? '').trim()\n if (value && !String(screen.get(`seo.${key}`) ?? '').trim()) patch[`seo.${key}`] = value\n }\n if (Object.keys(patch).length) tx.update(ref, { ...patch, updatedAt: input.now })\n })\n}\n\n/** The site's subdomain, which a console link names the site by. */\nexport async function aiSiteSubdomain(firestore: Firestore, hostId: string): Promise<string | null> {\n return subdomainOf(await firestore.collection('hosts').doc(hostId).get())\n}\n\n/**\n * What a door says before a job that writes a draft of this kind exists: a\n * site is named, and it is the job's own org's; whatever the kind checks of\n * its own; then the allowance. `null` admits the job.\n */\nexport async function aiDraftAdmissionRefusal(\n firestore: Firestore,\n input: {\n orgId: string\n hostId: string | null\n kind: AiDraftKind\n /** What the refusal calls the job's output; the kind when absent. */\n noun?: string\n org: object | null\n /** The kind's own check, asked once the site is known to be the org's. */\n ownCheck?: (hostId: string) => Promise<AiJobAdmissionRefusal | null>\n },\n): Promise<AiJobAdmissionRefusal | null> {\n if (!input.hostId) {\n return {\n status: 400,\n error: `Open the site the ${input.noun ?? input.kind} is for before starting the job`,\n }\n }\n const owner = await resolveOrgIdForHost(input.hostId)\n if (!owner || owner !== input.orgId) return { status: 404, error: 'Unknown site' }\n const own = await input.ownCheck?.(input.hostId)\n if (own) return own\n const refusal = await aiDraftAllowanceRefusal(firestore, {\n kind: input.kind,\n hostId: input.hostId,\n org: input.org as Partial<AglynOrgBilling> | null,\n })\n return refusal ? { status: 403, error: refusal } : null\n}\n"],"names":["createResourceUid","uniqueDuplicateName","FORM_COMPONENT_ID","newFormListFields","normalizeFormSlug","artifactCreateListKeys","checkDatasetQuota","checkEntitlement","checkQuota","resolveEffectivePlan","resolveOrgEntitlements","billableScreenIds","normalizeScreenSlug","reservedScreenRouteSegment","SCREEN_ROOT_PATH","decodeStoredNodes","encodeStoredNodes","resolveOrgIdForHost","aiUnrestrictedPlanCapabilities","AI_DRAFT_FIELDS","layout","template","form","component","screen","AI_DRAFT_VERSION_FIELDS","AI_DRAFT_SCREEN_VERSION_FIELDS","AI_DRAFT_VERSION_NAME","AI_DRAFT_ENTITLEMENT_REFUSAL","AI_DRAFT_BANDS","collection","quotaKey","label","entitlement","draftCollection","firestore","hostId","kind","doc","siblingsOf","select","siblingRows","snapshot","docs","map","id","name","String","get","slug","sourceType","deletedAt","deleted","subdomainOf","host","subdomain","aiDraftBandRefusal","rows","org","routingMap","band","used","filter","row","undefined","length","size","quota","allowed","limit","AI_PLAN_DRAFT_KINDS","bandLabel","count","replace","planCreation","left","reason","Number","POSITIVE_INFINITY","isFinite","refusal","Math","max","countsRows","aiPlanCapabilitiesFrom","unrestricted","datasets","reusableComponents","create","dataset","freeTaste","readAiPlanCapabilities","input","counted","snapshots","Promise","all","Object","fromEntries","index","aiDraftAllowanceRefusal","resolve","aiDraftScreenSlug","requested","defaultHomeScreenId","usable","placeholder","path","Boolean","taken","Set","entries","value","has","base","attempt","recordOf","draft","versionId","seo","hostSubdomain","readAiDraft","exists","readAiTemplateDraftNodes","readAiDraftComponentProps","props","Array","isArray","prop","readAiDraftNodes","draftRef","version","nodes","allowListed","data","key","withFormCaption","formNodeId","node","componentId","formName","writeAiDraft","packed","Error","rootId","hostRef","runTransaction","tx","existing","siblings","ok","status","error","replayed","replacesPlaceholder","stamps","createdAt","now","updatedAt","createdBy","uid","marker","aiJobId","Buffer","from","displayName","layoutId","screenId","source","type","captioned","fields","consentFieldName","routing","updateAiDraftNodes","versionRef","next","update","changed","writeAiDraftScreenSeo","ref","patch","trim","keys","aiSiteSubdomain","aiDraftAdmissionRefusal","noun","owner","orgId","own","ownCheck"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-drafts.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 { createResourceUid } from '@aglyn/aglyn/app-utils/create-resource-uid'\nimport { uniqueDuplicateName } from '@aglyn/aglyn/app-utils/duplicate-resource'\nimport {\n FORM_COMPONENT_ID,\n newFormListFields,\n normalizeFormSlug,\n type FormFieldDecl,\n type FormRouting,\n} from '@aglyn/aglyn/app-utils/forms'\nimport { artifactCreateListKeys } from '@aglyn/aglyn/app-utils/artifact-list-keys'\nimport {\n checkDatasetQuota,\n checkEntitlement,\n checkQuota,\n resolveEffectivePlan,\n resolveOrgEntitlements,\n} from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n billableScreenIds,\n normalizeScreenSlug,\n reservedScreenRouteSegment,\n SCREEN_ROOT_PATH,\n} from '@aglyn/aglyn/app-utils/screen-route'\nimport { decodeStoredNodes, encodeStoredNodes } from '@aglyn/aglyn/app-utils/stored-nodes'\nimport type {\n AglynOrgBilling,\n OrgFeatureFlags,\n} from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/platform.types'\nimport type { NodesMap } from '@aglyn/aglyn/types/nodes'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport {\n aiUnrestrictedPlanCapabilities,\n type AiPlanCapabilities,\n type AiPlanCreation,\n} from '../model/ai-plan-capabilities'\nimport type { AiJobAdmissionRefusal } from './ai-job-admission'\n\n/**\n * Where a generation job's layout, page template, form, reusable component\n * or page lands (AGL-2909, AGL-2913, AGL-2908, AGL-2907): a new draft, made\n * as the console's host resources route makes one.\n *\n * ── The document the create route makes ─────────────────────────────────\n *\n * `AI_DRAFT_FIELDS` is that route's allow-list for each kind, and a spec\n * reads the route's source to hold the two together. Nothing outside the\n * list is written except the route's own stamps: `createdAt`, `updatedAt`,\n * `createdBy`, the list keys every artifact create carries\n * (`artifactCreateListKeys`: the name keys, a component's or template's stored\n * kind, a template's library row), and a template's `source`, which is\n * `authored` — a template a member's job generated is theirs, never a starter\n * or a listing. A layout's or a screen's first version carries the keys the\n * versions route seeds one with. A form is the document the Forms page's\n * Create sends: its design canvas-shaped under `rootId` with the form node\n * bound to the form's id and captioned with its name, a slug read from that\n * name, and no version — the form's page mints the first one when someone\n * opens it. Where Create sends an empty `fields`, a generated form sends the\n * declaration read off its own design, so the two agree from the start. A\n * component carries its tree and the props it declares on its own document,\n * as Use template writes one, and gets its first version where every\n * component does: when a member opens it.\n *\n * ── Counted like a create ────────────────────────────────────────────────\n *\n * The band is met INSIDE the transaction that writes (AGL-2231), with the\n * route's arithmetic: every layout document counts against\n * `sharedLayoutsPerHost`; a template counts against `templatesPerHost` when it\n * records a source other than a platform starter — the route's `!=` query,\n * which never matches a document with no `source.type` at all; a screen counts\n * against `screensPerHost` by `billableScreenIds`, over the screens and the\n * host's routing map, which counts an unrouted screen too (AGL-1445); and a\n * form needs the plan to include the route's `entitlement` first, refused in\n * the route's own words, after which every form document counts against\n * `formsPerHost`. A reusable component counts against `componentsPerHost`\n * (AGL-3615) — Free 1, every paid plan unlimited — over its LIVE documents,\n * since a deleted component keeps its document with `deletedAt` stamped; on\n * an unlimited plan nothing is counted.\n *\n * ── Applied to nothing ───────────────────────────────────────────────────\n *\n * A layout renders only around a screen or layout that names it, or as the\n * built-in page layout the host document names; a template is inert until a\n * member uses it; a form renders only where a page places it; a component\n * renders only where an instance places it, and a new one has no instance;\n * and a screen serves nothing until the host's routing map names it, which is\n * what publishing writes (`publishScreenRoute`). So the writer touches the new\n * documents and nothing else — no host document, no routing map, no\n * collection, store setting, other layout or component — and never a\n * `publishedAt` or a publish schedule. What the job hands back is a link to\n * the draft, never a binding.\n *\n * ── One draft per recorded id ────────────────────────────────────────────\n *\n * The caller names the document id: the console resource id the job recorded\n * for the draft before its first write (`ai-job-draft-ids.ts`). A step run\n * again after its draft was written — a process cut off before the machine\n * recorded the step — reads the same id, finds that draft and reports it,\n * rather than writing a second.\n */\n\ntype Firestore = FirebaseFirestore.Firestore\n\nexport type AiDraftKind = 'layout' | 'template' | 'form' | 'component' | 'screen'\n\n/** The draft kinds that keep their tree in versions, as the besigner opens them. */\nexport type AiVersionedDraftKind = Extract<AiDraftKind, 'layout' | 'screen'>\n\n/** The console host resources route's allow-list for each kind. */\nexport const AI_DRAFT_FIELDS: Readonly<Record<AiDraftKind, readonly string[]>> = {\n layout: ['displayName', 'description', 'versionId'],\n template: [\n 'kind',\n 'displayName',\n 'description',\n 'placeholders',\n 'nodes',\n 'rootId',\n 'props',\n 'slug',\n 'seo',\n // A component template's own kind (AGL-3287); a job never sends one.\n 'componentKind',\n ],\n form: [\n 'displayName',\n 'slug',\n 'fields',\n 'consentFieldName',\n 'routing',\n 'legacyMatch',\n 'rootId',\n 'nodes',\n ],\n // `kind` marks a reusable email block (AGL-3287); a job makes page\n // components and never sends one.\n component: ['displayName', 'description', 'rootId', 'nodes', 'props', 'kind'],\n // `kind` is on the route's list for the email composer; a page has none, and\n // the writer never sends one.\n screen: ['displayName', 'description', 'slug', 'seo', 'kind', 'versionId'],\n}\n\n/** The keys a layout's first version is seeded with, all on the versions route's list. */\nexport const AI_DRAFT_VERSION_FIELDS: readonly string[] = ['layoutId', 'hostId', 'displayName', 'nodes']\n\n/**\n * The keys a screen's first version is seeded with, all on the versions\n * route's list: the screen it belongs to, the site, the label, the tree, and\n * the layout it renders inside when the plan names one the site has.\n */\nexport const AI_DRAFT_SCREEN_VERSION_FIELDS: readonly string[] = [\n 'screenId',\n 'hostId',\n 'displayName',\n 'nodes',\n 'layoutId',\n]\n\n/** The label on a generated draft's first version, as on one Use template makes. */\nexport const AI_DRAFT_VERSION_NAME = 'Initial version'\n\n/** The route's refusal when the site's plan does not include the kind at all. */\nexport const AI_DRAFT_ENTITLEMENT_REFUSAL = 'This feature is not included in your plan — see Billing'\n\nexport interface AiDraftBand {\n collection: 'layouts' | 'templates' | 'forms' | 'components' | 'screens'\n /** The allowance the route counts the kind against, where it counts one. */\n quotaKey?:\n | 'sharedLayoutsPerHost'\n | 'templatesPerHost'\n | 'formsPerHost'\n | 'componentsPerHost'\n | 'screensPerHost'\n /** The feature the plan must include before any document of the kind counts. */\n entitlement?: keyof OrgFeatureFlags\n /** The route's plural label, in the refusal it gives. */\n label: string\n}\n\n/** Each kind's collection, counter, feature and label, as the create route declares them. */\nexport const AI_DRAFT_BANDS: Readonly<Record<AiDraftKind, AiDraftBand>> = {\n layout: { collection: 'layouts', quotaKey: 'sharedLayoutsPerHost', label: 'shared layouts' },\n template: { collection: 'templates', quotaKey: 'templatesPerHost', label: 'templates' },\n // A saved form is counted against `formsPerHost` alone — Free 1 through\n // the ceiling — and is not behind `reusableComponents` (AGL-3597).\n form: { collection: 'forms', quotaKey: 'formsPerHost', label: 'forms' },\n // A reusable component is counted against `componentsPerHost` alone — Free\n // 1, every paid plan unlimited — and is not behind `reusableComponents`\n // (AGL-3615), the way a saved form left it.\n component: { collection: 'components', quotaKey: 'componentsPerHost', label: 'reusable components' },\n screen: { collection: 'screens', quotaKey: 'screensPerHost', label: 'pages' },\n}\n\ninterface SiblingRow {\n id: string\n name: string\n slug: string\n kind: unknown\n sourceType: unknown\n deletedAt: unknown\n deleted: boolean\n}\n\n/** A host's routing map: screen id → the path it is published at. */\ntype RoutingMap = Record<string, unknown> | null | undefined\n\nfunction draftCollection(firestore: Firestore, hostId: string, kind: AiDraftKind) {\n return firestore.collection('hosts').doc(hostId).collection(AI_DRAFT_BANDS[kind].collection)\n}\n\n/** The fields the band, the name, the address and a deletion are read from. */\nfunction siblingsOf(collection: FirebaseFirestore.CollectionReference) {\n return collection.select('displayName', 'slug', 'kind', 'source.type', 'deletedAt')\n}\n\nfunction siblingRows(snapshot: FirebaseFirestore.QuerySnapshot): SiblingRow[] {\n return snapshot.docs.map((doc) => ({\n id: doc.id,\n name: String(doc.get('displayName') ?? ''),\n slug: String(doc.get('slug') ?? ''),\n kind: doc.get('kind'),\n sourceType: doc.get('source.type'),\n deletedAt: doc.get('deletedAt'),\n deleted: doc.get('deletedAt') != null,\n }))\n}\n\nfunction subdomainOf(host: FirebaseFirestore.DocumentSnapshot): string | null {\n const subdomain = host.get('subdomain')\n return typeof subdomain === 'string' && subdomain ? subdomain : null\n}\n\n/**\n * The route's refusal when the site's plan does not include the kind, or\n * holds no more of it; `null` while it does. The feature is asked first, as\n * the route asks it before it counts.\n */\nexport function aiDraftBandRefusal(\n kind: AiDraftKind,\n rows: ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>>,\n org: Partial<AglynOrgBilling> | null,\n routingMap?: RoutingMap,\n): string | null {\n const band = AI_DRAFT_BANDS[kind]\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) {\n return AI_DRAFT_ENTITLEMENT_REFUSAL\n }\n if (!band.quotaKey) return null\n const used =\n kind === 'template'\n ? rows.filter((row) => row.sourceType !== undefined && row.sourceType !== 'starter').length\n : kind === 'screen'\n ? billableScreenIds(\n rows.map((row) => ({ id: row.id, kind: row.kind, deletedAt: row.deletedAt })),\n (routingMap ?? undefined) as never,\n ).size\n : liveRows(kind, rows).length\n const quota = checkQuota(org, band.quotaKey, used)\n return quota.allowed\n ? null\n : `Your plan includes ${quota.limit} ${band.label} — upgrade in Billing for more`\n}\n\n/**\n * The rows a kind's allowance counts: every one, except a component's, where\n * a deletion stamps `deletedAt` and keeps the document — the route counts the\n * live ones, so a Free site that deleted its one component may make another\n * (AGL-3615).\n */\nfunction liveRows<Row extends { deletedAt?: unknown }>(kind: AiDraftKind, rows: ReadonlyArray<Row>): ReadonlyArray<Row> {\n return kind === 'component' ? rows.filter((row) => row.deletedAt == null) : rows\n}\n\n/**\n * Whether a job builds a page's repeats as reusable components (AGL-3071,\n * AGL-3615): only where the site's component allowance is unlimited. A\n * finite one — Free's 1 — cannot hold every repeat a page or a site plan\n * draws, and the doctrine's rule 1 would then demand components the plan may\n * not create; so there the repeats are drawn inline and the allowance is the\n * member's to spend, as it was while Free had no components at all.\n */\nexport function aiBuildsWithComponents(org: Partial<AglynOrgBilling> | null): boolean {\n return !Number.isFinite(resolveOrgEntitlements(org).componentsPerHost)\n}\n\n/** The draft kinds a plan's creations land as, whose band a plan is told about (AGL-3030). */\nconst AI_PLAN_DRAFT_KINDS = ['component', 'form', 'layout', 'template'] as const\n\ntype AiPlanDraftKind = (typeof AI_PLAN_DRAFT_KINDS)[number]\n\n/** Every draft kind's plural label made singular, for a band of one. */\nfunction bandLabel(label: string, count: number): string {\n return count === 1 ? label.replace(/s$/, '') : label\n}\n\n/**\n * Whether the site may take `used + 1` of a kind, in the create route's\n * arithmetic: the feature first, then the allowance. `null` for a kind whose\n * allowance is unlimited or counts nothing, which needs no rows read.\n */\nfunction planCreation(\n kind: AiPlanDraftKind,\n org: Partial<AglynOrgBilling> | null,\n rows: ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>> | null,\n): AiPlanCreation {\n const band = AI_DRAFT_BANDS[kind]\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) {\n return {\n allowed: false,\n left: 0,\n reason: `this workspace's plan does not include ${kind === 'form' ? 'saved forms' : band.label}`,\n }\n }\n const limit = band.quotaKey ? resolveOrgEntitlements(org)[band.quotaKey] : Number.POSITIVE_INFINITY\n if (!rows || !Number.isFinite(limit)) return { allowed: true, left: null, reason: null }\n // The route's own arithmetic decides; the count beside it is what is left.\n const refusal = aiDraftBandRefusal(kind, rows, org)\n const used =\n kind === 'template'\n ? rows.filter((row) => row.sourceType !== undefined && row.sourceType !== 'starter').length\n : liveRows(kind, rows).length\n return refusal\n ? {\n allowed: false,\n left: 0,\n reason: `this site already holds the ${limit} ${bandLabel(band.label, limit)} its plan includes`,\n }\n : { allowed: true, left: Math.max(0, limit - used), reason: null }\n}\n\n/** Whether a kind's rows are counted at all on this plan: included, and under a finite allowance. */\nfunction countsRows(kind: AiPlanDraftKind, org: Partial<AglynOrgBilling> | null): boolean {\n const band = AI_DRAFT_BANDS[kind]\n if (!band.quotaKey) return false\n if (band.entitlement && !checkEntitlement(org, band.entitlement)) return false\n return Number.isFinite(resolveOrgEntitlements(org)[band.quotaKey])\n}\n\n/**\n * What a plan may create on this site (AGL-3030), from the workspace's plan\n * and the rows each counted kind already holds — pure, so the specs and the\n * eval harness hold the arithmetic without a Firestore. A kind whose rows\n * are not given is counted as having room, which is what an unlimited\n * allowance means.\n *\n * A theme change is a proposal a member applies, never a draft, so it counts\n * against nothing. A dataset is org data that no job writes; a plan may name\n * one only where the workspace's plan includes datasets at all. An email\n * design is written by the email plugin, whose own gate decides.\n *\n * A workspace on the Free plan spends the Free taste, a wall of monthly\n * credits, so its plan is held to the sections that wall pays for (AGL-3070).\n * The effective plan decides, as it decides the taste's own allowance: a\n * workspace whose subscription died is Free again.\n */\nexport function aiPlanCapabilitiesFrom(\n org: Partial<AglynOrgBilling> | null,\n rows: Partial<Record<AiPlanDraftKind, ReadonlyArray<Pick<SiblingRow, 'id' | 'kind' | 'sourceType' | 'deletedAt'>>>> = {},\n): AiPlanCapabilities {\n const unrestricted = aiUnrestrictedPlanCapabilities()\n const datasets = checkDatasetQuota(org, 0).limit > 0\n return {\n reusableComponents: aiBuildsWithComponents(org),\n create: {\n ...unrestricted.create,\n component: planCreation('component', org, rows.component ?? null),\n form: planCreation('form', org, rows.form ?? null),\n layout: planCreation('layout', org, rows.layout ?? null),\n template: planCreation('template', org, rows.template ?? null),\n dataset: datasets\n ? unrestricted.create.dataset\n : { allowed: false, left: 0, reason: \"this workspace's plan does not include datasets\" },\n },\n ...(resolveEffectivePlan(org) === 'free' ? { freeTaste: true } : {}),\n }\n}\n\n/**\n * What a plan may create on this site, read: the rows of each kind whose\n * allowance the workspace's plan counts, as the draft writer reads them, and\n * nothing for a kind the plan leaves out or leaves unlimited.\n */\nexport async function readAiPlanCapabilities(\n firestore: Firestore,\n input: { hostId: string; org: Partial<AglynOrgBilling> | null },\n): Promise<AiPlanCapabilities> {\n const counted = AI_PLAN_DRAFT_KINDS.filter((kind) => countsRows(kind, input.org))\n const snapshots = await Promise.all(\n counted.map((kind) => siblingsOf(draftCollection(firestore, input.hostId, kind)).get()),\n )\n return aiPlanCapabilitiesFrom(\n input.org,\n Object.fromEntries(counted.map((kind, index) => [kind, siblingRows(snapshots[index])])),\n )\n}\n\n/**\n * Whether the site can take one more draft of the kind: a read, for a door\n * that asks before it spends. The write asks again inside its transaction.\n */\nexport async function aiDraftAllowanceRefusal(\n firestore: Firestore,\n input: { kind: AiDraftKind; hostId: string; org: Partial<AglynOrgBilling> | null },\n): Promise<string | null> {\n const [snapshot, host] = await Promise.all([\n siblingsOf(draftCollection(firestore, input.hostId, input.kind)).get(),\n input.kind === 'screen'\n ? firestore.collection('hosts').doc(input.hostId).get()\n : Promise.resolve(null),\n ])\n return aiDraftBandRefusal(\n input.kind,\n siblingRows(snapshot),\n input.org,\n host?.get('screens') as RoutingMap,\n )\n}\n\n/**\n * A draft screen's one-segment address: the one asked for, else one from its\n * name, never a reserved segment, and never an address a live screen keeps\n * or the routing map already serves — so the member's publish at it later\n * takes nothing from a live page. The root is asked for only while no screen\n * holds it.\n *\n * Except the placeholder home page a site is created with (AGL-3408), which\n * holds `/` only until a real home page is published there: the guided start\n * is offered to exactly the sites that still have it, and the home page it\n * drafts has to be able to ask for the root. Publishing it replaces the\n * placeholder in the same write.\n */\nexport function aiDraftScreenSlug(\n requested: string | null | undefined,\n name: string,\n rows: ReadonlyArray<Pick<SiblingRow, 'slug' | 'deleted'> & { id?: string }>,\n routingMap: RoutingMap,\n defaultHomeScreenId?: string | null,\n): string {\n const placeholder = (id: string | undefined, path: unknown) =>\n Boolean(defaultHomeScreenId) && id === defaultHomeScreenId && path === SCREEN_ROOT_PATH\n const taken = new Set<string>([\n ...rows\n .filter((row) => !row.deleted && row.slug && !placeholder(row.id, row.slug))\n .map((row) => row.slug),\n ...Object.entries(routingMap ?? {})\n .filter(([id, path]) => typeof path === 'string' && !placeholder(id, path))\n .map(([, path]) => path as string),\n ])\n const usable = (value: string | undefined) =>\n value && !reservedScreenRouteSegment(value) && !(value === SCREEN_ROOT_PATH && taken.has(value))\n ? value\n : undefined\n const base = usable(normalizeScreenSlug(requested ?? undefined)) ?? usable(normalizeScreenSlug(name)) ?? 'page'\n if (base === SCREEN_ROOT_PATH) return base\n let slug = base\n let attempt = 2\n while (taken.has(slug)) slug = `${base}-${attempt++}`\n return slug\n}\n\nexport interface AiDraftRecord {\n id: string\n /** A layout's or a screen's first version; `null` for every other kind, which the writer gives none. */\n versionId: string | null\n name: string\n hostSubdomain: string | null\n /** A screen's search listing as the draft holds it now; absent on other kinds. */\n seo?: { title?: string; description?: string } | null\n /** Whether a member deleted the draft since it was written. */\n deleted?: boolean\n}\n\nfunction recordOf(\n id: string,\n draft: FirebaseFirestore.DocumentSnapshot,\n host: FirebaseFirestore.DocumentSnapshot,\n): AiDraftRecord {\n const versionId = draft.get('versionId')\n const seo = draft.get('seo')\n return {\n id,\n versionId: typeof versionId === 'string' && versionId ? versionId : null,\n name: String(draft.get('displayName') ?? ''),\n hostSubdomain: subdomainOf(host),\n ...(seo && typeof seo === 'object' ? { seo: seo as AiDraftRecord['seo'] } : {}),\n ...(draft.get('deletedAt') != null ? { deleted: true } : {}),\n }\n}\n\n/** The draft a job already wrote under `id`, when a run of its step got that far. */\nexport async function readAiDraft(\n firestore: Firestore,\n input: { kind: AiDraftKind; hostId: string; id: string },\n): Promise<AiDraftRecord | null> {\n const draft = await draftCollection(firestore, input.hostId, input.kind).doc(input.id).get()\n if (!draft.exists) return null\n return recordOf(input.id, draft, await firestore.collection('hosts').doc(input.hostId).get())\n}\n\n/**\n * The tree a template draft holds now, decoded; `null` when there is none.\n *\n * A template is not a versioned draft — its nodes live on the document\n * itself, which is why `readAiDraftNodes` refuses its kind.\n */\nexport async function readAiTemplateDraftNodes(\n firestore: Firestore,\n input: { hostId: string; id: string },\n): Promise<NodesMap | null> {\n const draft = await draftCollection(firestore, input.hostId, 'template').doc(input.id).get()\n if (!draft.exists) return null\n return decodeStoredNodes<NodesMap>(draft.get('nodes')) ?? null\n}\n\n/**\n * The property names a component draft declares now; `null` when there is no\n * such draft. Read from the draft document rather than its version, because a\n * component's props live beside its tree and a copy carries its source's.\n */\nexport async function readAiDraftComponentProps(\n firestore: Firestore,\n input: { hostId: string; id: string },\n): Promise<string[] | null> {\n const draft = await draftCollection(firestore, input.hostId, 'component').doc(input.id).get()\n if (!draft.exists) return null\n const props = draft.get('props')\n if (!Array.isArray(props)) return []\n return props\n .map((prop) => (prop && typeof prop === 'object' ? (prop as Record<string, unknown>)['name'] : null))\n .filter((name): name is string => typeof name === 'string' && name.length > 0)\n}\n\n/** The tree a versioned draft's first version holds now, decoded; `null` when there is none. */\nexport async function readAiDraftNodes(\n firestore: Firestore,\n input: { kind: AiVersionedDraftKind; hostId: string; id: string },\n): Promise<{ versionId: string; nodes: NodesMap } | null> {\n const draftRef = draftCollection(firestore, input.hostId, input.kind).doc(input.id)\n const draft = await draftRef.get()\n const versionId = draft.exists ? draft.get('versionId') : null\n if (typeof versionId !== 'string' || !versionId) return null\n const version = await draftRef.collection('versions').doc(versionId).get()\n const nodes = version.exists ? decodeStoredNodes<NodesMap>(version.get('nodes')) : null\n return nodes ? { versionId, nodes } : null\n}\n\nexport interface AiDraftInput {\n kind: AiDraftKind\n hostId: string\n /** The draft's document id: the id the job recorded for this draft. */\n id: string\n /** Whose work the draft is: the job's creator, whose brief it built. */\n uid: string\n org: Partial<AglynOrgBilling> | null\n /** The name asked for; a live sibling already called that gets a number. */\n name: string\n nodes: NodesMap\n /** A page template's suggested address, or a screen's one-segment address. */\n slug?: string | null\n /** A form's declaration and the ids its design hangs from; required for a form. */\n form?: AiFormDraftDeclaration\n /** A component's root, which the node map holds; required for a component. */\n rootId?: string\n /** The properties a component declares, which its tree binds. */\n props?: readonly ReusableComponentProp[]\n /** A screen's layout, for its first version to render inside; a layout the site has. */\n layoutId?: string | null\n /**\n * The job the member started that wrote this draft (AGL-3596), stamped as\n * `aiJobId` on a page's or a layout's document and first version, so staff\n * can tell a generated page from a copy someone made. A scaffold's unit\n * names the site job, not the unit's derived id.\n */\n aiJobId?: string | null\n now: Date\n}\n\n/** What a form draft declares beside its design, read off that design. */\nexport interface AiFormDraftDeclaration {\n /** The canvas root `nodes` is stored under, as the Forms page's Create stores it. */\n rootId: string\n /** The form node, which carries the form's id and is captioned with the draft's name. */\n formNodeId: string\n fields: FormFieldDecl[]\n consentFieldName: string | null\n routing: FormRouting | null\n}\n\nexport type AiDraftWrite =\n | (AiDraftRecord & { ok: true; replayed: boolean })\n | { ok: false; status: 403 | 404; error: string }\n\n/** Keep only the keys the create route admits for the kind. */\nfunction allowListed(kind: AiDraftKind, data: Record<string, unknown>): Record<string, unknown> {\n const allowed = new Set(AI_DRAFT_FIELDS[kind])\n return Object.fromEntries(\n Object.entries(data).filter(([key, value]) => allowed.has(key) && value !== undefined),\n )\n}\n\n/** The node map with its form node captioned with `name`, as Create captions a new form. */\nfunction withFormCaption(nodes: NodesMap, formNodeId: string, name: string): NodesMap {\n const node = nodes[formNodeId]\n if (!node || node.componentId !== FORM_COMPONENT_ID) return nodes\n return { ...nodes, [formNodeId]: { ...node, props: { ...(node.props ?? {}), formName: name } } }\n}\n\n/**\n * Write one draft: the band met, the name made unique among the live\n * siblings, the node map stored as msgpack, all in one transaction. A\n * refusal is returned, never thrown; a Firestore failure propagates, so the\n * machine's retry applies.\n */\nexport async function writeAiDraft(firestore: Firestore, input: AiDraftInput): Promise<AiDraftWrite> {\n const packed = encodeStoredNodes(input.nodes)\n if (!packed) throw new Error('an AI draft needs a node map')\n if (input.kind === 'form' && !input.form) throw new Error('an AI form draft needs its declaration')\n if (input.kind === 'component' && !(input.rootId && input.nodes[input.rootId])) {\n throw new Error('a component draft needs a root its node map holds')\n }\n const hostRef = firestore.collection('hosts').doc(input.hostId)\n const collection = draftCollection(firestore, input.hostId, input.kind)\n const draftRef = collection.doc(input.id)\n return firestore.runTransaction(async (tx): Promise<AiDraftWrite> => {\n // Every read before any write, which Firestore requires.\n const [host, existing, siblings] = await Promise.all([\n tx.get(hostRef),\n tx.get(draftRef),\n tx.get(siblingsOf(collection)),\n ])\n if (!host.exists) return { ok: false, status: 404, error: 'Unknown site' }\n if (existing.exists) {\n return { ok: true, replayed: true, ...recordOf(input.id, existing, host) }\n }\n const rows = siblingRows(siblings)\n // Read, never written: the routing map decides which screens count.\n const routingMap = host.get('screens') as RoutingMap\n const refusal = aiDraftBandRefusal(input.kind, rows, input.org, routingMap)\n if (refusal) return { ok: false, status: 403, error: refusal }\n const defaultHomeScreenId = host.get('defaultHomeScreenId') as string | null | undefined\n // A screen draft that takes the root the placeholder home page holds\n // replaces that page when it is published (AGL-3408), so it is named as\n // the page it replaces, \"Home\", rather than as a copy of it (AGL-3596).\n const slug =\n input.kind === 'screen'\n ? aiDraftScreenSlug(input.slug, input.name, rows, routingMap, defaultHomeScreenId)\n : null\n const replacesPlaceholder = (row: SiblingRow) =>\n slug === SCREEN_ROOT_PATH &&\n Boolean(defaultHomeScreenId) &&\n row.id === defaultHomeScreenId &&\n (routingMap ?? {})[row.id] === SCREEN_ROOT_PATH\n const name = uniqueDuplicateName(\n input.name,\n rows.filter((row) => !row.deleted && !replacesPlaceholder(row)).map((row) => row.name),\n )\n const stamps = { createdAt: input.now, updatedAt: input.now, createdBy: input.uid }\n const marker = input.aiJobId ? { aiJobId: input.aiJobId } : {}\n const nodes = Buffer.from(packed)\n let versionId: string | null = null\n if (input.kind === 'layout') {\n versionId = createResourceUid()\n tx.create(draftRef, {\n ...allowListed('layout', { displayName: name, versionId }),\n ...artifactCreateListKeys('layouts', { displayName: name }),\n ...stamps,\n ...marker,\n })\n tx.create(draftRef.collection('versions').doc(versionId), {\n layoutId: input.id,\n hostId: input.hostId,\n displayName: AI_DRAFT_VERSION_NAME,\n nodes,\n ...stamps,\n ...marker,\n })\n } else if (input.kind === 'screen') {\n versionId = createResourceUid()\n tx.create(draftRef, {\n ...allowListed('screen', { displayName: name, slug, versionId }),\n ...artifactCreateListKeys('screens', { displayName: name }),\n ...stamps,\n ...marker,\n })\n tx.create(draftRef.collection('versions').doc(versionId), {\n screenId: input.id,\n hostId: input.hostId,\n displayName: AI_DRAFT_VERSION_NAME,\n nodes,\n ...(input.layoutId ? { layoutId: input.layoutId } : {}),\n ...stamps,\n ...marker,\n })\n } else if (input.kind === 'template') {\n tx.create(draftRef, {\n ...allowListed('template', {\n kind: 'page',\n displayName: name,\n nodes,\n slug: input.slug || undefined,\n }),\n source: { type: 'authored' },\n ...artifactCreateListKeys('templates', {\n kind: 'page',\n displayName: name,\n source: { type: 'authored' },\n }),\n ...stamps,\n })\n } else if (input.kind === 'component') {\n tx.create(draftRef, {\n ...allowListed('component', {\n displayName: name,\n rootId: input.rootId,\n nodes,\n props: input.props?.length ? [...input.props] : undefined,\n }),\n ...artifactCreateListKeys('components', { displayName: name }),\n ...stamps,\n })\n } else {\n const form = input.form as AiFormDraftDeclaration\n const captioned = encodeStoredNodes(withFormCaption(input.nodes, form.formNodeId, name)) ?? packed\n const slug = normalizeFormSlug(name) || input.id\n tx.create(draftRef, {\n ...allowListed('form', {\n displayName: name,\n slug,\n fields: form.fields,\n consentFieldName: form.consentFieldName || undefined,\n routing: form.routing ?? undefined,\n rootId: form.rootId,\n nodes: Buffer.from(captioned),\n }),\n // What the Forms list queries (AGL-3330), as a console create writes it.\n ...newFormListFields({ id: input.id, displayName: name, slug, routing: form.routing }),\n ...stamps,\n })\n }\n return {\n ok: true,\n replayed: false,\n id: input.id,\n versionId,\n name,\n hostSubdomain: subdomainOf(host),\n }\n })\n}\n\nexport type AiDraftNodesUpdate =\n | { ok: true; changed: boolean }\n | { ok: false; status: 404; error: string }\n\n/**\n * Change the tree of a versioned draft the job wrote, in one transaction:\n * read the first version as it is stored NOW — a member may have opened and\n * saved it — hand it to `update`, and store what comes back with a fresh\n * `updatedAt`, which is what the besigner's save guard compares. `update`\n * answering `null` changes nothing: a pass run again after its write.\n */\nexport async function updateAiDraftNodes(\n firestore: Firestore,\n input: {\n kind: AiVersionedDraftKind\n hostId: string\n id: string\n now: Date\n update: (nodes: NodesMap) => NodesMap | null\n },\n): Promise<AiDraftNodesUpdate> {\n const draftRef = draftCollection(firestore, input.hostId, input.kind).doc(input.id)\n return firestore.runTransaction(async (tx): Promise<AiDraftNodesUpdate> => {\n const draft = await tx.get(draftRef)\n const versionId = draft.exists && draft.get('deletedAt') == null ? draft.get('versionId') : null\n if (typeof versionId !== 'string' || !versionId) {\n return { ok: false, status: 404, error: 'The draft is no longer on the site' }\n }\n const versionRef = draftRef.collection('versions').doc(versionId)\n const version = await tx.get(versionRef)\n const nodes = version.exists ? decodeStoredNodes<NodesMap>(version.get('nodes')) : null\n if (!nodes) return { ok: false, status: 404, error: 'The draft is no longer on the site' }\n const next = input.update(nodes)\n if (!next) return { ok: true, changed: false }\n const packed = encodeStoredNodes(next)\n if (!packed) throw new Error('an AI draft needs a node map')\n tx.update(versionRef, { nodes: Buffer.from(packed), updatedAt: input.now })\n return { ok: true, changed: true }\n })\n}\n\n/**\n * Put a search title and description on a draft screen the job wrote, each\n * only where the draft has none: a value a member typed since is theirs.\n */\nexport async function writeAiDraftScreenSeo(\n firestore: Firestore,\n input: {\n hostId: string\n id: string\n seo: { title?: string | null; description?: string | null }\n now: Date\n },\n): Promise<void> {\n const ref = draftCollection(firestore, input.hostId, 'screen').doc(input.id)\n await firestore.runTransaction(async (tx) => {\n const screen = await tx.get(ref)\n if (!screen.exists || screen.get('deletedAt') != null) return\n const patch: Record<string, unknown> = {}\n for (const key of ['title', 'description'] as const) {\n const value = String(input.seo[key] ?? '').trim()\n if (value && !String(screen.get(`seo.${key}`) ?? '').trim()) patch[`seo.${key}`] = value\n }\n if (Object.keys(patch).length) tx.update(ref, { ...patch, updatedAt: input.now })\n })\n}\n\n/** The site's subdomain, which a console link names the site by. */\nexport async function aiSiteSubdomain(firestore: Firestore, hostId: string): Promise<string | null> {\n return subdomainOf(await firestore.collection('hosts').doc(hostId).get())\n}\n\n/**\n * What a door says before a job that writes a draft of this kind exists: a\n * site is named, and it is the job's own org's; whatever the kind checks of\n * its own; then the allowance. `null` admits the job.\n */\nexport async function aiDraftAdmissionRefusal(\n firestore: Firestore,\n input: {\n orgId: string\n hostId: string | null\n kind: AiDraftKind\n /** What the refusal calls the job's output; the kind when absent. */\n noun?: string\n org: object | null\n /** The kind's own check, asked once the site is known to be the org's. */\n ownCheck?: (hostId: string) => Promise<AiJobAdmissionRefusal | null>\n },\n): Promise<AiJobAdmissionRefusal | null> {\n if (!input.hostId) {\n return {\n status: 400,\n error: `Open the site the ${input.noun ?? input.kind} is for before starting the job`,\n }\n }\n const owner = await resolveOrgIdForHost(input.hostId)\n if (!owner || owner !== input.orgId) return { status: 404, error: 'Unknown site' }\n const own = await input.ownCheck?.(input.hostId)\n if (own) return own\n const refusal = await aiDraftAllowanceRefusal(firestore, {\n kind: input.kind,\n hostId: input.hostId,\n org: input.org as Partial<AglynOrgBilling> | null,\n })\n return refusal ? { status: 403, error: refusal } : null\n}\n"],"names":["createResourceUid","uniqueDuplicateName","FORM_COMPONENT_ID","newFormListFields","normalizeFormSlug","artifactCreateListKeys","checkDatasetQuota","checkEntitlement","checkQuota","resolveEffectivePlan","resolveOrgEntitlements","billableScreenIds","normalizeScreenSlug","reservedScreenRouteSegment","SCREEN_ROOT_PATH","decodeStoredNodes","encodeStoredNodes","resolveOrgIdForHost","aiUnrestrictedPlanCapabilities","AI_DRAFT_FIELDS","layout","template","form","component","screen","AI_DRAFT_VERSION_FIELDS","AI_DRAFT_SCREEN_VERSION_FIELDS","AI_DRAFT_VERSION_NAME","AI_DRAFT_ENTITLEMENT_REFUSAL","AI_DRAFT_BANDS","collection","quotaKey","label","draftCollection","firestore","hostId","kind","doc","siblingsOf","select","siblingRows","snapshot","docs","map","id","name","String","get","slug","sourceType","deletedAt","deleted","subdomainOf","host","subdomain","aiDraftBandRefusal","rows","org","routingMap","band","entitlement","used","filter","row","undefined","length","size","liveRows","quota","allowed","limit","aiBuildsWithComponents","Number","isFinite","componentsPerHost","AI_PLAN_DRAFT_KINDS","bandLabel","count","replace","planCreation","left","reason","POSITIVE_INFINITY","refusal","Math","max","countsRows","aiPlanCapabilitiesFrom","unrestricted","datasets","reusableComponents","create","dataset","freeTaste","readAiPlanCapabilities","input","counted","snapshots","Promise","all","Object","fromEntries","index","aiDraftAllowanceRefusal","resolve","aiDraftScreenSlug","requested","defaultHomeScreenId","usable","placeholder","path","Boolean","taken","Set","entries","value","has","base","attempt","recordOf","draft","versionId","seo","hostSubdomain","readAiDraft","exists","readAiTemplateDraftNodes","readAiDraftComponentProps","props","Array","isArray","prop","readAiDraftNodes","draftRef","version","nodes","allowListed","data","key","withFormCaption","formNodeId","node","componentId","formName","writeAiDraft","packed","Error","rootId","hostRef","runTransaction","tx","existing","siblings","ok","status","error","replayed","replacesPlaceholder","stamps","createdAt","now","updatedAt","createdBy","uid","marker","aiJobId","Buffer","from","displayName","layoutId","screenId","source","type","captioned","fields","consentFieldName","routing","updateAiDraftNodes","versionRef","next","update","changed","writeAiDraftScreenSeo","ref","patch","trim","keys","aiSiteSubdomain","aiDraftAdmissionRefusal","noun","owner","orgId","own","ownCheck"],"mappings":"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"}
@@ -100,12 +100,20 @@ export declare function aiDoctrineReviewOutline(answer: Record<string, unknown>
100
100
  * entries it names, and an outline of those parts of the answer it was found
101
101
  * in (AGL-3078), so why a job stopped can be read back from the job once the
102
102
  * answer itself is gone.
103
+ *
104
+ * The doctrine's own sentence ("Rule 12 (Responsive by the theme's
105
+ * breakpoints): ...") is never what the customer reads (AGL-3596): it rides
106
+ * as `detail`, for staff, and `message` is the plain refusal, a page's when
107
+ * `page` says the refused thing is one, followed by `then` when given.
103
108
  */
104
109
  export declare function aiDoctrineReview(result: {
105
110
  message: string;
106
111
  violations: readonly AiDoctrineViolation[];
107
112
  /** The refused answer, whose nodes the findings name by id; outlined, never kept. */
108
113
  answer?: Record<string, unknown> | null;
114
+ }, options?: {
115
+ page?: boolean;
116
+ then?: string;
109
117
  }): AiJobReview;
110
118
  /** The review a step stops its job for when the site cannot take the draft. */
111
119
  export declare function aiLimitReview(message: string): AiJobReview;