@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232

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 (305) hide show
  1. package/package.json +17 -17
  2. package/src/lib/activity/ai-activity-actions.js +8 -4
  3. package/src/lib/activity/ai-activity-actions.js.map +1 -1
  4. package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
  5. package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
  6. package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
  7. package/src/lib/components/ai-assist-provider.component.js +9 -10
  8. package/src/lib/components/ai-assist-provider.component.js.map +1 -1
  9. package/src/lib/components/ai-job-follow.component.js +1 -0
  10. package/src/lib/components/ai-job-follow.component.js.map +1 -1
  11. package/src/lib/components/ai-job-links.js +13 -0
  12. package/src/lib/components/ai-job-links.js.map +1 -1
  13. package/src/lib/components/ai-job-plan.component.js +3 -1
  14. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  15. package/src/lib/components/ai-jobs-list-page.component.js +1 -0
  16. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  17. package/src/lib/components/ai-media-create.component.d.ts +20 -7
  18. package/src/lib/components/ai-media-create.component.js +107 -55
  19. package/src/lib/components/ai-media-create.component.js.map +1 -1
  20. package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
  21. package/src/lib/components/ai-permissions-on-host.component.js +5 -53
  22. package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
  23. package/src/lib/components/ai-seo-fields-card.component.js +1 -0
  24. package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
  25. package/src/lib/components/ai-site-build-page.component.js +20 -8
  26. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  27. package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
  28. package/src/lib/components/ai-site-memory-card.component.js +187 -0
  29. package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
  30. package/src/lib/components/ai-site-start-card.component.js +80 -77
  31. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  32. package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
  33. package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
  34. package/src/lib/components/ai-usage-strip.component.js +1 -0
  35. package/src/lib/components/ai-usage-strip.component.js.map +1 -1
  36. package/src/lib/components/assist-panel.component.d.ts +10 -0
  37. package/src/lib/components/assist-panel.component.js +119 -9
  38. package/src/lib/components/assist-panel.component.js.map +1 -1
  39. package/src/lib/components/assist-signals-page.component.js +2 -0
  40. package/src/lib/components/assist-signals-page.component.js.map +1 -1
  41. package/src/lib/components/billing-ai-allotments.component.js +3 -0
  42. package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
  43. package/src/lib/components/billing-ai-top-users.component.js +1 -0
  44. package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
  45. package/src/lib/components/billing-assist-overage-card.component.js +2 -0
  46. package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
  47. package/src/lib/components/member-ai-usage-card.component.js +10 -0
  48. package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
  49. package/src/lib/components/staff-org-ai-card.component.js +1 -0
  50. package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
  51. package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
  52. package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
  53. package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
  54. package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
  55. package/src/lib/jobs/ai-build-capabilities.js +101 -7
  56. package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
  57. package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
  58. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
  59. package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
  60. package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
  61. package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
  62. package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
  63. package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
  64. package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
  65. package/src/lib/jobs/ai-job-build-step.js +14 -1
  66. package/src/lib/jobs/ai-job-build-step.js.map +1 -1
  67. package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
  68. package/src/lib/jobs/ai-job-edit-step.js +631 -0
  69. package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
  70. package/src/lib/jobs/ai-job-generation.d.ts +8 -0
  71. package/src/lib/jobs/ai-job-generation.js +13 -2
  72. package/src/lib/jobs/ai-job-generation.js.map +1 -1
  73. package/src/lib/jobs/ai-job-insight-step.js +29 -5
  74. package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
  75. package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
  76. package/src/lib/jobs/ai-job-layout-language.js +200 -0
  77. package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
  78. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
  79. package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
  80. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
  81. package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
  82. package/src/lib/jobs/ai-job-layout-step.js +107 -6
  83. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  84. package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
  85. package/src/lib/jobs/ai-job-logic-step.js +87 -6
  86. package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
  87. package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
  88. package/src/lib/jobs/ai-job-page-language.js +418 -0
  89. package/src/lib/jobs/ai-job-page-language.js.map +1 -0
  90. package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
  91. package/src/lib/jobs/ai-job-page-sections.js +93 -11
  92. package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
  93. package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
  94. package/src/lib/jobs/ai-job-page-step.js +115 -14
  95. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  96. package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
  97. package/src/lib/jobs/ai-job-plan-step.js +62 -22
  98. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  99. package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
  100. package/src/lib/jobs/ai-job-site-content.js +443 -0
  101. package/src/lib/jobs/ai-job-site-content.js.map +1 -0
  102. package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
  103. package/src/lib/jobs/ai-job-site-look.js +322 -0
  104. package/src/lib/jobs/ai-job-site-look.js.map +1 -0
  105. package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
  106. package/src/lib/jobs/ai-job-site-step.js +183 -27
  107. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  108. package/src/lib/jobs/ai-job-workflow-step.js +2 -2
  109. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  110. package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
  111. package/src/lib/jobs/ai-jobs-notify.js +15 -3
  112. package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
  113. package/src/lib/jobs/ai-jobs.d.ts +1 -1
  114. package/src/lib/jobs/ai-jobs.js +17 -8
  115. package/src/lib/jobs/ai-jobs.js.map +1 -1
  116. package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
  117. package/src/lib/jobs/ai-layout-full-height.js +82 -0
  118. package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
  119. package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
  120. package/src/lib/jobs/ai-layout-site-facts.js +153 -0
  121. package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
  122. package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
  123. package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
  124. package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
  125. package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
  126. package/src/lib/layout-language/ai-layout-copy.js +137 -0
  127. package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
  128. package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
  129. package/src/lib/layout-language/ai-layout-frame.js +458 -0
  130. package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
  131. package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
  132. package/src/lib/layout-language/ai-layout-gaps.js +345 -0
  133. package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
  134. package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
  135. package/src/lib/layout-language/ai-layout-language.js +529 -0
  136. package/src/lib/layout-language/ai-layout-language.js.map +1 -0
  137. package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
  138. package/src/lib/layout-language/ai-layout-links.js +109 -0
  139. package/src/lib/layout-language/ai-layout-links.js.map +1 -0
  140. package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
  141. package/src/lib/layout-language/ai-layout-pictures.js +214 -0
  142. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
  143. package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
  144. package/src/lib/layout-language/ai-layout-store.js +75 -0
  145. package/src/lib/layout-language/ai-layout-store.js.map +1 -0
  146. package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
  147. package/src/lib/layout-language/ai-layout-tree.js +84 -0
  148. package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
  149. package/src/lib/lazy-widget.d.ts +45 -0
  150. package/src/lib/lazy-widget.js +53 -0
  151. package/src/lib/lazy-widget.js.map +1 -0
  152. package/src/lib/model/ai-build-job.js +2 -1
  153. package/src/lib/model/ai-build-job.js.map +1 -1
  154. package/src/lib/model/ai-build-progress.d.ts +20 -3
  155. package/src/lib/model/ai-build-progress.js +52 -7
  156. package/src/lib/model/ai-build-progress.js.map +1 -1
  157. package/src/lib/model/ai-edit-job.d.ts +109 -0
  158. package/src/lib/model/ai-edit-job.js +157 -0
  159. package/src/lib/model/ai-edit-job.js.map +1 -0
  160. package/src/lib/model/ai-host-publish-context.d.ts +51 -0
  161. package/src/lib/model/ai-host-publish-context.js +104 -0
  162. package/src/lib/model/ai-host-publish-context.js.map +1 -0
  163. package/src/lib/model/ai-job-activity.d.ts +22 -0
  164. package/src/lib/model/ai-job-activity.js +23 -1
  165. package/src/lib/model/ai-job-activity.js.map +1 -1
  166. package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
  167. package/src/lib/model/ai-job-failure-copy.js +56 -1
  168. package/src/lib/model/ai-job-failure-copy.js.map +1 -1
  169. package/src/lib/model/ai-job-notice.d.ts +3 -1
  170. package/src/lib/model/ai-job-notice.js +31 -9
  171. package/src/lib/model/ai-job-notice.js.map +1 -1
  172. package/src/lib/model/ai-jobs.types.d.ts +22 -1
  173. package/src/lib/model/ai-jobs.types.js.map +1 -1
  174. package/src/lib/model/ai-logic-job.d.ts +18 -1
  175. package/src/lib/model/ai-logic-job.js +24 -3
  176. package/src/lib/model/ai-logic-job.js.map +1 -1
  177. package/src/lib/model/ai-media-credits.d.ts +8 -4
  178. package/src/lib/model/ai-media-credits.js +11 -4
  179. package/src/lib/model/ai-media-credits.js.map +1 -1
  180. package/src/lib/model/ai-media-kinds.d.ts +68 -0
  181. package/src/lib/model/ai-media-kinds.js +98 -0
  182. package/src/lib/model/ai-media-kinds.js.map +1 -0
  183. package/src/lib/model/ai-site-build-progress.d.ts +9 -1
  184. package/src/lib/model/ai-site-build-progress.js +54 -14
  185. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  186. package/src/lib/model/ai-site-context.d.ts +111 -0
  187. package/src/lib/model/ai-site-context.js +222 -0
  188. package/src/lib/model/ai-site-context.js.map +1 -0
  189. package/src/lib/model/ai-site-job.d.ts +18 -2
  190. package/src/lib/model/ai-site-job.js +21 -5
  191. package/src/lib/model/ai-site-job.js.map +1 -1
  192. package/src/lib/model/ai-site-kinds.d.ts +164 -0
  193. package/src/lib/model/ai-site-kinds.js +1942 -0
  194. package/src/lib/model/ai-site-kinds.js.map +1 -0
  195. package/src/lib/model/ai-site-look.d.ts +150 -0
  196. package/src/lib/model/ai-site-look.js +1236 -0
  197. package/src/lib/model/ai-site-look.js.map +1 -0
  198. package/src/lib/model/ai-site-memory.d.ts +80 -0
  199. package/src/lib/model/ai-site-memory.js +171 -0
  200. package/src/lib/model/ai-site-memory.js.map +1 -0
  201. package/src/lib/model/ai-site-start-seo.d.ts +1 -1
  202. package/src/lib/model/ai-site-start.d.ts +15 -3
  203. package/src/lib/model/ai-site-start.js +21 -4
  204. package/src/lib/model/ai-site-start.js.map +1 -1
  205. package/src/lib/model/assist-follow-up.d.ts +92 -0
  206. package/src/lib/model/assist-follow-up.js +128 -0
  207. package/src/lib/model/assist-follow-up.js.map +1 -0
  208. package/src/lib/plugin-config.js +2 -0
  209. package/src/lib/plugin-config.js.map +1 -1
  210. package/src/lib/plugin.js +99 -48
  211. package/src/lib/plugin.js.map +1 -1
  212. package/src/lib/providers/anthropic.d.ts +9 -1
  213. package/src/lib/providers/anthropic.js +143 -9
  214. package/src/lib/providers/anthropic.js.map +1 -1
  215. package/src/lib/providers/catalog.d.ts +38 -8
  216. package/src/lib/providers/catalog.js +106 -14
  217. package/src/lib/providers/catalog.js.map +1 -1
  218. package/src/lib/providers/contract.d.ts +29 -0
  219. package/src/lib/providers/contract.js.map +1 -1
  220. package/src/lib/providers/image-contract.d.ts +22 -1
  221. package/src/lib/providers/image-contract.js +40 -1
  222. package/src/lib/providers/image-contract.js.map +1 -1
  223. package/src/lib/providers/image-endpoints.d.ts +24 -0
  224. package/src/lib/providers/image-endpoints.js +28 -0
  225. package/src/lib/providers/image-endpoints.js.map +1 -0
  226. package/src/lib/providers/model-choice.js +23 -2
  227. package/src/lib/providers/model-choice.js.map +1 -1
  228. package/src/lib/providers/routing.js +18 -0
  229. package/src/lib/providers/routing.js.map +1 -1
  230. package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
  231. package/src/lib/providers/vertex-image-endpoint.js +80 -0
  232. package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
  233. package/src/lib/providers/vertex-image.d.ts +13 -27
  234. package/src/lib/providers/vertex-image.js +15 -42
  235. package/src/lib/providers/vertex-image.js.map +1 -1
  236. package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
  237. package/src/lib/runtime/ai-blog-post-generation.js +89 -0
  238. package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
  239. package/src/lib/runtime/ai-dev-env.d.ts +53 -0
  240. package/src/lib/runtime/ai-dev-env.js +72 -0
  241. package/src/lib/runtime/ai-dev-env.js.map +1 -0
  242. package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
  243. package/src/lib/runtime/ai-dev-replay.js +313 -0
  244. package/src/lib/runtime/ai-dev-replay.js.map +1 -0
  245. package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
  246. package/src/lib/runtime/ai-doctrine-validators.js +436 -4
  247. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  248. package/src/lib/runtime/ai-doctrine.js +16 -3
  249. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  250. package/src/lib/runtime/ai-eval-live.js +4 -0
  251. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  252. package/src/lib/runtime/ai-eval.d.ts +13 -1
  253. package/src/lib/runtime/ai-eval.js +84 -1
  254. package/src/lib/runtime/ai-eval.js.map +1 -1
  255. package/src/lib/runtime/ai-icon-library.d.ts +257 -0
  256. package/src/lib/runtime/ai-icon-library.js +254 -0
  257. package/src/lib/runtime/ai-icon-library.js.map +1 -0
  258. package/src/lib/runtime/ai-live-batch.d.ts +45 -0
  259. package/src/lib/runtime/ai-live-batch.js +89 -0
  260. package/src/lib/runtime/ai-live-batch.js.map +1 -0
  261. package/src/lib/runtime/ai-node-tree.d.ts +9 -0
  262. package/src/lib/runtime/ai-node-tree.js +30 -4
  263. package/src/lib/runtime/ai-node-tree.js.map +1 -1
  264. package/src/lib/runtime/ai-palette.d.ts +6 -0
  265. package/src/lib/runtime/ai-palette.generated.js +70 -0
  266. package/src/lib/runtime/ai-palette.generated.js.map +1 -1
  267. package/src/lib/runtime/ai-palette.js.map +1 -1
  268. package/src/lib/runtime/ai-runtime.js +46 -2
  269. package/src/lib/runtime/ai-runtime.js.map +1 -1
  270. package/src/lib/runtime/site-context.d.ts +58 -0
  271. package/src/lib/runtime/site-context.js +127 -0
  272. package/src/lib/runtime/site-context.js.map +1 -0
  273. package/src/lib/server/ai-admin-conversations.d.ts +22 -0
  274. package/src/lib/server/ai-admin-conversations.js +197 -0
  275. package/src/lib/server/ai-admin-conversations.js.map +1 -0
  276. package/src/lib/server/ai-media-image.d.ts +5 -5
  277. package/src/lib/server/ai-media-image.js +25 -11
  278. package/src/lib/server/ai-media-image.js.map +1 -1
  279. package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
  280. package/src/lib/server/ai-media-raster-prompt.js +53 -0
  281. package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
  282. package/src/lib/server/assist-chat.d.ts +11 -0
  283. package/src/lib/server/assist-chat.js +57 -18
  284. package/src/lib/server/assist-chat.js.map +1 -1
  285. package/src/lib/server/assist-edit-applied.js +16 -3
  286. package/src/lib/server/assist-edit-applied.js.map +1 -1
  287. package/src/lib/server/assist-view-context.d.ts +19 -1
  288. package/src/lib/server/assist-view-context.js +38 -1
  289. package/src/lib/server/assist-view-context.js.map +1 -1
  290. package/src/lib/server.js +10 -0
  291. package/src/lib/server.js.map +1 -1
  292. package/src/lib/subprocessors.js +17 -1
  293. package/src/lib/subprocessors.js.map +1 -1
  294. package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
  295. package/src/lib/tools/ai-blog-post-tool.js +175 -0
  296. package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
  297. package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
  298. package/src/lib/tools/ai-workflow-tool.js +16 -14
  299. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  300. package/src/lib/usage/assist-usage.d.ts +3 -0
  301. package/src/lib/usage/assist-usage.js +4 -2
  302. package/src/lib/usage/assist-usage.js.map +1 -1
  303. package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
  304. package/src/lib/usage/staff-org-ai-conversations.js +152 -0
  305. package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
@@ -19,7 +19,7 @@ import { isHostPluginEnabled } from "@aglyn/aglyn/plugin-manager/enabled-plugins
19
19
  import { filterEnabledPluginsByReleaseFlags } from "@aglyn/tenant-data-admin/server/release-flags";
20
20
  import { resolveOrgIdForHost } from "@aglyn/tenant-data-admin/server/organizations";
21
21
  import { aiWorkflowExplanationText } from "../model/ai-automation-outline.js";
22
- import { AI_LOGIC_GONE_COPY, AI_LOGIC_MAX_OPERATIONS, AI_LOGIC_MAX_PARAMETERS, AI_LOGIC_NO_EXPLANATION_COPY, AI_LOGIC_NO_FUNCTION_COPY, AI_LOGIC_NO_SITE_COPY, AI_LOGIC_NO_VARIABLE_COPY, AI_LOGIC_RESOURCE, AI_LOGIC_UNAVAILABLE_COPY, checkAiLogicFunction, checkAiLogicVariable, parseAiLogicJobInputs } from "../model/ai-logic-job.js";
22
+ import { AI_LOGIC_FUNCTION_RESOURCE, AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY, AI_LOGIC_FUNCTION_UNAVAILABLE_COPY, AI_LOGIC_FUNCTION_WRITTEN_NOTE, AI_LOGIC_GONE_COPY, AI_LOGIC_MAX_OPERATIONS, AI_LOGIC_MAX_PARAMETERS, AI_LOGIC_NO_EXPLANATION_COPY, AI_LOGIC_NO_FUNCTION_COPY, AI_LOGIC_NO_SITE_COPY, AI_LOGIC_NO_VARIABLE_COPY, AI_LOGIC_RESOURCE, AI_LOGIC_UNAVAILABLE_COPY, aiLogicJobIsBuildUnit, checkAiLogicFunction, checkAiLogicVariable, parseAiLogicJobInputs } from "../model/ai-logic-job.js";
23
23
  import { AI_STEP_TIERS } from "../providers/catalog.js";
24
24
  import { AI_ROUTING_TABLE, aiModelForStep } from "../providers/routing.js";
25
25
  import { runValidatedGeneration } from "../runtime/ai-doctrine.js";
@@ -27,7 +27,8 @@ import { aiLogicFunctionTool, aiLogicVariableTool, AI_LOGIC_FUNCTION_TOOL_NAME,
27
27
  import { AI_WORKFLOW_EXPLANATION_TOOL_NAME, aiWorkflowExplanationTool, readAiWorkflowExplanation } from "../tools/ai-workflow-tool.js";
28
28
  import { registerAiJobAdmission } from "./ai-job-admission.js";
29
29
  import { aiJobStepBudget } from "./ai-job-budget.js";
30
- import { aiGenerationSpent, aiUnspentOutcome } from "./ai-job-generation.js";
30
+ import { aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from "./ai-job-generation.js";
31
+ import { aiPluginDraftWriter } from "./ai-job-plugin-drafts.js";
31
32
  import { AI_JOB_BRIEF_MAX_CHARS } from "./ai-job-text-step.js";
32
33
  import { registerAiJobStep } from "./ai-jobs.js";
33
34
  import { readAiLogicFunction, readAiLogicRecords } from "./ai-logic-records.js";
@@ -45,6 +46,14 @@ import { readAiLogicFunction, readAiLogicRecords } from "./ai-logic-records.js";
45
46
  *
46
47
  * Nothing is written. A proposal rides on a `logic` output, and the logic
47
48
  * editor opens it for a person to save.
49
+ *
50
+ * Except for a build's unit (AGL-3616): a job derived for a `function` item
51
+ * of a confirmed build plan — it carries `inputs.originJobId` — writes the
52
+ * NEW function it checked, through the `function` draft writer the logic
53
+ * plugin registers on the core's resource-drafts seam (this plugin imports
54
+ * no other). The writer's refusal is asked before anything is spent, the
55
+ * record is written under the unit job's id so a run asked again finds it,
56
+ * and the output is a `draft` the build's ledger records.
48
57
  */ /** The longest the step may take reading the site first. */ export const AI_LOGIC_RECORDS_READ_MS = 2000;
49
58
  /** The step's time: its answer and its re-ask at the `job.logic` ceiling, with its own reads. */ export const AI_JOB_LOGIC_STEP_BUDGET = aiJobStepBudget({
50
59
  tier: AI_STEP_TIERS['job.logic'],
@@ -204,6 +213,18 @@ function routedEffort() {
204
213
  check: readAiWorkflowExplanation
205
214
  });
206
215
  }
216
+ /** A function a build's unit wrote, as the build's ledger records it. */ export function aiLogicFunctionDraftOutput(place, record) {
217
+ return {
218
+ resource: 'draft',
219
+ id: record.id,
220
+ versionId: null,
221
+ hostId: place.hostId,
222
+ hostSubdomain: place.hostSubdomain,
223
+ label: record.name,
224
+ draftResource: AI_LOGIC_FUNCTION_RESOURCE,
225
+ note: AI_LOGIC_FUNCTION_WRITTEN_NOTE
226
+ };
227
+ }
207
228
  function proposalOutput(place, id, label, proposal, note) {
208
229
  return {
209
230
  resource: AI_LOGIC_RESOURCE,
@@ -216,11 +237,12 @@ function proposalOutput(place, id, label, proposal, note) {
216
237
  };
217
238
  }
218
239
  export function createAiJobLogicStep(deps = {}) {
219
- var _deps_readRecords, _deps_readFunction;
240
+ var _deps_readRecords, _deps_readFunction, _deps_writerFor;
220
241
  const readRecords = (_deps_readRecords = deps.readRecords) != null ? _deps_readRecords : readAiLogicRecords;
221
242
  const readFunction = (_deps_readFunction = deps.readFunction) != null ? _deps_readFunction : readAiLogicFunction;
222
- return async ({ job, signal, firestore, modelFor })=>{
223
- var _ref, _ref1;
243
+ const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : aiPluginDraftWriter;
244
+ return async ({ job, now, signal, firestore, org, modelFor })=>{
245
+ var _ref, _ref1, _ref2;
224
246
  const model = (_ref = modelFor == null ? void 0 : modelFor('job.logic')) != null ? _ref : aiJobLogicModel();
225
247
  const unspent = (failure)=>_extends({}, aiUnspentOutcome(model), {
226
248
  failure
@@ -237,6 +259,37 @@ export function createAiJobLogicStep(deps = {}) {
237
259
  hostSubdomain: typeof subdomain === 'string' && subdomain ? subdomain : null
238
260
  };
239
261
  const functionId = inputs.mode === 'variable' ? null : inputs.functionId;
262
+ // A build's new function is written, not proposed: the writer is found,
263
+ // asked for what it already wrote under this unit's id, and asked whether
264
+ // this member may have one, all before anything is spent.
265
+ const writes = inputs.mode === 'function' && !functionId && aiLogicJobIsBuildUnit(job);
266
+ const writer = writes ? writerFor(AI_LOGIC_FUNCTION_RESOURCE) : null;
267
+ const draftContext = writes ? {
268
+ orgId: job.orgId,
269
+ hostId,
270
+ uid: job.createdBy,
271
+ org: (_ref1 = org != null ? org : (await firestore.collection('orgs').doc(job.orgId).get()).data()) != null ? _ref1 : null,
272
+ now
273
+ } : null;
274
+ if (writes) {
275
+ if (!writer || !draftContext) return unspent(AI_LOGIC_FUNCTION_UNAVAILABLE_COPY);
276
+ // The unit's job is named by its plan item's id, which is the id its draft is written under.
277
+ const written = await writer.read({
278
+ hostId,
279
+ id: job.$id
280
+ });
281
+ if (written) return aiUnspentOutcome(model, {
282
+ outputs: [
283
+ aiLogicFunctionDraftOutput(place, written)
284
+ ]
285
+ });
286
+ const refusal = await writer.refusal(draftContext);
287
+ if (refusal) {
288
+ return refusal.status === 403 ? aiUnspentOutcome(model, {
289
+ review: aiLimitReview(refusal.error)
290
+ }) : unspent(refusal.error);
291
+ }
292
+ }
240
293
  const saved = functionId ? await readFunction(firestore, {
241
294
  hostId,
242
295
  id: functionId
@@ -244,7 +297,7 @@ export function createAiJobLogicStep(deps = {}) {
244
297
  if (functionId && !saved) return unspent(AI_LOGIC_GONE_COPY);
245
298
  const records = await readRecords(firestore, hostId);
246
299
  const context = _extends({}, records, {
247
- editing: (_ref1 = saved == null ? void 0 : saved.name) != null ? _ref1 : null
300
+ editing: (_ref2 = saved == null ? void 0 : saved.name) != null ? _ref2 : null
248
301
  });
249
302
  const signalled = signal ? {
250
303
  signal
@@ -319,6 +372,34 @@ export function createAiJobLogicStep(deps = {}) {
319
372
  failure: AI_LOGIC_NO_FUNCTION_COPY
320
373
  });
321
374
  const definition = generation.value;
375
+ if (writer && draftContext) {
376
+ var _checked_problems_;
377
+ // Held to the owner's rules before it is written, as every draft is.
378
+ const content = definition;
379
+ const checked = writer.check(content, {
380
+ hostId
381
+ });
382
+ if (checked.ok === false) return _extends({}, spent, {
383
+ failure: (_checked_problems_ = checked.problems[0]) != null ? _checked_problems_ : AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY
384
+ });
385
+ const write = await writer.write(_extends({}, draftContext, {
386
+ id: job.$id,
387
+ name: definition.name,
388
+ content
389
+ }));
390
+ if (write.ok === false) {
391
+ return write.status === 403 ? _extends({}, spent, {
392
+ review: aiLimitReview(write.error)
393
+ }) : _extends({}, spent, {
394
+ failure: write.status === 409 ? write.error : AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY
395
+ });
396
+ }
397
+ return _extends({}, spent, {
398
+ outputs: [
399
+ aiLogicFunctionDraftOutput(place, write)
400
+ ]
401
+ });
402
+ }
322
403
  return _extends({}, spent, {
323
404
  outputs: [
324
405
  proposalOutput(place, functionId != null ? functionId : 'function', definition.name, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-logic-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 { FUNCTION_BUILTIN_NAMES, type HostFunction } from '@aglyn/aglyn/app-utils/functions'\nimport { isHostPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { aiWorkflowExplanationText } from '../model/ai-automation-outline'\nimport type { AiJobOutput, AiJobOutputResource } from '../model/ai-jobs.types'\nimport {\n AI_LOGIC_GONE_COPY,\n AI_LOGIC_MAX_OPERATIONS,\n AI_LOGIC_MAX_PARAMETERS,\n AI_LOGIC_NO_EXPLANATION_COPY,\n AI_LOGIC_NO_FUNCTION_COPY,\n AI_LOGIC_NO_SITE_COPY,\n AI_LOGIC_NO_VARIABLE_COPY,\n AI_LOGIC_RESOURCE,\n AI_LOGIC_UNAVAILABLE_COPY,\n checkAiLogicFunction,\n checkAiLogicVariable,\n parseAiLogicJobInputs,\n type AiLogicContext,\n type AiLogicProposal,\n type AiLogicSiteVariable,\n} from '../model/ai-logic-job'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport { runValidatedGeneration, type AiCustomGenerationInput } from '../runtime/ai-doctrine'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiLogicFunctionTool, aiLogicVariableTool, AI_LOGIC_FUNCTION_TOOL_NAME, AI_LOGIC_VARIABLE_TOOL_NAME } from '../tools/ai-logic-tool'\nimport {\n AI_WORKFLOW_EXPLANATION_TOOL_NAME,\n aiWorkflowExplanationTool,\n readAiWorkflowExplanation,\n type AiWorkflowExplanation,\n} from '../tools/ai-workflow-tool'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiGenerationSpent, aiUnspentOutcome } from './ai-job-generation'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\nimport { readAiLogicFunction, readAiLogicRecords } from './ai-logic-records'\n\n/**\n * The `logic` step (AGL-3603): a function or a variable proposed from a\n * description, a saved function changed or fixed, or one explained.\n *\n * The model is shown the expression grammar the evaluator runs — operators,\n * built-ins, how a dictionary's member is read — and, in the user turn, the\n * site's variables by name, type and value, and its function names. A saved\n * function is shown as an outline of its own definition. It answers through\n * `submit_function`, `submit_variable` or `submit_explanation`; a function\n * is held to the grammar, to the names it may read and to a first run before\n * it is kept (`checkAiLogicFunction`), and a re-ask names what failed.\n *\n * Nothing is written. A proposal rides on a `logic` output, and the logic\n * editor opens it for a person to save.\n */\n\n/** The longest the step may take reading the site first. */\nexport const AI_LOGIC_RECORDS_READ_MS = 2_000\n\n/** The step's time: its answer and its re-ask at the `job.logic` ceiling, with its own reads. */\nexport const AI_JOB_LOGIC_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.logic'],\n maxTokens: AI_ROUTING_TABLE['job.logic'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_LOGIC_RECORDS_READ_MS,\n})\n\nexport const AI_JOB_LOGIC_STEP_MINIMUM_MS = AI_JOB_LOGIC_STEP_BUDGET.minimumMs\n\nexport function aiJobLogicModel(): string {\n return aiModelForStep('job.logic')\n}\n\n/** How many site variables the user turn lists, and how much of each value. */\nexport const AI_LOGIC_VARIABLES_LISTED = 60\nexport const AI_LOGIC_VALUE_LISTED_CHARS = 120\n\n/**\n * The instructions, byte-identical on every request so they cache: one block\n * for a function, a variable and an explanation alike, because a block of\n * either half alone is under the balanced tier's cacheable minimum, and the\n * two halves together clear it on every tool.\n */\nexport const AI_JOB_LOGIC_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You work on the Functions & Variables page of a website builder, for the person who owns the site. Asked for a function, call ${AI_LOGIC_FUNCTION_TOOL_NAME}; asked for a variable, call ${AI_LOGIC_VARIABLE_TOOL_NAME}; asked what a function does, call ${AI_WORKFLOW_EXPLANATION_TOOL_NAME}. Call exactly one, once. A reply in prose cannot be used.`,\n '',\n 'A function has parameters (what it is given), locals (working values), operations run in order, and returnValue, the parameter or local whose final value it returns. Each operation is: if (left comparator right) then assign each of its then list, otherwise each of its otherwise list. A condition that should always hold is written 1 == 1.',\n '',\n 'Expressions:',\n '- Numbers, \"quoted text\", true and false; + - * / and parentheses; + also joins text.',\n `- Built-in functions only: ${FUNCTION_BUILTIN_NAMES.map((name) => `${name}()`).join(', ')}.`,\n '- Names: the function’s own parameters and locals, and the site variables the request lists, by name; a dictionary variable’s member as name.member.',\n '- An assignment sets only a parameter or a local, never a site variable.',\n '',\n 'Writing a function or a variable:',\n `- At most ${AI_LOGIC_MAX_PARAMETERS} parameters and ${AI_LOGIC_MAX_OPERATIONS} operations. Names start with a letter or _, then letters, digits or _.`,\n '- Types are number, text or boolean. A parameter a visitor fills in gets a short label; defaultValue is what the input starts with, or empty.',\n '- Use only the names and built-ins listed. Where the description needs a value the site does not hold, make it a parameter rather than inventing a number.',\n '- When the request shows a saved function to change, answer with the whole function after the change, keeping what the change does not touch.',\n '- A variable’s value is written as stored: digits; true or false; YYYY-MM-DD; HH:MM; a JSON object for a dictionary; a JSON list for a collection.',\n '',\n 'Explaining a function, in plain words for someone who does not write code:',\n '- summary: in one or two sentences, what the function computes from what it is given.',\n '- points: what it does, operation by operation, in order. One sentence each.',\n '- suggestions: what is worth checking — a condition that can never hold, a value it returns that nothing sets, a name it reads that the site does not have. Empty when there is nothing to suggest.',\n '- Explain only what you are shown, and never say you changed anything: you change nothing. Write plain text, with no markdown and no HTML.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n// ── The user turns ────────────────────────────────────────────────────────\n\nfunction variableLine(variable: AiLogicSiteVariable): string {\n const value = String(variable.value ?? '').replace(/\\s+/g, ' ')\n return `- ${variable.name} (${variable.type}) = ${value.length > AI_LOGIC_VALUE_LISTED_CHARS ? `${value.slice(0, AI_LOGIC_VALUE_LISTED_CHARS)}…` : value || '(empty)'}`\n}\n\n/** A saved function as lines a model reads: its own definition, in the editor's terms. */\nexport function aiLogicFunctionOutline(definition: HostFunction): string {\n const lines = [`Function: ${definition.name}`]\n lines.push(\n 'Parameters:',\n ...(definition.parameters.length\n ? definition.parameters.map(\n (parameter) =>\n `- ${parameter.name} (${parameter.type}${parameter.required ? ', required' : ''})${parameter.label ? ` labeled \"${parameter.label}\"` : ''}${parameter.defaultValue ? `, starts as ${parameter.defaultValue}` : ''}`,\n )\n : ['- none']),\n 'Locals:',\n ...(definition.variables.length\n ? definition.variables.map((local) => `- ${local.name} (${local.type})`)\n : ['- none']),\n 'Operations, in order:',\n )\n definition.operations.forEach((operation, index) => {\n const sets = (list: HostFunction['operations'][number]['then']) =>\n list.length ? list.map((set) => `${set.set} = ${set.workflow ? `the workflow \"${set.workflow}\"` : set.expression}`).join('; ') : 'nothing'\n lines.push(\n `${index + 1}. If ${operation.if?.left} ${operation.if?.comparator} ${operation.if?.right}: ${sets(operation.then ?? [])}. Otherwise: ${sets(operation.otherwise ?? [])}.`,\n )\n })\n lines.push(`Returns: ${definition.returnValue || '(nothing named)'}`)\n return lines.join('\\n')\n}\n\n/** What a function or a variable is asked from. */\nexport function aiJobLogicPrompt(input: {\n brief: string\n mode: 'function' | 'variable' | 'explain'\n context: AiLogicContext\n saved: HostFunction | null\n}): string {\n const variables = input.context.variables.slice(0, AI_LOGIC_VARIABLES_LISTED)\n return [\n input.mode === 'variable'\n ? 'Asked: one site variable.'\n : input.mode === 'explain'\n ? 'Asked: what this function does.'\n : input.saved\n ? 'Asked: the saved function below, changed as the request says.'\n : 'Asked: one new function.',\n 'Site variables (name (type) = value):',\n ...(variables.length ? variables.map(variableLine) : ['- none']),\n `Function names already on the site: ${input.context.functions.join(', ') || 'none'}`,\n ...(input.saved ? ['The saved function:', aiLogicFunctionOutline(input.saved)] : []),\n `Request: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ].join('\\n')\n}\n\nfunction routedEffort(): Pick<AiCustomGenerationInput<unknown>, 'thinking' | 'effort'> {\n const row = AI_ROUTING_TABLE['job.logic']\n return {\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n }\n}\n\n/** The function generation as the doctrine's loop runs it. */\nexport function aiJobLogicFunctionGeneration(request: {\n brief: string\n context: AiLogicContext\n saved: HostFunction | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<HostFunction> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'function' }) }],\n tool: aiLogicFunctionTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) => checkAiLogicFunction(answer, request.context),\n }\n}\n\n/** The variable generation. */\nexport function aiJobLogicVariableGeneration(request: {\n brief: string\n context: AiLogicContext\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiLogicSiteVariable> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'variable', saved: null }) }],\n tool: aiLogicVariableTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) => checkAiLogicVariable(answer, request.context),\n }\n}\n\n/** The explanation generation. */\nexport function aiJobLogicExplainGeneration(request: {\n brief: string\n context: AiLogicContext\n saved: HostFunction\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiWorkflowExplanation> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'explain' }) }],\n tool: aiWorkflowExplanationTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: readAiWorkflowExplanation,\n }\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobLogicStepDeps {\n readRecords?: typeof readAiLogicRecords\n readFunction?: typeof readAiLogicFunction\n}\n\nfunction proposalOutput(\n place: { hostId: string; hostSubdomain: string | null },\n id: string,\n label: string,\n proposal: AiLogicProposal,\n note: string,\n): AiJobOutput {\n return {\n resource: AI_LOGIC_RESOURCE as AiJobOutputResource,\n id,\n hostId: place.hostId,\n hostSubdomain: place.hostSubdomain,\n label,\n proposal: proposal as unknown as Record<string, unknown>,\n note,\n }\n}\n\nexport function createAiJobLogicStep(deps: AiJobLogicStepDeps = {}): AiJobStepRunner {\n const readRecords = deps.readRecords ?? readAiLogicRecords\n const readFunction = deps.readFunction ?? readAiLogicFunction\n return async ({ job, signal, firestore, modelFor }): Promise<AiJobStepOutcome> => {\n const model = modelFor?.('job.logic') ?? aiJobLogicModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiLogicJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n const hostId = job.hostId\n if (!hostId) return unspent(AI_LOGIC_NO_SITE_COPY)\n const host = await firestore.collection('hosts').doc(hostId).get()\n if (!host.exists || host.get('orgId') !== job.orgId) return unspent(AI_LOGIC_NO_SITE_COPY)\n const subdomain = host.get('subdomain')\n const place = { hostId, hostSubdomain: typeof subdomain === 'string' && subdomain ? subdomain : null }\n\n const functionId = inputs.mode === 'variable' ? null : inputs.functionId\n const saved = functionId ? await readFunction(firestore, { hostId, id: functionId }) : null\n if (functionId && !saved) return unspent(AI_LOGIC_GONE_COPY)\n const records = await readRecords(firestore, hostId)\n const context: AiLogicContext = { ...records, editing: saved?.name ?? null }\n const signalled = signal ? { signal } : {}\n\n if (inputs.mode === 'explain' && saved) {\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicExplainGeneration({ brief: job.brief, context, saved, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_EXPLANATION_COPY }\n return {\n ...spent,\n outputs: [\n {\n resource: 'text',\n id: 'explanation',\n hostId,\n hostSubdomain: place.hostSubdomain,\n label: `How ${saved.name} works`,\n text: aiWorkflowExplanationText(generation.value, 'explain'),\n },\n ],\n }\n }\n\n if (inputs.mode === 'variable') {\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicVariableGeneration({ brief: job.brief, context, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_VARIABLE_COPY }\n const variable = generation.value\n return {\n ...spent,\n outputs: [\n proposalOutput(place, 'variable', variable.name, { kind: 'variable', variable }, 'Open it in Variables to review and save it.'),\n ],\n }\n }\n\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicFunctionGeneration({ brief: job.brief, context, saved, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_FUNCTION_COPY }\n const definition = generation.value\n return {\n ...spent,\n outputs: [\n proposalOutput(\n place,\n functionId ?? 'function',\n definition.name,\n { kind: 'function', functionId, definition },\n saved\n ? `A changed ${saved.name}, checked against the expression grammar and run once. Nothing is saved until you save it in the editor.`\n : 'Checked against the expression grammar and run once. Nothing is saved until you save it in the editor.',\n ),\n ],\n }\n }\n}\n\nexport const runAiJobLogicStep = createAiJobLogicStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\n/** The plugin that keeps Functions & Variables. */\nexport const AI_LOGIC_PLUGIN_ID = 'logic'\n\nexport interface AiLogicJobAdmissionDeps {\n readFunction?: typeof readAiLogicFunction\n}\n\n/**\n * A logic job is admitted for a site of its org with Logic on for it and past\n * its release flag; a change or an explanation also needs the function it\n * names to exist.\n */\nexport function createAiLogicJobAdmission(deps: AiLogicJobAdmissionDeps = {}): AiJobAdmission {\n const readFunction = deps.readFunction ?? readAiLogicFunction\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n const inputs = parseAiLogicJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n const hostId = context.hostId\n if (!hostId) return { status: 400, error: 'Open the site the logic is for before starting the job' }\n const owner = await resolveOrgIdForHost(hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const host = (await context.firestore.collection('hosts').doc(hostId).get()).data() ?? null\n const org = context.org as { enabledPlugins?: string[] } | null\n const released = await filterEnabledPluginsByReleaseFlags([AI_LOGIC_PLUGIN_ID], {\n orgId: context.orgId,\n authorization: null,\n })\n if (!released.includes(AI_LOGIC_PLUGIN_ID) || !isHostPluginEnabled(org, host, AI_LOGIC_PLUGIN_ID)) {\n return { status: 403, error: AI_LOGIC_UNAVAILABLE_COPY }\n }\n const functionId = inputs.mode === 'variable' ? null : inputs.functionId\n if (functionId && !(await readFunction(context.firestore, { hostId, id: functionId }))) {\n return { status: 404, error: AI_LOGIC_GONE_COPY }\n }\n return null\n }\n}\n\nexport const aiLogicJobAdmission = createAiLogicJobAdmission()\n\n/** Registers the logic step and the check a logic job passes before it is created or resumed. */\nexport function registerAiLogicJob(): void {\n registerAiJobStep('logic', runAiJobLogicStep, { minimumMs: AI_JOB_LOGIC_STEP_MINIMUM_MS })\n registerAiJobAdmission('logic', aiLogicJobAdmission)\n}\n"],"names":["FUNCTION_BUILTIN_NAMES","isHostPluginEnabled","filterEnabledPluginsByReleaseFlags","resolveOrgIdForHost","aiWorkflowExplanationText","AI_LOGIC_GONE_COPY","AI_LOGIC_MAX_OPERATIONS","AI_LOGIC_MAX_PARAMETERS","AI_LOGIC_NO_EXPLANATION_COPY","AI_LOGIC_NO_FUNCTION_COPY","AI_LOGIC_NO_SITE_COPY","AI_LOGIC_NO_VARIABLE_COPY","AI_LOGIC_RESOURCE","AI_LOGIC_UNAVAILABLE_COPY","checkAiLogicFunction","checkAiLogicVariable","parseAiLogicJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","aiLogicFunctionTool","aiLogicVariableTool","AI_LOGIC_FUNCTION_TOOL_NAME","AI_LOGIC_VARIABLE_TOOL_NAME","AI_WORKFLOW_EXPLANATION_TOOL_NAME","aiWorkflowExplanationTool","readAiWorkflowExplanation","registerAiJobAdmission","aiJobStepBudget","aiGenerationSpent","aiUnspentOutcome","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","readAiLogicFunction","readAiLogicRecords","AI_LOGIC_RECORDS_READ_MS","AI_JOB_LOGIC_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_LOGIC_STEP_MINIMUM_MS","minimumMs","aiJobLogicModel","AI_LOGIC_VARIABLES_LISTED","AI_LOGIC_VALUE_LISTED_CHARS","AI_JOB_LOGIC_INSTRUCTIONS","text","map","name","join","cacheBreakpoint","variableLine","variable","value","String","replace","type","length","slice","aiLogicFunctionOutline","definition","lines","push","parameters","parameter","required","label","defaultValue","variables","local","operations","forEach","operation","index","sets","list","set","workflow","expression","if","left","comparator","right","then","otherwise","returnValue","aiJobLogicPrompt","input","context","mode","saved","functions","brief","routedEffort","row","thinking","effort","aiJobLogicFunctionGeneration","request","step","model","instructions","messages","role","content","tool","signal","check","answer","aiJobLogicVariableGeneration","aiJobLogicExplainGeneration","proposalOutput","place","id","proposal","note","resource","hostId","hostSubdomain","createAiJobLogicStep","deps","readRecords","readFunction","job","firestore","modelFor","unspent","failure","inputs","host","collection","doc","get","exists","orgId","subdomain","functionId","records","editing","signalled","generation","spent","status","refused","outputs","kind","runAiJobLogicStep","AI_LOGIC_PLUGIN_ID","createAiLogicJobAdmission","error","owner","data","org","released","authorization","includes","aiLogicJobAdmission","registerAiLogicJob"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/jobs/ai-job-logic-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 { FUNCTION_BUILTIN_NAMES, type HostFunction } from '@aglyn/aglyn/app-utils/functions'\nimport { isHostPluginEnabled } from '@aglyn/aglyn/plugin-manager/enabled-plugins'\nimport { filterEnabledPluginsByReleaseFlags } from '@aglyn/tenant-data-admin/server/release-flags'\nimport { resolveOrgIdForHost } from '@aglyn/tenant-data-admin/server/organizations'\nimport { aiWorkflowExplanationText } from '../model/ai-automation-outline'\nimport type { AiJobOutput, AiJobOutputResource } from '../model/ai-jobs.types'\nimport {\n AI_LOGIC_FUNCTION_RESOURCE,\n AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY,\n AI_LOGIC_FUNCTION_UNAVAILABLE_COPY,\n AI_LOGIC_FUNCTION_WRITTEN_NOTE,\n AI_LOGIC_GONE_COPY,\n AI_LOGIC_MAX_OPERATIONS,\n AI_LOGIC_MAX_PARAMETERS,\n AI_LOGIC_NO_EXPLANATION_COPY,\n AI_LOGIC_NO_FUNCTION_COPY,\n AI_LOGIC_NO_SITE_COPY,\n AI_LOGIC_NO_VARIABLE_COPY,\n AI_LOGIC_RESOURCE,\n AI_LOGIC_UNAVAILABLE_COPY,\n aiLogicJobIsBuildUnit,\n checkAiLogicFunction,\n checkAiLogicVariable,\n parseAiLogicJobInputs,\n type AiLogicContext,\n type AiLogicProposal,\n type AiLogicSiteVariable,\n} from '../model/ai-logic-job'\nimport { AI_STEP_TIERS } from '../providers/catalog'\nimport { AI_ROUTING_TABLE, aiModelForStep } from '../providers/routing'\nimport { runValidatedGeneration, type AiCustomGenerationInput } from '../runtime/ai-doctrine'\nimport type { AiSystemBlock } from '../runtime/ai-runtime'\nimport { aiLogicFunctionTool, aiLogicVariableTool, AI_LOGIC_FUNCTION_TOOL_NAME, AI_LOGIC_VARIABLE_TOOL_NAME } from '../tools/ai-logic-tool'\nimport {\n AI_WORKFLOW_EXPLANATION_TOOL_NAME,\n aiWorkflowExplanationTool,\n readAiWorkflowExplanation,\n type AiWorkflowExplanation,\n} from '../tools/ai-workflow-tool'\nimport { registerAiJobAdmission, type AiJobAdmission, type AiJobAdmissionRefusal } from './ai-job-admission'\nimport { aiJobStepBudget } from './ai-job-budget'\nimport { aiGenerationSpent, aiLimitReview, aiUnspentOutcome } from './ai-job-generation'\nimport { aiPluginDraftWriter, type AiPluginDraftWriterLookup } from './ai-job-plugin-drafts'\nimport { AI_JOB_BRIEF_MAX_CHARS, type AiJobStepOutcome, type AiJobStepRunner } from './ai-job-text-step'\nimport { registerAiJobStep } from './ai-jobs'\nimport { readAiLogicFunction, readAiLogicRecords } from './ai-logic-records'\n\n/**\n * The `logic` step (AGL-3603): a function or a variable proposed from a\n * description, a saved function changed or fixed, or one explained.\n *\n * The model is shown the expression grammar the evaluator runs — operators,\n * built-ins, how a dictionary's member is read — and, in the user turn, the\n * site's variables by name, type and value, and its function names. A saved\n * function is shown as an outline of its own definition. It answers through\n * `submit_function`, `submit_variable` or `submit_explanation`; a function\n * is held to the grammar, to the names it may read and to a first run before\n * it is kept (`checkAiLogicFunction`), and a re-ask names what failed.\n *\n * Nothing is written. A proposal rides on a `logic` output, and the logic\n * editor opens it for a person to save.\n *\n * Except for a build's unit (AGL-3616): a job derived for a `function` item\n * of a confirmed build plan — it carries `inputs.originJobId` — writes the\n * NEW function it checked, through the `function` draft writer the logic\n * plugin registers on the core's resource-drafts seam (this plugin imports\n * no other). The writer's refusal is asked before anything is spent, the\n * record is written under the unit job's id so a run asked again finds it,\n * and the output is a `draft` the build's ledger records.\n */\n\n/** The longest the step may take reading the site first. */\nexport const AI_LOGIC_RECORDS_READ_MS = 2_000\n\n/** The step's time: its answer and its re-ask at the `job.logic` ceiling, with its own reads. */\nexport const AI_JOB_LOGIC_STEP_BUDGET = aiJobStepBudget({\n tier: AI_STEP_TIERS['job.logic'],\n maxTokens: AI_ROUTING_TABLE['job.logic'].maxTokens,\n lookups: 0,\n ownReadsMs: AI_LOGIC_RECORDS_READ_MS,\n})\n\nexport const AI_JOB_LOGIC_STEP_MINIMUM_MS = AI_JOB_LOGIC_STEP_BUDGET.minimumMs\n\nexport function aiJobLogicModel(): string {\n return aiModelForStep('job.logic')\n}\n\n/** How many site variables the user turn lists, and how much of each value. */\nexport const AI_LOGIC_VARIABLES_LISTED = 60\nexport const AI_LOGIC_VALUE_LISTED_CHARS = 120\n\n/**\n * The instructions, byte-identical on every request so they cache: one block\n * for a function, a variable and an explanation alike, because a block of\n * either half alone is under the balanced tier's cacheable minimum, and the\n * two halves together clear it on every tool.\n */\nexport const AI_JOB_LOGIC_INSTRUCTIONS: readonly AiSystemBlock[] = [\n {\n text: [\n `You work on the Functions & Variables page of a website builder, for the person who owns the site. Asked for a function, call ${AI_LOGIC_FUNCTION_TOOL_NAME}; asked for a variable, call ${AI_LOGIC_VARIABLE_TOOL_NAME}; asked what a function does, call ${AI_WORKFLOW_EXPLANATION_TOOL_NAME}. Call exactly one, once. A reply in prose cannot be used.`,\n '',\n 'A function has parameters (what it is given), locals (working values), operations run in order, and returnValue, the parameter or local whose final value it returns. Each operation is: if (left comparator right) then assign each of its then list, otherwise each of its otherwise list. A condition that should always hold is written 1 == 1.',\n '',\n 'Expressions:',\n '- Numbers, \"quoted text\", true and false; + - * / and parentheses; + also joins text.',\n `- Built-in functions only: ${FUNCTION_BUILTIN_NAMES.map((name) => `${name}()`).join(', ')}.`,\n '- Names: the function’s own parameters and locals, and the site variables the request lists, by name; a dictionary variable’s member as name.member.',\n '- An assignment sets only a parameter or a local, never a site variable.',\n '',\n 'Writing a function or a variable:',\n `- At most ${AI_LOGIC_MAX_PARAMETERS} parameters and ${AI_LOGIC_MAX_OPERATIONS} operations. Names start with a letter or _, then letters, digits or _.`,\n '- Types are number, text or boolean. A parameter a visitor fills in gets a short label; defaultValue is what the input starts with, or empty.',\n '- Use only the names and built-ins listed. Where the description needs a value the site does not hold, make it a parameter rather than inventing a number.',\n '- When the request shows a saved function to change, answer with the whole function after the change, keeping what the change does not touch.',\n '- A variable’s value is written as stored: digits; true or false; YYYY-MM-DD; HH:MM; a JSON object for a dictionary; a JSON list for a collection.',\n '',\n 'Explaining a function, in plain words for someone who does not write code:',\n '- summary: in one or two sentences, what the function computes from what it is given.',\n '- points: what it does, operation by operation, in order. One sentence each.',\n '- suggestions: what is worth checking — a condition that can never hold, a value it returns that nothing sets, a name it reads that the site does not have. Empty when there is nothing to suggest.',\n '- Explain only what you are shown, and never say you changed anything: you change nothing. Write plain text, with no markdown and no HTML.',\n ].join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\n// ── The user turns ────────────────────────────────────────────────────────\n\nfunction variableLine(variable: AiLogicSiteVariable): string {\n const value = String(variable.value ?? '').replace(/\\s+/g, ' ')\n return `- ${variable.name} (${variable.type}) = ${value.length > AI_LOGIC_VALUE_LISTED_CHARS ? `${value.slice(0, AI_LOGIC_VALUE_LISTED_CHARS)}…` : value || '(empty)'}`\n}\n\n/** A saved function as lines a model reads: its own definition, in the editor's terms. */\nexport function aiLogicFunctionOutline(definition: HostFunction): string {\n const lines = [`Function: ${definition.name}`]\n lines.push(\n 'Parameters:',\n ...(definition.parameters.length\n ? definition.parameters.map(\n (parameter) =>\n `- ${parameter.name} (${parameter.type}${parameter.required ? ', required' : ''})${parameter.label ? ` labeled \"${parameter.label}\"` : ''}${parameter.defaultValue ? `, starts as ${parameter.defaultValue}` : ''}`,\n )\n : ['- none']),\n 'Locals:',\n ...(definition.variables.length\n ? definition.variables.map((local) => `- ${local.name} (${local.type})`)\n : ['- none']),\n 'Operations, in order:',\n )\n definition.operations.forEach((operation, index) => {\n const sets = (list: HostFunction['operations'][number]['then']) =>\n list.length ? list.map((set) => `${set.set} = ${set.workflow ? `the workflow \"${set.workflow}\"` : set.expression}`).join('; ') : 'nothing'\n lines.push(\n `${index + 1}. If ${operation.if?.left} ${operation.if?.comparator} ${operation.if?.right}: ${sets(operation.then ?? [])}. Otherwise: ${sets(operation.otherwise ?? [])}.`,\n )\n })\n lines.push(`Returns: ${definition.returnValue || '(nothing named)'}`)\n return lines.join('\\n')\n}\n\n/** What a function or a variable is asked from. */\nexport function aiJobLogicPrompt(input: {\n brief: string\n mode: 'function' | 'variable' | 'explain'\n context: AiLogicContext\n saved: HostFunction | null\n}): string {\n const variables = input.context.variables.slice(0, AI_LOGIC_VARIABLES_LISTED)\n return [\n input.mode === 'variable'\n ? 'Asked: one site variable.'\n : input.mode === 'explain'\n ? 'Asked: what this function does.'\n : input.saved\n ? 'Asked: the saved function below, changed as the request says.'\n : 'Asked: one new function.',\n 'Site variables (name (type) = value):',\n ...(variables.length ? variables.map(variableLine) : ['- none']),\n `Function names already on the site: ${input.context.functions.join(', ') || 'none'}`,\n ...(input.saved ? ['The saved function:', aiLogicFunctionOutline(input.saved)] : []),\n `Request: ${input.brief.slice(0, AI_JOB_BRIEF_MAX_CHARS)}`,\n ].join('\\n')\n}\n\nfunction routedEffort(): Pick<AiCustomGenerationInput<unknown>, 'thinking' | 'effort'> {\n const row = AI_ROUTING_TABLE['job.logic']\n return {\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n }\n}\n\n/** The function generation as the doctrine's loop runs it. */\nexport function aiJobLogicFunctionGeneration(request: {\n brief: string\n context: AiLogicContext\n saved: HostFunction | null\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<HostFunction> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'function' }) }],\n tool: aiLogicFunctionTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) => checkAiLogicFunction(answer, request.context),\n }\n}\n\n/** The variable generation. */\nexport function aiJobLogicVariableGeneration(request: {\n brief: string\n context: AiLogicContext\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiLogicSiteVariable> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'variable', saved: null }) }],\n tool: aiLogicVariableTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: (answer) => checkAiLogicVariable(answer, request.context),\n }\n}\n\n/** The explanation generation. */\nexport function aiJobLogicExplainGeneration(request: {\n brief: string\n context: AiLogicContext\n saved: HostFunction\n model: string\n signal?: AbortSignal\n}): AiCustomGenerationInput<AiWorkflowExplanation> {\n return {\n step: 'job.logic',\n model: request.model,\n instructions: AI_JOB_LOGIC_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiJobLogicPrompt({ ...request, mode: 'explain' }) }],\n tool: aiWorkflowExplanationTool(),\n maxTokens: AI_JOB_LOGIC_STEP_BUDGET.maxTokens(request.model),\n ...routedEffort(),\n ...(request.signal ? { signal: request.signal } : {}),\n check: readAiWorkflowExplanation,\n }\n}\n\n// ── The runner ────────────────────────────────────────────────────────────\n\nexport interface AiJobLogicStepDeps {\n readRecords?: typeof readAiLogicRecords\n readFunction?: typeof readAiLogicFunction\n /** How the logic plugin's `function` writer is found; the core's registry otherwise. */\n writerFor?: AiPluginDraftWriterLookup\n}\n\n/** A function a build's unit wrote, as the build's ledger records it. */\nexport function aiLogicFunctionDraftOutput(\n place: { hostId: string; hostSubdomain: string | null },\n record: { id: string; name: string },\n): AiJobOutput {\n return {\n resource: 'draft',\n id: record.id,\n versionId: null,\n hostId: place.hostId,\n hostSubdomain: place.hostSubdomain,\n label: record.name,\n draftResource: AI_LOGIC_FUNCTION_RESOURCE,\n note: AI_LOGIC_FUNCTION_WRITTEN_NOTE,\n }\n}\n\nfunction proposalOutput(\n place: { hostId: string; hostSubdomain: string | null },\n id: string,\n label: string,\n proposal: AiLogicProposal,\n note: string,\n): AiJobOutput {\n return {\n resource: AI_LOGIC_RESOURCE as AiJobOutputResource,\n id,\n hostId: place.hostId,\n hostSubdomain: place.hostSubdomain,\n label,\n proposal: proposal as unknown as Record<string, unknown>,\n note,\n }\n}\n\nexport function createAiJobLogicStep(deps: AiJobLogicStepDeps = {}): AiJobStepRunner {\n const readRecords = deps.readRecords ?? readAiLogicRecords\n const readFunction = deps.readFunction ?? readAiLogicFunction\n const writerFor = deps.writerFor ?? aiPluginDraftWriter\n return async ({ job, now, signal, firestore, org, modelFor }): Promise<AiJobStepOutcome> => {\n const model = modelFor?.('job.logic') ?? aiJobLogicModel()\n const unspent = (failure: string): AiJobStepOutcome => ({ ...aiUnspentOutcome(model), failure })\n const inputs = parseAiLogicJobInputs(job.inputs)\n if (typeof inputs === 'string') return unspent(inputs)\n const hostId = job.hostId\n if (!hostId) return unspent(AI_LOGIC_NO_SITE_COPY)\n const host = await firestore.collection('hosts').doc(hostId).get()\n if (!host.exists || host.get('orgId') !== job.orgId) return unspent(AI_LOGIC_NO_SITE_COPY)\n const subdomain = host.get('subdomain')\n const place = { hostId, hostSubdomain: typeof subdomain === 'string' && subdomain ? subdomain : null }\n\n const functionId = inputs.mode === 'variable' ? null : inputs.functionId\n\n // A build's new function is written, not proposed: the writer is found,\n // asked for what it already wrote under this unit's id, and asked whether\n // this member may have one, all before anything is spent.\n const writes = inputs.mode === 'function' && !functionId && aiLogicJobIsBuildUnit(job)\n const writer = writes ? writerFor(AI_LOGIC_FUNCTION_RESOURCE) : null\n const draftContext = writes\n ? {\n orgId: job.orgId,\n hostId,\n uid: job.createdBy,\n org: (org ?? (await firestore.collection('orgs').doc(job.orgId).get()).data() ?? null) as Record<\n string,\n unknown\n > | null,\n now,\n }\n : null\n if (writes) {\n if (!writer || !draftContext) return unspent(AI_LOGIC_FUNCTION_UNAVAILABLE_COPY)\n // The unit's job is named by its plan item's id, which is the id its draft is written under.\n const written = await writer.read({ hostId, id: job.$id })\n if (written) return aiUnspentOutcome(model, { outputs: [aiLogicFunctionDraftOutput(place, written)] })\n const refusal = await writer.refusal(draftContext)\n if (refusal) {\n return refusal.status === 403\n ? aiUnspentOutcome(model, { review: aiLimitReview(refusal.error) })\n : unspent(refusal.error)\n }\n }\n\n const saved = functionId ? await readFunction(firestore, { hostId, id: functionId }) : null\n if (functionId && !saved) return unspent(AI_LOGIC_GONE_COPY)\n const records = await readRecords(firestore, hostId)\n const context: AiLogicContext = { ...records, editing: saved?.name ?? null }\n const signalled = signal ? { signal } : {}\n\n if (inputs.mode === 'explain' && saved) {\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicExplainGeneration({ brief: job.brief, context, saved, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_EXPLANATION_COPY }\n return {\n ...spent,\n outputs: [\n {\n resource: 'text',\n id: 'explanation',\n hostId,\n hostSubdomain: place.hostSubdomain,\n label: `How ${saved.name} works`,\n text: aiWorkflowExplanationText(generation.value, 'explain'),\n },\n ],\n }\n }\n\n if (inputs.mode === 'variable') {\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicVariableGeneration({ brief: job.brief, context, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_VARIABLE_COPY }\n const variable = generation.value\n return {\n ...spent,\n outputs: [\n proposalOutput(place, 'variable', variable.name, { kind: 'variable', variable }, 'Open it in Variables to review and save it.'),\n ],\n }\n }\n\n const generation = await runValidatedGeneration(\n 'logic',\n aiJobLogicFunctionGeneration({ brief: job.brief, context, saved, model, ...signalled }),\n )\n const spent = { ...aiGenerationSpent(generation), ...(generation.effort ? { effort: generation.effort } : {}) }\n if (generation.status === 'refused') return { ...spent, refused: true }\n if (generation.status === 'needs_input') return { ...spent, failure: AI_LOGIC_NO_FUNCTION_COPY }\n const definition = generation.value\n if (writer && draftContext) {\n // Held to the owner's rules before it is written, as every draft is.\n const content = definition as unknown as Record<string, unknown>\n const checked = writer.check(content, { hostId })\n if (checked.ok === false) return { ...spent, failure: checked.problems[0] ?? AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY }\n const write = await writer.write({ ...draftContext, id: job.$id, name: definition.name, content })\n if (write.ok === false) {\n return write.status === 403\n ? { ...spent, review: aiLimitReview(write.error) }\n : { ...spent, failure: write.status === 409 ? write.error : AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY }\n }\n return { ...spent, outputs: [aiLogicFunctionDraftOutput(place, write)] }\n }\n return {\n ...spent,\n outputs: [\n proposalOutput(\n place,\n functionId ?? 'function',\n definition.name,\n { kind: 'function', functionId, definition },\n saved\n ? `A changed ${saved.name}, checked against the expression grammar and run once. Nothing is saved until you save it in the editor.`\n : 'Checked against the expression grammar and run once. Nothing is saved until you save it in the editor.',\n ),\n ],\n }\n }\n}\n\nexport const runAiJobLogicStep = createAiJobLogicStep()\n\n// ── Admission ─────────────────────────────────────────────────────────────\n\n/** The plugin that keeps Functions & Variables. */\nexport const AI_LOGIC_PLUGIN_ID = 'logic'\n\nexport interface AiLogicJobAdmissionDeps {\n readFunction?: typeof readAiLogicFunction\n}\n\n/**\n * A logic job is admitted for a site of its org with Logic on for it and past\n * its release flag; a change or an explanation also needs the function it\n * names to exist.\n */\nexport function createAiLogicJobAdmission(deps: AiLogicJobAdmissionDeps = {}): AiJobAdmission {\n const readFunction = deps.readFunction ?? readAiLogicFunction\n return async (context): Promise<AiJobAdmissionRefusal | null> => {\n const inputs = parseAiLogicJobInputs(context.inputs)\n if (typeof inputs === 'string') return { status: 400, error: inputs }\n const hostId = context.hostId\n if (!hostId) return { status: 400, error: 'Open the site the logic is for before starting the job' }\n const owner = await resolveOrgIdForHost(hostId)\n if (!owner || owner !== context.orgId) return { status: 404, error: 'Unknown site' }\n const host = (await context.firestore.collection('hosts').doc(hostId).get()).data() ?? null\n const org = context.org as { enabledPlugins?: string[] } | null\n const released = await filterEnabledPluginsByReleaseFlags([AI_LOGIC_PLUGIN_ID], {\n orgId: context.orgId,\n authorization: null,\n })\n if (!released.includes(AI_LOGIC_PLUGIN_ID) || !isHostPluginEnabled(org, host, AI_LOGIC_PLUGIN_ID)) {\n return { status: 403, error: AI_LOGIC_UNAVAILABLE_COPY }\n }\n const functionId = inputs.mode === 'variable' ? null : inputs.functionId\n if (functionId && !(await readFunction(context.firestore, { hostId, id: functionId }))) {\n return { status: 404, error: AI_LOGIC_GONE_COPY }\n }\n return null\n }\n}\n\nexport const aiLogicJobAdmission = createAiLogicJobAdmission()\n\n/** Registers the logic step and the check a logic job passes before it is created or resumed. */\nexport function registerAiLogicJob(): void {\n registerAiJobStep('logic', runAiJobLogicStep, { minimumMs: AI_JOB_LOGIC_STEP_MINIMUM_MS })\n registerAiJobAdmission('logic', aiLogicJobAdmission)\n}\n"],"names":["FUNCTION_BUILTIN_NAMES","isHostPluginEnabled","filterEnabledPluginsByReleaseFlags","resolveOrgIdForHost","aiWorkflowExplanationText","AI_LOGIC_FUNCTION_RESOURCE","AI_LOGIC_FUNCTION_SAVE_FAILURE_COPY","AI_LOGIC_FUNCTION_UNAVAILABLE_COPY","AI_LOGIC_FUNCTION_WRITTEN_NOTE","AI_LOGIC_GONE_COPY","AI_LOGIC_MAX_OPERATIONS","AI_LOGIC_MAX_PARAMETERS","AI_LOGIC_NO_EXPLANATION_COPY","AI_LOGIC_NO_FUNCTION_COPY","AI_LOGIC_NO_SITE_COPY","AI_LOGIC_NO_VARIABLE_COPY","AI_LOGIC_RESOURCE","AI_LOGIC_UNAVAILABLE_COPY","aiLogicJobIsBuildUnit","checkAiLogicFunction","checkAiLogicVariable","parseAiLogicJobInputs","AI_STEP_TIERS","AI_ROUTING_TABLE","aiModelForStep","runValidatedGeneration","aiLogicFunctionTool","aiLogicVariableTool","AI_LOGIC_FUNCTION_TOOL_NAME","AI_LOGIC_VARIABLE_TOOL_NAME","AI_WORKFLOW_EXPLANATION_TOOL_NAME","aiWorkflowExplanationTool","readAiWorkflowExplanation","registerAiJobAdmission","aiJobStepBudget","aiGenerationSpent","aiLimitReview","aiUnspentOutcome","aiPluginDraftWriter","AI_JOB_BRIEF_MAX_CHARS","registerAiJobStep","readAiLogicFunction","readAiLogicRecords","AI_LOGIC_RECORDS_READ_MS","AI_JOB_LOGIC_STEP_BUDGET","tier","maxTokens","lookups","ownReadsMs","AI_JOB_LOGIC_STEP_MINIMUM_MS","minimumMs","aiJobLogicModel","AI_LOGIC_VARIABLES_LISTED","AI_LOGIC_VALUE_LISTED_CHARS","AI_JOB_LOGIC_INSTRUCTIONS","text","map","name","join","cacheBreakpoint","variableLine","variable","value","String","replace","type","length","slice","aiLogicFunctionOutline","definition","lines","push","parameters","parameter","required","label","defaultValue","variables","local","operations","forEach","operation","index","sets","list","set","workflow","expression","if","left","comparator","right","then","otherwise","returnValue","aiJobLogicPrompt","input","context","mode","saved","functions","brief","routedEffort","row","thinking","effort","aiJobLogicFunctionGeneration","request","step","model","instructions","messages","role","content","tool","signal","check","answer","aiJobLogicVariableGeneration","aiJobLogicExplainGeneration","aiLogicFunctionDraftOutput","place","record","resource","id","versionId","hostId","hostSubdomain","draftResource","note","proposalOutput","proposal","createAiJobLogicStep","deps","readRecords","readFunction","writerFor","job","now","firestore","org","modelFor","unspent","failure","inputs","host","collection","doc","get","exists","orgId","subdomain","functionId","writes","writer","draftContext","uid","createdBy","data","written","read","$id","outputs","refusal","status","review","error","records","editing","signalled","generation","spent","refused","kind","checked","ok","problems","write","runAiJobLogicStep","AI_LOGIC_PLUGIN_ID","createAiLogicJobAdmission","owner","released","authorization","includes","aiLogicJobAdmission","registerAiLogicJob"],"mappings":"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"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { NodesMap } from '@aglyn/aglyn/types/nodes';
18
+ import { type AiBuildPlanScreen } from '../model/ai-build-plan';
19
+ import type { AiJob, AiJobPlan } from '../model/ai-jobs.types';
20
+ import { type AiSiteInventory } from '../model/ai-site-inventory';
21
+ import { type AiLayoutSection, type AiLayoutSettlement } from '../layout-language/ai-layout-language';
22
+ import type { AiLayoutTargets } from '../layout-language/ai-layout-links';
23
+ import { type AiGenerationCheck, type AiValidatedGeneration } from '../runtime/ai-doctrine';
24
+ import { type AiDoctrineTreeContext, type AiDoctrineViolation } from '../runtime/ai-doctrine-validators';
25
+ import type { AiLoadEstimate } from '../runtime/ai-palette';
26
+ import type { AiSystemBlock } from '../runtime/ai-runtime';
27
+ /**
28
+ * A page built in the compact layout language (AGL-3660): the page step's
29
+ * pass for a site's page units — a guided start's and a build's — in place of
30
+ * the raw-tree section passes.
31
+ *
32
+ * ONE answer a page: every planned section, in the language, through
33
+ * `submit_page`. The compiler writes the tree, the palette validator stores
34
+ * it as it stores any generated tree, and the doctrine checks the whole page.
35
+ * A section the answer left out or left empty is the one thing a re-ask asks
36
+ * for: the sections that read are kept, and the second answer fills only
37
+ * what is still missing. Then the page step's last pass runs as it always
38
+ * has — the whole page held to the doctrine again, its listing written, the
39
+ * draft reported — so the item ledger, the credits and Try again work exactly
40
+ * as they did.
41
+ */
42
+ /** The unit input that says a page or a layout is built in the layout language. */
43
+ export declare const AI_LAYOUT_LANGUAGE_INPUT = "layoutLanguage";
44
+ /** The unit input naming the page of the site that places its form, where it is another page. */
45
+ export declare const AI_LAYOUT_FORM_PAGE_INPUT = "layoutFormPage";
46
+ /** Whether this job builds its page or its layout in the layout language. */
47
+ export declare function aiJobUsesLayoutLanguage(job: Pick<AiJob, 'inputs'>): boolean;
48
+ /**
49
+ * How the language doors think (AGL-3660): adaptively, at an effort named
50
+ * rather than left to the model's default. Zach, 2026-10-07: quality first —
51
+ * a page and a site's frame are designed, not transcribed, and the caching
52
+ * work pays for the thinking. Medium is where Claude Sonnet 5.5, the balanced
53
+ * default, is tuned to start for multistep design work.
54
+ */
55
+ export declare const AI_LAYOUT_LANGUAGE_THINKING: {
56
+ readonly thinking: "adaptive";
57
+ readonly effort: "medium";
58
+ };
59
+ /**
60
+ * Room for the thinking a page's answer does before it writes, inside the
61
+ * same `max_tokens` as the answer: thinking is drawn from the ceiling first,
62
+ * so a ceiling sized for the answer alone would cut a thoughtful answer off.
63
+ *
64
+ * Bounded by TIME, not by money: the page step's answer, its re-ask and the
65
+ * follow-up must fit the least time a beat gives a step
66
+ * (`AI_JOB_STEP_MAX_MINIMUM_MS`), and `aiJobStepBudget` quietly lowers every
67
+ * ceiling to what fits. 2,000 here, with the follow-up's 500, is the most
68
+ * that fits whole on the balanced tier; at 5,000 the budget cut a page's
69
+ * ceiling to 879 tokens, under the answer itself.
70
+ */
71
+ export declare const AI_LAYOUT_PAGE_THINKING_TOKENS = 2000;
72
+ /** The same room for a site's header and footer, whose step has the time for more. */
73
+ export declare const AI_LAYOUT_FRAME_THINKING_TOKENS = 4000;
74
+ /**
75
+ * The most one page's answer asks for: the answer and the thinking before it.
76
+ * A section in the language is about 120 to 250 tokens with its copy, so the
77
+ * largest page a plan admits — eight sections — fits in 3,000 with room (the
78
+ * live runs of 2026-10-07 wrote 683 to 1,745); the compiler, not the ceiling,
79
+ * keeps a page small.
80
+ */
81
+ export declare const AI_JOB_PAGE_LANGUAGE_TOKENS: number;
82
+ /**
83
+ * The time a language page pass needs: its lookup rounds, its answer and its
84
+ * re-ask at `AI_JOB_PAGE_LANGUAGE_TOKENS` on the tier the page step is served
85
+ * from, with the step's reads and writes.
86
+ */
87
+ /** The most the follow-up asking only for the sections an answer left out may run to, its thinking included. */
88
+ export declare const AI_JOB_PAGE_LANGUAGE_FOLLOW_UP_TOKENS: number;
89
+ export declare const AI_JOB_PAGE_LANGUAGE_BUDGET: import("./ai-job-budget").AiJobStepBudget;
90
+ /** The doctrine scope the language doors are told: values the compiler checks itself. */
91
+ export declare const AI_LAYOUT_PAGE_KIND = "layout-page";
92
+ /** The language, as the page door's cached instructions. */
93
+ export declare const AI_JOB_PAGE_LANGUAGE_INSTRUCTIONS: readonly AiSystemBlock[];
94
+ /** The page of a site plan that places its form: the form the plan creates, or one the site has. */
95
+ export declare function aiLayoutFormPageOfPlan(plan: Pick<AiJobPlan, 'create' | 'screens'>, formIds?: readonly string[]): string | null;
96
+ /** Every word the job was given, which a page's contact details are held to. */
97
+ export declare function aiLayoutFacts(job: Pick<AiJob, 'brief' | 'inputs'>): string;
98
+ /** What a page's links resolve against: the site's pages, built and planned, its forms and components. */
99
+ export declare function aiLayoutPageTargets(input: {
100
+ job: Pick<AiJob, 'brief' | 'inputs'>;
101
+ inventory: AiSiteInventory | null;
102
+ own: ReadonlyArray<string | null | undefined>;
103
+ }): AiLayoutTargets;
104
+ /** The page's user turn: the page, the brief, the plan, its sections and where its links may go. */
105
+ export declare function aiLayoutPagePrompt(input: {
106
+ job: Pick<AiJob, 'brief' | 'inputs' | '$id'>;
107
+ plan: AiJobPlan;
108
+ screen: AiBuildPlanScreen;
109
+ targets: AiLayoutTargets;
110
+ reusableComponents: boolean;
111
+ /** The sections a re-ask still needs, by plan index; absent on the first ask. */
112
+ missing?: readonly number[];
113
+ }): string;
114
+ /** A validated language page, stored as the page keeps it. */
115
+ export interface AiLayoutPageBuilt {
116
+ nodes: NodesMap;
117
+ load: AiLoadEstimate | null;
118
+ settled: AiLayoutSettlement[];
119
+ /** What the last answer's gaps took out of the page (`ai-layout-gaps.ts`). */
120
+ dropped: string[];
121
+ /** How many repeated items each plan section shows, as built (AGL-3660). */
122
+ items: number[];
123
+ }
124
+ export interface AiLayoutPageCheckInput {
125
+ screen: AiBuildPlanScreen;
126
+ sectionIds: readonly string[];
127
+ targets: AiLayoutTargets;
128
+ context: AiDoctrineTreeContext;
129
+ reusableComponents: boolean;
130
+ /**
131
+ * What earlier answers gave, by plan index, shared between the page's
132
+ * generations: a follow-up asking only for missing sections fills the rest.
133
+ */
134
+ kept?: Array<AiLayoutSection | null>;
135
+ /** The plan indices this answer's sections fill, in order; absent, every section. */
136
+ only?: readonly number[];
137
+ }
138
+ /**
139
+ * The follow-up turn for the sections a page's answer left out (AGL-3660):
140
+ * the page as before, told which sections it already has, and asked for only
141
+ * the missing ones, in order — the one part of a page asked for again.
142
+ */
143
+ export declare function aiLayoutMissingSectionsPrompt(input: {
144
+ base: string;
145
+ screen: AiBuildPlanScreen;
146
+ missing: readonly number[];
147
+ }): string;
148
+ /** The plan indices a page still lacks, after the answers so far. */
149
+ export declare function aiLayoutMissingSections(kept: ReadonlyArray<AiLayoutSection | null>): number[];
150
+ /** The violation a planned section the answer gave nothing usable for is named by. */
151
+ export declare const AI_LAYOUT_SECTION_MISSING_CODE = "layout-section-missing";
152
+ /** The code a section planned with items that shows none is refused under (AGL-3660). */
153
+ export declare const AI_LAYOUT_SECTION_EMPTY_CODE = "layout-section-no-items";
154
+ /**
155
+ * How many repeated items each section shows once stored (AGL-3660): the
156
+ * elements the compiler drew for them, under the ids they are stored by,
157
+ * that are still in the page after anything later took words out.
158
+ */
159
+ export declare function aiLayoutShownItems(itemIds: readonly (readonly string[])[], storedIds: Readonly<Record<string, string>>, nodes: Readonly<Record<string, unknown>>): number[];
160
+ /**
161
+ * The sections a page planned with repeated items — cards, steps, a list —
162
+ * that show none, or too few, once the page is built (AGL-3660). A heading
163
+ * and an intro over nothing is a section a visitor reads as broken, so it is
164
+ * asked for again.
165
+ */
166
+ export declare function aiLayoutEmptyItemSections(screen: Pick<AiBuildPlanScreen, 'sections'>, items: readonly number[]): AiDoctrineViolation[];
167
+ /**
168
+ * The check a language page answer is held to: each planned section read,
169
+ * the page compiled, stored and checked whole. Sections that read are kept
170
+ * across the re-ask, so the second answer is asked only for what was missing,
171
+ * and a section it sends again that already read is not taken twice.
172
+ */
173
+ export declare function aiLayoutPageCheck(input: AiLayoutPageCheckInput): AiGenerationCheck<AiLayoutPageBuilt>;
174
+ export interface AiLayoutPageRunInput {
175
+ job: Pick<AiJob, 'brief' | 'inputs' | '$id'>;
176
+ plan: AiJobPlan;
177
+ screen: AiBuildPlanScreen;
178
+ sectionIds: readonly string[];
179
+ targets: AiLayoutTargets;
180
+ context: AiDoctrineTreeContext;
181
+ reusableComponents: boolean;
182
+ inventory: AiSiteInventory | null;
183
+ model: string;
184
+ signal?: AbortSignal;
185
+ /** Wraps the check, for an eval that records each answer it reads. */
186
+ observe?: (check: AiGenerationCheck<AiLayoutPageBuilt>) => AiGenerationCheck<AiLayoutPageBuilt>;
187
+ }
188
+ /**
189
+ * A whole page in the layout language: one answer for every section, and,
190
+ * where that answer and its re-ask still left a section out, one follow-up
191
+ * asking for only those (AGL-3660). The sections that read are kept between
192
+ * them, so the page is compiled whole once every section is designed.
193
+ */
194
+ export declare function aiRunLayoutPage(input: AiLayoutPageRunInput): Promise<AiValidatedGeneration<AiLayoutPageBuilt>>;