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

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 (274) 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-automation-zones.d.ts +38 -0
  6. package/src/lib/components/ai-automation-zones.js +5 -1
  7. package/src/lib/components/ai-automation-zones.js.map +1 -1
  8. package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
  9. package/src/lib/components/ai-brief-dialog.component.js +87 -8
  10. package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
  11. package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
  12. package/src/lib/components/ai-campaign-create.component.js +270 -0
  13. package/src/lib/components/ai-campaign-create.component.js.map +1 -0
  14. package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
  15. package/src/lib/components/ai-describe-automation.component.js +15 -6
  16. package/src/lib/components/ai-describe-automation.component.js.map +1 -1
  17. package/src/lib/components/ai-describe-button.component.d.ts +20 -8
  18. package/src/lib/components/ai-describe-button.component.js +19 -36
  19. package/src/lib/components/ai-describe-button.component.js.map +1 -1
  20. package/src/lib/components/ai-describe-email.component.d.ts +31 -0
  21. package/src/lib/components/ai-describe-email.component.js +22 -0
  22. package/src/lib/components/ai-describe-email.component.js.map +1 -0
  23. package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
  24. package/src/lib/components/ai-describe-org-automation.component.js +238 -0
  25. package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
  26. package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
  27. package/src/lib/components/ai-experiment-cards.component.js +104 -18
  28. package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
  29. package/src/lib/components/ai-explain-automation.component.js +1 -5
  30. package/src/lib/components/ai-explain-automation.component.js.map +1 -1
  31. package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
  32. package/src/lib/components/ai-funnel-zones.component.js +178 -0
  33. package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
  34. package/src/lib/components/ai-insight-card.component.d.ts +42 -0
  35. package/src/lib/components/ai-insight-card.component.js +137 -0
  36. package/src/lib/components/ai-insight-card.component.js.map +1 -0
  37. package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
  38. package/src/lib/components/ai-insight-dialog.component.js +9 -3
  39. package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
  40. package/src/lib/components/ai-job-plan.component.d.ts +7 -2
  41. package/src/lib/components/ai-job-plan.component.js +35 -5
  42. package/src/lib/components/ai-job-plan.component.js.map +1 -1
  43. package/src/lib/components/ai-job-requests.d.ts +17 -1
  44. package/src/lib/components/ai-job-requests.js +39 -3
  45. package/src/lib/components/ai-job-requests.js.map +1 -1
  46. package/src/lib/components/ai-jobs-list-page.component.js +12 -1
  47. package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
  48. package/src/lib/components/ai-logic-zones.d.ts +64 -0
  49. package/src/lib/components/ai-logic-zones.js +18 -0
  50. package/src/lib/components/ai-logic-zones.js.map +1 -0
  51. package/src/lib/components/ai-logic.component.d.ts +77 -0
  52. package/src/lib/components/ai-logic.component.js +553 -0
  53. package/src/lib/components/ai-logic.component.js.map +1 -0
  54. package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
  55. package/src/lib/components/ai-marketing-insight.component.js +80 -0
  56. package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
  57. package/src/lib/components/ai-media-create.component.d.ts +55 -0
  58. package/src/lib/components/ai-media-create.component.js +433 -0
  59. package/src/lib/components/ai-media-create.component.js.map +1 -0
  60. package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
  61. package/src/lib/components/ai-overlay-cards.component.js +505 -0
  62. package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
  63. package/src/lib/components/ai-product-zones.d.ts +5 -0
  64. package/src/lib/components/ai-product-zones.js +1 -1
  65. package/src/lib/components/ai-product-zones.js.map +1 -1
  66. package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
  67. package/src/lib/components/ai-products-create-button.component.js +67 -0
  68. package/src/lib/components/ai-products-create-button.component.js.map +1 -0
  69. package/src/lib/components/ai-products-hub-card.component.js +100 -81
  70. package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
  71. package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
  72. package/src/lib/components/ai-revise-automation.component.js +207 -0
  73. package/src/lib/components/ai-revise-automation.component.js.map +1 -0
  74. package/src/lib/components/ai-site-build-page.component.d.ts +1 -1
  75. package/src/lib/components/ai-site-build-page.component.js +177 -20
  76. package/src/lib/components/ai-site-build-page.component.js.map +1 -1
  77. package/src/lib/components/ai-site-start-card.component.d.ts +10 -1
  78. package/src/lib/components/ai-site-start-card.component.js +7 -4
  79. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  80. package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
  81. package/src/lib/components/ai-upsell-dialog.component.js +177 -0
  82. package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
  83. package/src/lib/components/assist-build-card.component.d.ts +42 -0
  84. package/src/lib/components/assist-build-card.component.js +183 -0
  85. package/src/lib/components/assist-build-card.component.js.map +1 -0
  86. package/src/lib/components/assist-edit-canvas.d.ts +2 -0
  87. package/src/lib/components/assist-edit-canvas.js +56 -1
  88. package/src/lib/components/assist-edit-canvas.js.map +1 -1
  89. package/src/lib/components/assist-jobs-drawer.component.d.ts +11 -0
  90. package/src/lib/components/assist-jobs-drawer.component.js +85 -4
  91. package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
  92. package/src/lib/components/assist-panel.component.js +51 -2
  93. package/src/lib/components/assist-panel.component.js.map +1 -1
  94. package/src/lib/components/use-ai-job-run.d.ts +3 -14
  95. package/src/lib/components/use-ai-job-run.js +0 -50
  96. package/src/lib/components/use-ai-job-run.js.map +1 -1
  97. package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
  98. package/src/lib/jobs/ai-build-capabilities.js +256 -0
  99. package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
  100. package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
  101. package/src/lib/jobs/ai-build-unit-outcome.js +138 -0
  102. package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
  103. package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
  104. package/src/lib/jobs/ai-job-build-step.js +657 -0
  105. package/src/lib/jobs/ai-job-build-step.js.map +1 -0
  106. package/src/lib/jobs/ai-job-draft-ids.js +9 -1
  107. package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
  108. package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
  109. package/src/lib/jobs/ai-job-drafts.js +25 -4
  110. package/src/lib/jobs/ai-job-drafts.js.map +1 -1
  111. package/src/lib/jobs/ai-job-layout-site-pages.d.ts +70 -0
  112. package/src/lib/jobs/ai-job-layout-site-pages.js +216 -0
  113. package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -0
  114. package/src/lib/jobs/ai-job-layout-step.d.ts +2 -2
  115. package/src/lib/jobs/ai-job-layout-step.js +19 -1
  116. package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
  117. package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
  118. package/src/lib/jobs/ai-job-logic-step.js +395 -0
  119. package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
  120. package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
  121. package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
  122. package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
  123. package/src/lib/jobs/ai-job-page-step.js +6 -6
  124. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  125. package/src/lib/jobs/ai-job-plan-step.d.ts +26 -1
  126. package/src/lib/jobs/ai-job-plan-step.js +79 -11
  127. package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
  128. package/src/lib/jobs/ai-job-site-step.d.ts +15 -1
  129. package/src/lib/jobs/ai-job-site-step.js +207 -58
  130. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  131. package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
  132. package/src/lib/jobs/ai-job-text-step.js +12 -0
  133. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  134. package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
  135. package/src/lib/jobs/ai-job-workflow-step.js +318 -13
  136. package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
  137. package/src/lib/jobs/ai-jobs.d.ts +90 -2
  138. package/src/lib/jobs/ai-jobs.js +495 -61
  139. package/src/lib/jobs/ai-jobs.js.map +1 -1
  140. package/src/lib/jobs/ai-logic-records.d.ts +40 -0
  141. package/src/lib/jobs/ai-logic-records.js +55 -0
  142. package/src/lib/jobs/ai-logic-records.js.map +1 -0
  143. package/src/lib/jobs/ai-site-publish.d.ts +7 -0
  144. package/src/lib/jobs/ai-site-publish.js +9 -0
  145. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  146. package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
  147. package/src/lib/jobs/ai-workflow-records.js +40 -1
  148. package/src/lib/jobs/ai-workflow-records.js.map +1 -1
  149. package/src/lib/model/ai-automation-draft.d.ts +18 -0
  150. package/src/lib/model/ai-automation-draft.js +86 -0
  151. package/src/lib/model/ai-automation-draft.js.map +1 -1
  152. package/src/lib/model/ai-build-job.d.ts +201 -0
  153. package/src/lib/model/ai-build-job.js +471 -0
  154. package/src/lib/model/ai-build-job.js.map +1 -0
  155. package/src/lib/model/ai-build-plan.d.ts +61 -0
  156. package/src/lib/model/ai-build-plan.js +154 -9
  157. package/src/lib/model/ai-build-plan.js.map +1 -1
  158. package/src/lib/model/ai-build-progress.d.ts +56 -0
  159. package/src/lib/model/ai-build-progress.js +128 -0
  160. package/src/lib/model/ai-build-progress.js.map +1 -0
  161. package/src/lib/model/ai-insight.d.ts +20 -10
  162. package/src/lib/model/ai-insight.js +72 -9
  163. package/src/lib/model/ai-insight.js.map +1 -1
  164. package/src/lib/model/ai-job-activity.js +3 -1
  165. package/src/lib/model/ai-job-activity.js.map +1 -1
  166. package/src/lib/model/ai-job-failure-copy.d.ts +20 -1
  167. package/src/lib/model/ai-job-failure-copy.js +28 -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 -0
  170. package/src/lib/model/ai-job-notice.js +11 -2
  171. package/src/lib/model/ai-job-notice.js.map +1 -1
  172. package/src/lib/model/ai-jobs.types.d.ts +87 -2
  173. package/src/lib/model/ai-jobs.types.js +3 -1
  174. package/src/lib/model/ai-jobs.types.js.map +1 -1
  175. package/src/lib/model/ai-logic-job.d.ts +108 -0
  176. package/src/lib/model/ai-logic-job.js +327 -0
  177. package/src/lib/model/ai-logic-job.js.map +1 -0
  178. package/src/lib/model/ai-media-credits.d.ts +52 -0
  179. package/src/lib/model/ai-media-credits.js +61 -0
  180. package/src/lib/model/ai-media-credits.js.map +1 -0
  181. package/src/lib/model/ai-overlay-copy.d.ts +114 -0
  182. package/src/lib/model/ai-overlay-copy.js +175 -0
  183. package/src/lib/model/ai-overlay-copy.js.map +1 -0
  184. package/src/lib/model/ai-site-build-progress.d.ts +28 -3
  185. package/src/lib/model/ai-site-build-progress.js +231 -17
  186. package/src/lib/model/ai-site-build-progress.js.map +1 -1
  187. package/src/lib/model/ai-site-start-seo.d.ts +16 -0
  188. package/src/lib/model/ai-site-start-seo.js +54 -4
  189. package/src/lib/model/ai-site-start-seo.js.map +1 -1
  190. package/src/lib/model/ai-site-start.d.ts +14 -0
  191. package/src/lib/model/ai-site-start.js +43 -0
  192. package/src/lib/model/ai-site-start.js.map +1 -1
  193. package/src/lib/model/ai-svg.d.ts +69 -0
  194. package/src/lib/model/ai-svg.js +265 -0
  195. package/src/lib/model/ai-svg.js.map +1 -0
  196. package/src/lib/model/ai-usage-by-user.d.ts +3 -2
  197. package/src/lib/model/ai-usage-by-user.js.map +1 -1
  198. package/src/lib/model/ai-workflow-job.d.ts +92 -4
  199. package/src/lib/model/ai-workflow-job.js +124 -7
  200. package/src/lib/model/ai-workflow-job.js.map +1 -1
  201. package/src/lib/model/assist-build.d.ts +58 -0
  202. package/src/lib/model/assist-build.js +52 -0
  203. package/src/lib/model/assist-build.js.map +1 -0
  204. package/src/lib/model/assist-edit.d.ts +48 -3
  205. package/src/lib/model/assist-edit.js +48 -4
  206. package/src/lib/model/assist-edit.js.map +1 -1
  207. package/src/lib/plugin-config.js +2 -0
  208. package/src/lib/plugin-config.js.map +1 -1
  209. package/src/lib/plugin.js +240 -11
  210. package/src/lib/plugin.js.map +1 -1
  211. package/src/lib/providers/catalog.d.ts +81 -4
  212. package/src/lib/providers/catalog.js +80 -9
  213. package/src/lib/providers/catalog.js.map +1 -1
  214. package/src/lib/providers/image-contract.d.ts +116 -0
  215. package/src/lib/providers/image-contract.js +74 -0
  216. package/src/lib/providers/image-contract.js.map +1 -0
  217. package/src/lib/providers/model-choice.js +11 -0
  218. package/src/lib/providers/model-choice.js.map +1 -1
  219. package/src/lib/providers/routing.js +18 -0
  220. package/src/lib/providers/routing.js.map +1 -1
  221. package/src/lib/providers/vertex-image.d.ts +121 -0
  222. package/src/lib/providers/vertex-image.js +312 -0
  223. package/src/lib/providers/vertex-image.js.map +1 -0
  224. package/src/lib/runtime/ai-doctrine-validators.d.ts +19 -0
  225. package/src/lib/runtime/ai-doctrine-validators.js +89 -0
  226. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  227. package/src/lib/runtime/ai-doctrine.d.ts +6 -0
  228. package/src/lib/runtime/ai-doctrine.js +16 -6
  229. package/src/lib/runtime/ai-doctrine.js.map +1 -1
  230. package/src/lib/runtime/ai-eval-live.js +4 -0
  231. package/src/lib/runtime/ai-eval-live.js.map +1 -1
  232. package/src/lib/runtime/ai-eval.d.ts +15 -1
  233. package/src/lib/runtime/ai-eval.js +52 -1
  234. package/src/lib/runtime/ai-eval.js.map +1 -1
  235. package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
  236. package/src/lib/runtime/experiment-variant-copy.js +102 -0
  237. package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
  238. package/src/lib/server/ai-experiment-versions.d.ts +51 -0
  239. package/src/lib/server/ai-experiment-versions.js +270 -0
  240. package/src/lib/server/ai-experiment-versions.js.map +1 -0
  241. package/src/lib/server/ai-jobs-resume.js +74 -4
  242. package/src/lib/server/ai-jobs-resume.js.map +1 -1
  243. package/src/lib/server/ai-media-image.d.ts +66 -0
  244. package/src/lib/server/ai-media-image.js +521 -0
  245. package/src/lib/server/ai-media-image.js.map +1 -0
  246. package/src/lib/server/ai-media-svg.d.ts +91 -0
  247. package/src/lib/server/ai-media-svg.js +186 -0
  248. package/src/lib/server/ai-media-svg.js.map +1 -0
  249. package/src/lib/server/assist-build.d.ts +54 -0
  250. package/src/lib/server/assist-build.js +121 -0
  251. package/src/lib/server/assist-build.js.map +1 -0
  252. package/src/lib/server/assist-chat.js +108 -12
  253. package/src/lib/server/assist-chat.js.map +1 -1
  254. package/src/lib/server/assist-edit.js +213 -3
  255. package/src/lib/server/assist-edit.js.map +1 -1
  256. package/src/lib/server/assist-view-context.js +242 -4
  257. package/src/lib/server/assist-view-context.js.map +1 -1
  258. package/src/lib/server.js +26 -0
  259. package/src/lib/server.js.map +1 -1
  260. package/src/lib/tools/ai-logic-tool.d.ts +27 -0
  261. package/src/lib/tools/ai-logic-tool.js +207 -0
  262. package/src/lib/tools/ai-logic-tool.js.map +1 -0
  263. package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
  264. package/src/lib/tools/ai-overlay-tool.js +107 -0
  265. package/src/lib/tools/ai-overlay-tool.js.map +1 -0
  266. package/src/lib/tools/ai-workflow-tool.d.ts +11 -0
  267. package/src/lib/tools/ai-workflow-tool.js +31 -0
  268. package/src/lib/tools/ai-workflow-tool.js.map +1 -1
  269. package/src/lib/usage/assist-job-refund.d.ts +31 -3
  270. package/src/lib/usage/assist-job-refund.js +46 -10
  271. package/src/lib/usage/assist-job-refund.js.map +1 -1
  272. package/src/lib/usage/assist-usage.d.ts +5 -0
  273. package/src/lib/usage/assist-usage.js +2 -0
  274. package/src/lib/usage/assist-usage.js.map +1 -1
@@ -20,7 +20,7 @@ import { HOST_EVENT_LABELS, HOST_EVENT_TYPES } from "@aglyn/aglyn/app-utils/host
20
20
  * A `workflow` job (AGL-2919): an automation drafted from a description, or an
21
21
  * existing automation explained — what it does, or why one of its runs failed.
22
22
  *
23
- * One kind, three modes, because the three share everything a job carries:
23
+ * One kind, four modes, because the four share everything a job carries:
24
24
  * the site, the gates, the meter and the automation vocabulary. `inputs.mode`
25
25
  * picks the mode, and a job created without one drafts.
26
26
  *
@@ -32,12 +32,31 @@ import { HOST_EVENT_LABELS, HOST_EVENT_TYPES } from "@aglyn/aglyn/app-utils/host
32
32
  * answers in plain words what it does.
33
33
  * - `diagnose` reads a saved automation and one of its FAILED runs, as the
34
34
  * run history recorded it, and answers why it failed and what to change.
35
+ * - `revise` reads a saved ACTION and a change asked of it — "fix it", or
36
+ * "also tag them" — and writes the action as it would be after the change
37
+ * as a NEW draft, OFF, through the same draft writer and vocabulary as
38
+ * `draft`. The saved action is never written: the person compares the two,
39
+ * switches the new one on and the old one off. An action that holds what
40
+ * the vocabulary cannot write (an on-page step, a page event, a filter
41
+ * expression) is refused rather than revised without it.
35
42
  *
36
- * Nothing in any mode switches an automation on, runs one, or changes one.
43
+ * `draft` has a second scope (AGL-3603): `scope: 'org'` drafts one of the
44
+ * WORKSPACE's automations — the ones the workflows plugin's Org automations
45
+ * section places on several sites — with no site named. Its vocabulary is
46
+ * the narrower one that section's zone hands the widget (`triggers` and
47
+ * `steps`, each a comma-separated list, intersected here with the drafting
48
+ * vocabulary below), its records are the workspace's, and nothing is written:
49
+ * the automation rides on an `orgAutomation` output as a proposal that the
50
+ * section's editor opens unsaved, switched off, where the person picks its
51
+ * sites and saves it through that section's own route.
52
+ *
53
+ * Nothing in any mode switches an automation on, runs one, or changes a saved
54
+ * one.
37
55
  */ export const AI_WORKFLOW_JOB_MODES = [
38
56
  'draft',
39
57
  'explain',
40
- 'diagnose'
58
+ 'diagnose',
59
+ 'revise'
41
60
  ];
42
61
  /** What an explained automation is: an action, or a workflow of function calls. */ export const AI_WORKFLOW_TARGET_TYPES = [
43
62
  'action',
@@ -48,11 +67,18 @@ const DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/;
48
67
  var _ref;
49
68
  const mode = (_ref = inputs == null ? void 0 : inputs['mode']) != null ? _ref : 'draft';
50
69
  if (!AI_WORKFLOW_JOB_MODES.includes(mode)) {
51
- return 'inputs.mode must be draft, explain or diagnose';
70
+ return 'inputs.mode must be draft, explain, diagnose or revise';
71
+ }
72
+ if (mode === 'draft') {
73
+ if ((inputs == null ? void 0 : inputs['scope']) !== 'org') return {
74
+ mode
75
+ };
76
+ const vocabulary = readAiOrgAutomationVocabulary(inputs);
77
+ return vocabulary ? _extends({
78
+ mode,
79
+ scope: 'org'
80
+ }, vocabulary) : AI_ORG_AUTOMATION_NO_VOCABULARY_COPY;
52
81
  }
53
- if (mode === 'draft') return {
54
- mode
55
- };
56
82
  const targetType = inputs == null ? void 0 : inputs['targetType'];
57
83
  const targetId = inputs == null ? void 0 : inputs['targetId'];
58
84
  if (!AI_WORKFLOW_TARGET_TYPES.includes(targetType) || typeof targetId !== 'string' || !DOCUMENT_ID.test(targetId)) {
@@ -65,6 +91,15 @@ const DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/;
65
91
  if (mode === 'explain') return _extends({
66
92
  mode
67
93
  }, target);
94
+ if (mode === 'revise') {
95
+ // A workflow is a list of function calls the draft writer does not write.
96
+ if (targetType !== 'action') return AI_WORKFLOW_REVISE_WORKFLOW_COPY;
97
+ return {
98
+ mode,
99
+ targetType,
100
+ targetId
101
+ };
102
+ }
68
103
  const runId = inputs == null ? void 0 : inputs['runId'];
69
104
  if (typeof runId !== 'string' || !DOCUMENT_ID.test(runId)) return 'Pick the run to explain';
70
105
  return _extends({
@@ -73,6 +108,31 @@ const DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/;
73
108
  runId
74
109
  });
75
110
  }
111
+ /** A comma-separated list of names, each kept only when `known` has it, in `known`'s order. */ function knownNames(raw, known) {
112
+ const asked = new Set((typeof raw === 'string' ? raw : '').split(',').map((name)=>name.trim()).filter(Boolean));
113
+ return known.filter((name)=>asked.has(name));
114
+ }
115
+ /**
116
+ * An org automation's vocabulary from a job's inputs: the triggers and steps
117
+ * the zone named that the drafting vocabulary also has, or `null` when that
118
+ * leaves no trigger or no step. The zone can only narrow what is drafted;
119
+ * the section's save route holds what is saved to its own rules.
120
+ */ export function readAiOrgAutomationVocabulary(inputs) {
121
+ const triggers = knownNames(inputs == null ? void 0 : inputs['triggers'], AI_AUTOMATION_TRIGGERS);
122
+ const steps = knownNames(inputs == null ? void 0 : inputs['steps'], AI_AUTOMATION_STEP_TYPES);
123
+ return triggers.length && steps.length ? {
124
+ triggers,
125
+ steps
126
+ } : null;
127
+ }
128
+ /** An org automation's vocabulary as a job's inputs carry it. */ export function aiOrgAutomationInputs(vocabulary) {
129
+ return {
130
+ mode: 'draft',
131
+ scope: 'org',
132
+ triggers: vocabulary.triggers.join(','),
133
+ steps: vocabulary.steps.join(',')
134
+ };
135
+ }
76
136
  /** Each need as a sentence names it. */ export const AI_AUTOMATION_NEED_LABELS = {
77
137
  crm: 'the CRM',
78
138
  webhooks: 'webhooks',
@@ -178,5 +238,62 @@ export const AI_WORKFLOW_RUN_NOT_FAILED_COPY = 'That run did not fail, so there
178
238
  export const AI_WORKFLOW_NO_DRAFT_COPY = 'The AI could not draft an automation from this description. Try describing what happens, and what should happen then.';
179
239
  export const AI_WORKFLOW_NO_EXPLANATION_COPY = 'The AI could not explain this automation. Try again.';
180
240
  export const AI_WORKFLOW_SAVE_FAILURE_COPY = 'The automation was drafted but could not be saved. Try the job again.';
241
+ export const AI_ORG_AUTOMATION_NO_VOCABULARY_COPY = 'Open Org automations to draft one: the page says what an org automation can start on and do.';
242
+ export const AI_ORG_AUTOMATION_UNAVAILABLE_COPY = 'Turn on Automation for this workspace before drafting an org automation.';
243
+ export const AI_ORG_AUTOMATION_PLAN_COPY = 'Org automations are built from the actions builder, which this workspace’s plan does not include. Upgrade in Billing to build them.';
244
+ /**
245
+ * The output resource an org automation's proposal rides on (AGL-3603): never
246
+ * a document — the Org automations editor opens it unsaved.
247
+ */ export const AI_ORG_AUTOMATION_RESOURCE = 'orgAutomation';
248
+ /** An output's proposal read back, or `null` for one that is not an org automation. */ export function readAiOrgAutomationProposal(value) {
249
+ const automation = value == null ? void 0 : value.automation;
250
+ if (!automation || typeof automation !== 'object') return null;
251
+ const trigger = automation['trigger'];
252
+ const steps = automation['steps'];
253
+ if (!trigger || typeof trigger['event'] !== 'string' || !Array.isArray(steps)) return null;
254
+ if (!steps.every((step)=>step && typeof step === 'object' && typeof step.type === 'string')) {
255
+ return null;
256
+ }
257
+ return {
258
+ name: typeof automation['name'] === 'string' ? automation['name'] : '',
259
+ trigger: _extends({
260
+ event: trigger['event']
261
+ }, Array.isArray(trigger['conditions']) ? {
262
+ conditions: trigger['conditions']
263
+ } : {}, trigger['combinator'] === 'or' || trigger['combinator'] === 'and' ? {
264
+ combinator: trigger['combinator']
265
+ } : {}),
266
+ steps: steps
267
+ };
268
+ }
269
+ export const AI_WORKFLOW_REVISE_WORKFLOW_COPY = 'Only an action can be changed with AI. A workflow can be explained, and edited in its own editor.';
270
+ // ── An action AI can revise ───────────────────────────────────────────────
271
+ /** Why a saved action cannot be revised, each with the sentence a person reads. */ export const AI_WORKFLOW_REVISE_BLOCKERS = [
272
+ 'page-event',
273
+ 'page-step',
274
+ 'filter'
275
+ ];
276
+ export const AI_WORKFLOW_REVISE_BLOCKER_COPY = {
277
+ 'page-event': 'This action starts on something a visitor does on a page, which AI does not write, so it cannot be changed with AI. Edit it here instead.',
278
+ 'page-step': 'This action has a step that runs on the page, which AI does not write, so it cannot be changed with AI without losing that step. Edit it here instead.',
279
+ filter: 'This action only runs when an expression is true, which AI does not write, so it cannot be changed with AI without losing it. Edit it here instead.'
280
+ };
281
+ /**
282
+ * Why a saved action cannot be revised, or `null` when every part of it is
283
+ * one the drafting vocabulary writes — so the revised draft can hold the
284
+ * whole action and lose nothing the person did not ask to change.
285
+ */ export function aiActionReviseBlocker(action) {
286
+ var _action_steps;
287
+ var _action_trigger, _action_trigger1;
288
+ const event = (_action_trigger = action.trigger) == null ? void 0 : _action_trigger.event;
289
+ if (!AI_AUTOMATION_TRIGGERS.includes(event)) return 'page-event';
290
+ const steps = (_action_steps = action.steps) != null ? _action_steps : [];
291
+ if (steps.some((step)=>!AI_AUTOMATION_STEP_TYPES.includes(step == null ? void 0 : step.type))) {
292
+ return 'page-step';
293
+ }
294
+ const filter = (_action_trigger1 = action.trigger) == null ? void 0 : _action_trigger1.filter;
295
+ if (typeof filter === 'string' && filter.trim()) return 'filter';
296
+ return null;
297
+ }
181
298
 
182
299
  //# sourceMappingURL=ai-workflow-job.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-workflow-job.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n HOST_EVENT_LABELS,\n HOST_EVENT_TYPES,\n type HostEventType,\n} from '@aglyn/aglyn/app-utils/host-events'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { AiAutomationFormatStepType } from './ai-automation-format'\n\n/**\n * A `workflow` job (AGL-2919): an automation drafted from a description, or an\n * existing automation explained — what it does, or why one of its runs failed.\n *\n * One kind, three modes, because the three share everything a job carries:\n * the site, the gates, the meter and the automation vocabulary. `inputs.mode`\n * picks the mode, and a job created without one drafts.\n *\n * - `draft` writes a new automation to the site's Actions through the\n * workflows plugin's draft writer, OFF, built only from the triggers and\n * steps below that this workspace can run. What the description names that\n * the site does not have is left as a placeholder, never guessed.\n * - `explain` reads a saved automation — an action or a workflow — and\n * answers in plain words what it does.\n * - `diagnose` reads a saved automation and one of its FAILED runs, as the\n * run history recorded it, and answers why it failed and what to change.\n *\n * Nothing in any mode switches an automation on, runs one, or changes one.\n */\n\nexport const AI_WORKFLOW_JOB_MODES = ['draft', 'explain', 'diagnose'] as const\nexport type AiWorkflowJobMode = (typeof AI_WORKFLOW_JOB_MODES)[number]\n\n/** What an explained automation is: an action, or a workflow of function calls. */\nexport const AI_WORKFLOW_TARGET_TYPES = ['action', 'workflow'] as const\nexport type AiWorkflowTargetType = (typeof AI_WORKFLOW_TARGET_TYPES)[number]\n\nexport type AiWorkflowJobInputs =\n | { mode: 'draft' }\n | { mode: 'explain'; targetType: AiWorkflowTargetType; targetId: string }\n | { mode: 'diagnose'; targetType: AiWorkflowTargetType; targetId: string; runId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiWorkflowJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiWorkflowJobInputs | string {\n const mode = inputs?.['mode'] ?? 'draft'\n if (!(AI_WORKFLOW_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be draft, explain or diagnose'\n }\n if (mode === 'draft') return { mode }\n const targetType = inputs?.['targetType']\n const targetId = inputs?.['targetId']\n if (\n !(AI_WORKFLOW_TARGET_TYPES as readonly unknown[]).includes(targetType) ||\n typeof targetId !== 'string' ||\n !DOCUMENT_ID.test(targetId)\n ) {\n return 'Pick the automation to explain'\n }\n const target = { targetType: targetType as AiWorkflowTargetType, targetId }\n if (mode === 'explain') return { mode, ...target }\n const runId = inputs?.['runId']\n if (typeof runId !== 'string' || !DOCUMENT_ID.test(runId)) return 'Pick the run to explain'\n return { mode: 'diagnose', ...target, runId }\n}\n\n// ── What a workspace can run ──────────────────────────────────────────────\n\n/** A part of the platform some triggers and steps need beyond the actions builder itself. */\nexport type AiAutomationNeed = 'crm' | 'webhooks' | 'bookings'\n\nexport type AiAutomationCapabilities = Readonly<Record<AiAutomationNeed, boolean>>\n\n/** Each need as a sentence names it. */\nexport const AI_AUTOMATION_NEED_LABELS: Readonly<Record<AiAutomationNeed, string>> = {\n crm: 'the CRM',\n webhooks: 'webhooks',\n bookings: 'bookings',\n}\n\n/**\n * What this workspace can run, from the entitlements the executor itself\n * reads before it runs a step: `crm` for the CRM steps, `webhooks` for a\n * webhook post, and `bookings` for the booking trigger's door.\n */\nexport function aiAutomationCapabilities(\n org: Partial<AglynOrgBilling> | Readonly<Record<string, unknown>> | null | undefined,\n): AiAutomationCapabilities {\n const billing = org as Partial<AglynOrgBilling> | null | undefined\n return {\n crm: checkEntitlement(billing, 'crm'),\n webhooks: checkEntitlement(billing, 'webhooks'),\n bookings: checkEntitlement(billing, 'bookings'),\n }\n}\n\n// ── The vocabulary ────────────────────────────────────────────────────────\n\n/**\n * The triggers a drafted automation may start on: every host event, which is\n * every event a server door emits. The on-page events (a click, a scroll) are\n * left out: they watch one element of one page, which a description cannot\n * name, and they belong to the page's own interactions.\n */\nexport const AI_AUTOMATION_TRIGGERS: readonly HostEventType[] = HOST_EVENT_TYPES\n\n/** The triggers only a door of a part of the platform emits. */\nexport const AI_AUTOMATION_TRIGGER_NEEDS: Readonly<Partial<Record<HostEventType, AiAutomationNeed>>> = {\n booking: 'bookings',\n taskCompleted: 'crm',\n contactStageChanged: 'crm',\n dealStageChanged: 'crm',\n dealWon: 'crm',\n dealLost: 'crm',\n}\n\n/**\n * The steps a drafted automation may take: the server steps and the flow\n * steps, which act on the person an event names. The on-page steps — show an\n * element, open a menu, run a script — are left out for the reason the\n * on-page triggers are: they need a selector on a page a description cannot\n * name.\n */\nexport const AI_AUTOMATION_STEP_TYPES = [\n 'sendEmail',\n 'notifyAdmins',\n 'enrollList',\n 'assignCampaign',\n 'runWorkflow',\n 'datasetAppend',\n 'updateDataset',\n 'webhookPost',\n 'siteAlert',\n 'wait',\n 'waitForEvent',\n 'exitFlow',\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const satisfies readonly AiAutomationFormatStepType[]\n\nexport type AiAutomationStepType = (typeof AI_AUTOMATION_STEP_TYPES)[number]\n\n/** The steps the executor refuses without a part of the platform. */\nexport const AI_AUTOMATION_STEP_NEEDS: Readonly<Partial<Record<AiAutomationStepType, AiAutomationNeed>>> = {\n webhookPost: 'webhooks',\n setContactStage: 'crm',\n addContactTag: 'crm',\n assignContactOwner: 'crm',\n createCrmTask: 'crm',\n logCrmActivity: 'crm',\n}\n\n/** The trigger's name as the Actions editor labels it. */\nexport function aiAutomationTriggerLabel(event: string): string {\n return HOST_EVENT_LABELS[event as HostEventType] ?? event\n}\n\n// ── A description the vocabulary cannot build ─────────────────────────────\n\n/**\n * Why a description was answered with no automation. Each is a fixed reason\n * with a fixed sentence, so what a person reads is never the model's own\n * account of itself.\n */\nexport const AI_AUTOMATION_UNSUPPORTED = [\n 'not-an-automation',\n 'no-trigger',\n 'no-step',\n 'needs-crm',\n 'needs-webhooks',\n 'needs-bookings',\n] as const\n\nexport type AiAutomationUnsupported = (typeof AI_AUTOMATION_UNSUPPORTED)[number]\n\n/** The need an unsupported reason names, for the reasons that name one. */\nexport const AI_AUTOMATION_UNSUPPORTED_NEED: Readonly<\n Partial<Record<AiAutomationUnsupported, AiAutomationNeed>>\n> = {\n 'needs-crm': 'crm',\n 'needs-webhooks': 'webhooks',\n 'needs-bookings': 'bookings',\n}\n\nexport const AI_AUTOMATION_UNSUPPORTED_COPY: Readonly<Record<AiAutomationUnsupported, string>> = {\n 'not-an-automation':\n 'This description is not an automation — something that happens, and what to do then — so nothing was drafted.',\n 'no-trigger':\n 'Nothing on the platform can start an automation like this one, so nothing was drafted. Describe it as something that happens on your site, such as a form being submitted.',\n 'no-step': 'An automation cannot do what this description asks yet, so nothing was drafted.',\n 'needs-crm':\n 'This automation needs the CRM, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-webhooks':\n 'This automation needs webhooks, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-bookings':\n 'This automation needs bookings, which this workspace’s plan does not include, so nothing was drafted.',\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\n/** The resource the workflows plugin writes a drafted automation under. */\nexport const AI_AUTOMATION_RESOURCE = 'automation'\n\nexport const AI_WORKFLOW_NO_SITE_COPY = 'This automation job names a site this workspace does not have.'\nexport const AI_WORKFLOW_UNAVAILABLE_COPY = 'Automations are not available on this site.'\nexport const AI_WORKFLOW_GONE_COPY = 'That automation no longer exists.'\nexport const AI_WORKFLOW_RUN_GONE_COPY = 'That run is no longer in the automation’s history.'\nexport const AI_WORKFLOW_RUN_NOT_FAILED_COPY = 'That run did not fail, so there is nothing to explain.'\nexport const AI_WORKFLOW_NO_DRAFT_COPY =\n 'The AI could not draft an automation from this description. Try describing what happens, and what should happen then.'\nexport const AI_WORKFLOW_NO_EXPLANATION_COPY =\n 'The AI could not explain this automation. Try again.'\nexport const AI_WORKFLOW_SAVE_FAILURE_COPY =\n 'The automation was drafted but could not be saved. Try the job again.'\n"],"names":["checkEntitlement","HOST_EVENT_LABELS","HOST_EVENT_TYPES","AI_WORKFLOW_JOB_MODES","AI_WORKFLOW_TARGET_TYPES","DOCUMENT_ID","parseAiWorkflowJobInputs","inputs","mode","includes","targetType","targetId","test","target","runId","AI_AUTOMATION_NEED_LABELS","crm","webhooks","bookings","aiAutomationCapabilities","org","billing","AI_AUTOMATION_TRIGGERS","AI_AUTOMATION_TRIGGER_NEEDS","booking","taskCompleted","contactStageChanged","dealStageChanged","dealWon","dealLost","AI_AUTOMATION_STEP_TYPES","AI_AUTOMATION_STEP_NEEDS","webhookPost","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","aiAutomationTriggerLabel","event","AI_AUTOMATION_UNSUPPORTED","AI_AUTOMATION_UNSUPPORTED_NEED","AI_AUTOMATION_UNSUPPORTED_COPY","AI_AUTOMATION_RESOURCE","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/ai-workflow-job.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { checkEntitlement } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport {\n HOST_EVENT_LABELS,\n HOST_EVENT_TYPES,\n type HostEventType,\n} from '@aglyn/aglyn/app-utils/host-events'\nimport type { AglynOrgBilling } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { AiAutomationFormatStepType } from './ai-automation-format'\n\n/**\n * A `workflow` job (AGL-2919): an automation drafted from a description, or an\n * existing automation explained — what it does, or why one of its runs failed.\n *\n * One kind, four modes, because the four share everything a job carries:\n * the site, the gates, the meter and the automation vocabulary. `inputs.mode`\n * picks the mode, and a job created without one drafts.\n *\n * - `draft` writes a new automation to the site's Actions through the\n * workflows plugin's draft writer, OFF, built only from the triggers and\n * steps below that this workspace can run. What the description names that\n * the site does not have is left as a placeholder, never guessed.\n * - `explain` reads a saved automation — an action or a workflow — and\n * answers in plain words what it does.\n * - `diagnose` reads a saved automation and one of its FAILED runs, as the\n * run history recorded it, and answers why it failed and what to change.\n * - `revise` reads a saved ACTION and a change asked of it — \"fix it\", or\n * \"also tag them\" — and writes the action as it would be after the change\n * as a NEW draft, OFF, through the same draft writer and vocabulary as\n * `draft`. The saved action is never written: the person compares the two,\n * switches the new one on and the old one off. An action that holds what\n * the vocabulary cannot write (an on-page step, a page event, a filter\n * expression) is refused rather than revised without it.\n *\n * `draft` has a second scope (AGL-3603): `scope: 'org'` drafts one of the\n * WORKSPACE's automations — the ones the workflows plugin's Org automations\n * section places on several sites — with no site named. Its vocabulary is\n * the narrower one that section's zone hands the widget (`triggers` and\n * `steps`, each a comma-separated list, intersected here with the drafting\n * vocabulary below), its records are the workspace's, and nothing is written:\n * the automation rides on an `orgAutomation` output as a proposal that the\n * section's editor opens unsaved, switched off, where the person picks its\n * sites and saves it through that section's own route.\n *\n * Nothing in any mode switches an automation on, runs one, or changes a saved\n * one.\n */\n\nexport const AI_WORKFLOW_JOB_MODES = ['draft', 'explain', 'diagnose', 'revise'] as const\nexport type AiWorkflowJobMode = (typeof AI_WORKFLOW_JOB_MODES)[number]\n\n/** What an explained automation is: an action, or a workflow of function calls. */\nexport const AI_WORKFLOW_TARGET_TYPES = ['action', 'workflow'] as const\nexport type AiWorkflowTargetType = (typeof AI_WORKFLOW_TARGET_TYPES)[number]\n\n/** What an org automation may start on and do, as the zone that asks for one says. */\nexport interface AiOrgAutomationVocabulary {\n triggers: HostEventType[]\n steps: AiAutomationStepType[]\n}\n\nexport type AiWorkflowJobInputs =\n | { mode: 'draft'; scope?: 'site' }\n | ({ mode: 'draft'; scope: 'org' } & AiOrgAutomationVocabulary)\n | { mode: 'explain'; targetType: AiWorkflowTargetType; targetId: string }\n | { mode: 'diagnose'; targetType: AiWorkflowTargetType; targetId: string; runId: string }\n | { mode: 'revise'; targetType: 'action'; targetId: string }\n\nconst DOCUMENT_ID = /^[A-Za-z0-9_-]{1,128}$/\n\n/** A job's inputs read into its mode, or the sentence that says what is wrong with them. */\nexport function parseAiWorkflowJobInputs(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiWorkflowJobInputs | string {\n const mode = inputs?.['mode'] ?? 'draft'\n if (!(AI_WORKFLOW_JOB_MODES as readonly unknown[]).includes(mode)) {\n return 'inputs.mode must be draft, explain, diagnose or revise'\n }\n if (mode === 'draft') {\n if (inputs?.['scope'] !== 'org') return { mode }\n const vocabulary = readAiOrgAutomationVocabulary(inputs)\n return vocabulary ? { mode, scope: 'org', ...vocabulary } : AI_ORG_AUTOMATION_NO_VOCABULARY_COPY\n }\n const targetType = inputs?.['targetType']\n const targetId = inputs?.['targetId']\n if (\n !(AI_WORKFLOW_TARGET_TYPES as readonly unknown[]).includes(targetType) ||\n typeof targetId !== 'string' ||\n !DOCUMENT_ID.test(targetId)\n ) {\n return 'Pick the automation to explain'\n }\n const target = { targetType: targetType as AiWorkflowTargetType, targetId }\n if (mode === 'explain') return { mode, ...target }\n if (mode === 'revise') {\n // A workflow is a list of function calls the draft writer does not write.\n if (targetType !== 'action') return AI_WORKFLOW_REVISE_WORKFLOW_COPY\n return { mode, targetType, targetId }\n }\n const runId = inputs?.['runId']\n if (typeof runId !== 'string' || !DOCUMENT_ID.test(runId)) return 'Pick the run to explain'\n return { mode: 'diagnose', ...target, runId }\n}\n\n/** A comma-separated list of names, each kept only when `known` has it, in `known`'s order. */\nfunction knownNames<T extends string>(raw: unknown, known: readonly T[]): T[] {\n const asked = new Set(\n (typeof raw === 'string' ? raw : '')\n .split(',')\n .map((name) => name.trim())\n .filter(Boolean),\n )\n return known.filter((name) => asked.has(name))\n}\n\n/**\n * An org automation's vocabulary from a job's inputs: the triggers and steps\n * the zone named that the drafting vocabulary also has, or `null` when that\n * leaves no trigger or no step. The zone can only narrow what is drafted;\n * the section's save route holds what is saved to its own rules.\n */\nexport function readAiOrgAutomationVocabulary(\n inputs: Readonly<Record<string, unknown>> | null | undefined,\n): AiOrgAutomationVocabulary | null {\n const triggers = knownNames(inputs?.['triggers'], AI_AUTOMATION_TRIGGERS)\n const steps = knownNames(inputs?.['steps'], AI_AUTOMATION_STEP_TYPES)\n return triggers.length && steps.length ? { triggers, steps } : null\n}\n\n/** An org automation's vocabulary as a job's inputs carry it. */\nexport function aiOrgAutomationInputs(vocabulary: {\n triggers: readonly string[]\n steps: readonly string[]\n}): Record<string, string> {\n return {\n mode: 'draft',\n scope: 'org',\n triggers: vocabulary.triggers.join(','),\n steps: vocabulary.steps.join(','),\n }\n}\n\n// ── What a workspace can run ──────────────────────────────────────────────\n\n/** A part of the platform some triggers and steps need beyond the actions builder itself. */\nexport type AiAutomationNeed = 'crm' | 'webhooks' | 'bookings'\n\nexport type AiAutomationCapabilities = Readonly<Record<AiAutomationNeed, boolean>>\n\n/** Each need as a sentence names it. */\nexport const AI_AUTOMATION_NEED_LABELS: Readonly<Record<AiAutomationNeed, string>> = {\n crm: 'the CRM',\n webhooks: 'webhooks',\n bookings: 'bookings',\n}\n\n/**\n * What this workspace can run, from the entitlements the executor itself\n * reads before it runs a step: `crm` for the CRM steps, `webhooks` for a\n * webhook post, and `bookings` for the booking trigger's door.\n */\nexport function aiAutomationCapabilities(\n org: Partial<AglynOrgBilling> | Readonly<Record<string, unknown>> | null | undefined,\n): AiAutomationCapabilities {\n const billing = org as Partial<AglynOrgBilling> | null | undefined\n return {\n crm: checkEntitlement(billing, 'crm'),\n webhooks: checkEntitlement(billing, 'webhooks'),\n bookings: checkEntitlement(billing, 'bookings'),\n }\n}\n\n// ── The vocabulary ────────────────────────────────────────────────────────\n\n/**\n * The triggers a drafted automation may start on: every host event, which is\n * every event a server door emits. The on-page events (a click, a scroll) are\n * left out: they watch one element of one page, which a description cannot\n * name, and they belong to the page's own interactions.\n */\nexport const AI_AUTOMATION_TRIGGERS: readonly HostEventType[] = HOST_EVENT_TYPES\n\n/** The triggers only a door of a part of the platform emits. */\nexport const AI_AUTOMATION_TRIGGER_NEEDS: Readonly<Partial<Record<HostEventType, AiAutomationNeed>>> = {\n booking: 'bookings',\n taskCompleted: 'crm',\n contactStageChanged: 'crm',\n dealStageChanged: 'crm',\n dealWon: 'crm',\n dealLost: 'crm',\n}\n\n/**\n * The steps a drafted automation may take: the server steps and the flow\n * steps, which act on the person an event names. The on-page steps — show an\n * element, open a menu, run a script — are left out for the reason the\n * on-page triggers are: they need a selector on a page a description cannot\n * name.\n */\nexport const AI_AUTOMATION_STEP_TYPES = [\n 'sendEmail',\n 'notifyAdmins',\n 'enrollList',\n 'assignCampaign',\n 'runWorkflow',\n 'datasetAppend',\n 'updateDataset',\n 'webhookPost',\n 'siteAlert',\n 'wait',\n 'waitForEvent',\n 'exitFlow',\n 'setContactStage',\n 'addContactTag',\n 'assignContactOwner',\n 'createCrmTask',\n 'logCrmActivity',\n] as const satisfies readonly AiAutomationFormatStepType[]\n\nexport type AiAutomationStepType = (typeof AI_AUTOMATION_STEP_TYPES)[number]\n\n/** The steps the executor refuses without a part of the platform. */\nexport const AI_AUTOMATION_STEP_NEEDS: Readonly<Partial<Record<AiAutomationStepType, AiAutomationNeed>>> = {\n webhookPost: 'webhooks',\n setContactStage: 'crm',\n addContactTag: 'crm',\n assignContactOwner: 'crm',\n createCrmTask: 'crm',\n logCrmActivity: 'crm',\n}\n\n/** The trigger's name as the Actions editor labels it. */\nexport function aiAutomationTriggerLabel(event: string): string {\n return HOST_EVENT_LABELS[event as HostEventType] ?? event\n}\n\n// ── A description the vocabulary cannot build ─────────────────────────────\n\n/**\n * Why a description was answered with no automation. Each is a fixed reason\n * with a fixed sentence, so what a person reads is never the model's own\n * account of itself.\n */\nexport const AI_AUTOMATION_UNSUPPORTED = [\n 'not-an-automation',\n 'no-trigger',\n 'no-step',\n 'needs-crm',\n 'needs-webhooks',\n 'needs-bookings',\n] as const\n\nexport type AiAutomationUnsupported = (typeof AI_AUTOMATION_UNSUPPORTED)[number]\n\n/** The need an unsupported reason names, for the reasons that name one. */\nexport const AI_AUTOMATION_UNSUPPORTED_NEED: Readonly<\n Partial<Record<AiAutomationUnsupported, AiAutomationNeed>>\n> = {\n 'needs-crm': 'crm',\n 'needs-webhooks': 'webhooks',\n 'needs-bookings': 'bookings',\n}\n\nexport const AI_AUTOMATION_UNSUPPORTED_COPY: Readonly<Record<AiAutomationUnsupported, string>> = {\n 'not-an-automation':\n 'This description is not an automation — something that happens, and what to do then — so nothing was drafted.',\n 'no-trigger':\n 'Nothing on the platform can start an automation like this one, so nothing was drafted. Describe it as something that happens on your site, such as a form being submitted.',\n 'no-step': 'An automation cannot do what this description asks yet, so nothing was drafted.',\n 'needs-crm':\n 'This automation needs the CRM, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-webhooks':\n 'This automation needs webhooks, which this workspace’s plan does not include, so nothing was drafted.',\n 'needs-bookings':\n 'This automation needs bookings, which this workspace’s plan does not include, so nothing was drafted.',\n}\n\n// ── What a job tells a person ─────────────────────────────────────────────\n\n/** The resource the workflows plugin writes a drafted automation under. */\nexport const AI_AUTOMATION_RESOURCE = 'automation'\n\nexport const AI_WORKFLOW_NO_SITE_COPY = 'This automation job names a site this workspace does not have.'\nexport const AI_WORKFLOW_UNAVAILABLE_COPY = 'Automations are not available on this site.'\nexport const AI_WORKFLOW_GONE_COPY = 'That automation no longer exists.'\nexport const AI_WORKFLOW_RUN_GONE_COPY = 'That run is no longer in the automation’s history.'\nexport const AI_WORKFLOW_RUN_NOT_FAILED_COPY = 'That run did not fail, so there is nothing to explain.'\nexport const AI_WORKFLOW_NO_DRAFT_COPY =\n 'The AI could not draft an automation from this description. Try describing what happens, and what should happen then.'\nexport const AI_WORKFLOW_NO_EXPLANATION_COPY =\n 'The AI could not explain this automation. Try again.'\nexport const AI_WORKFLOW_SAVE_FAILURE_COPY =\n 'The automation was drafted but could not be saved. Try the job again.'\nexport const AI_ORG_AUTOMATION_NO_VOCABULARY_COPY =\n 'Open Org automations to draft one: the page says what an org automation can start on and do.'\nexport const AI_ORG_AUTOMATION_UNAVAILABLE_COPY =\n 'Turn on Automation for this workspace before drafting an org automation.'\nexport const AI_ORG_AUTOMATION_PLAN_COPY =\n 'Org automations are built from the actions builder, which this workspace’s plan does not include. Upgrade in Billing to build them.'\n\n/**\n * The output resource an org automation's proposal rides on (AGL-3603): never\n * a document — the Org automations editor opens it unsaved.\n */\nexport const AI_ORG_AUTOMATION_RESOURCE = 'orgAutomation'\n\n/** The automation an `orgAutomation` output proposes, as the editor opens it. */\nexport interface AiOrgAutomationProposal {\n name: string\n trigger: {\n event: string\n conditions?: Array<{ field: string; op: string; value?: string }>\n combinator?: 'and' | 'or'\n }\n steps: Array<Record<string, unknown> & { type: string }>\n}\n\n/** An output's proposal read back, or `null` for one that is not an org automation. */\nexport function readAiOrgAutomationProposal(value: unknown): AiOrgAutomationProposal | null {\n const automation = (value as { automation?: unknown } | null | undefined)?.automation as\n | Record<string, unknown>\n | null\n | undefined\n if (!automation || typeof automation !== 'object') return null\n const trigger = automation['trigger'] as Record<string, unknown> | null | undefined\n const steps = automation['steps']\n if (!trigger || typeof trigger['event'] !== 'string' || !Array.isArray(steps)) return null\n if (!steps.every((step) => step && typeof step === 'object' && typeof (step as { type?: unknown }).type === 'string')) {\n return null\n }\n return {\n name: typeof automation['name'] === 'string' ? automation['name'] : '',\n trigger: {\n event: trigger['event'],\n ...(Array.isArray(trigger['conditions'])\n ? { conditions: trigger['conditions'] as NonNullable<AiOrgAutomationProposal['trigger']['conditions']> }\n : {}),\n ...(trigger['combinator'] === 'or' || trigger['combinator'] === 'and'\n ? { combinator: trigger['combinator'] }\n : {}),\n },\n steps: steps as AiOrgAutomationProposal['steps'],\n }\n}\n\nexport const AI_WORKFLOW_REVISE_WORKFLOW_COPY =\n 'Only an action can be changed with AI. A workflow can be explained, and edited in its own editor.'\n\n// ── An action AI can revise ───────────────────────────────────────────────\n\n/** Why a saved action cannot be revised, each with the sentence a person reads. */\nexport const AI_WORKFLOW_REVISE_BLOCKERS = ['page-event', 'page-step', 'filter'] as const\nexport type AiWorkflowReviseBlocker = (typeof AI_WORKFLOW_REVISE_BLOCKERS)[number]\n\nexport const AI_WORKFLOW_REVISE_BLOCKER_COPY: Readonly<Record<AiWorkflowReviseBlocker, string>> = {\n 'page-event':\n 'This action starts on something a visitor does on a page, which AI does not write, so it cannot be changed with AI. Edit it here instead.',\n 'page-step':\n 'This action has a step that runs on the page, which AI does not write, so it cannot be changed with AI without losing that step. Edit it here instead.',\n filter:\n 'This action only runs when an expression is true, which AI does not write, so it cannot be changed with AI without losing it. Edit it here instead.',\n}\n\n/**\n * Why a saved action cannot be revised, or `null` when every part of it is\n * one the drafting vocabulary writes — so the revised draft can hold the\n * whole action and lose nothing the person did not ask to change.\n */\nexport function aiActionReviseBlocker(action: {\n trigger?: { event?: unknown; filter?: unknown } | null\n steps?: ReadonlyArray<{ type?: unknown } | null | undefined> | null\n}): AiWorkflowReviseBlocker | null {\n const event = action.trigger?.event\n if (!(AI_AUTOMATION_TRIGGERS as readonly unknown[]).includes(event)) return 'page-event'\n const steps = action.steps ?? []\n if (steps.some((step) => !(AI_AUTOMATION_STEP_TYPES as readonly unknown[]).includes(step?.type))) {\n return 'page-step'\n }\n const filter = action.trigger?.filter\n if (typeof filter === 'string' && filter.trim()) return 'filter'\n return null\n}\n"],"names":["checkEntitlement","HOST_EVENT_LABELS","HOST_EVENT_TYPES","AI_WORKFLOW_JOB_MODES","AI_WORKFLOW_TARGET_TYPES","DOCUMENT_ID","parseAiWorkflowJobInputs","inputs","mode","includes","vocabulary","readAiOrgAutomationVocabulary","scope","AI_ORG_AUTOMATION_NO_VOCABULARY_COPY","targetType","targetId","test","target","AI_WORKFLOW_REVISE_WORKFLOW_COPY","runId","knownNames","raw","known","asked","Set","split","map","name","trim","filter","Boolean","has","triggers","AI_AUTOMATION_TRIGGERS","steps","AI_AUTOMATION_STEP_TYPES","length","aiOrgAutomationInputs","join","AI_AUTOMATION_NEED_LABELS","crm","webhooks","bookings","aiAutomationCapabilities","org","billing","AI_AUTOMATION_TRIGGER_NEEDS","booking","taskCompleted","contactStageChanged","dealStageChanged","dealWon","dealLost","AI_AUTOMATION_STEP_NEEDS","webhookPost","setContactStage","addContactTag","assignContactOwner","createCrmTask","logCrmActivity","aiAutomationTriggerLabel","event","AI_AUTOMATION_UNSUPPORTED","AI_AUTOMATION_UNSUPPORTED_NEED","AI_AUTOMATION_UNSUPPORTED_COPY","AI_AUTOMATION_RESOURCE","AI_WORKFLOW_NO_SITE_COPY","AI_WORKFLOW_UNAVAILABLE_COPY","AI_WORKFLOW_GONE_COPY","AI_WORKFLOW_RUN_GONE_COPY","AI_WORKFLOW_RUN_NOT_FAILED_COPY","AI_WORKFLOW_NO_DRAFT_COPY","AI_WORKFLOW_NO_EXPLANATION_COPY","AI_WORKFLOW_SAVE_FAILURE_COPY","AI_ORG_AUTOMATION_UNAVAILABLE_COPY","AI_ORG_AUTOMATION_PLAN_COPY","AI_ORG_AUTOMATION_RESOURCE","readAiOrgAutomationProposal","value","automation","trigger","Array","isArray","every","step","type","conditions","combinator","AI_WORKFLOW_REVISE_BLOCKERS","AI_WORKFLOW_REVISE_BLOCKER_COPY","aiActionReviseBlocker","action","some"],"mappings":"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"}
@@ -0,0 +1,58 @@
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
+ /**
18
+ * A build proposed from the Assist chat (AGL-3616): one request that names
19
+ * several things to make on a site — pages, a header and footer, a form, a
20
+ * booking service — turned into ONE `build` job. The chat proposes; the panel
21
+ * starts the job, whose plan step plans it against the site's real inventory
22
+ * and stops with one plan card and one price; nothing is built until the
23
+ * person confirms that card.
24
+ *
25
+ * Shared by the chat door that resolves it and the panel that starts it.
26
+ * Pure: no request, no React.
27
+ */
28
+ /** The strict tool the chat model proposes a build through. */
29
+ export declare const ASSIST_BUILD_TOOL_NAME = "propose_build";
30
+ /** The proposal's fixed id, as `assistant_proposal_shown` records it. */
31
+ export declare const ASSIST_BUILD_ACTION_ID = "build";
32
+ /** The longest one-line summary the panel shows above the plan card. */
33
+ export declare const ASSIST_BUILD_SUMMARY_MAX_CHARS = 200;
34
+ export interface AssistBuildProposal {
35
+ id: typeof ASSIST_BUILD_ACTION_ID;
36
+ /** The site the request was asked on; the job is created for it. */
37
+ hostId: string;
38
+ /** What to build, in the person's words plus what the console page implies. */
39
+ brief: string;
40
+ /** Whether the request asked for the new pages to go live; the plan card still asks. */
41
+ publish: boolean;
42
+ /** One line saying what will be planned. */
43
+ summary: string;
44
+ }
45
+ /** Shape-checks a build proposal before it can start a job. */
46
+ export declare function isAssistBuildProposal(value: unknown): value is AssistBuildProposal;
47
+ /**
48
+ * The body of `POST /api/ai/jobs` a proposal starts: a `build` on its site,
49
+ * asking to publish only when the request did. The door climbs its own
50
+ * ladder and takes its own reservation, as every job does.
51
+ */
52
+ export declare function assistBuildJobRequest(orgId: string, proposal: AssistBuildProposal): {
53
+ orgId: string;
54
+ hostId: string;
55
+ kind: 'build';
56
+ brief: string;
57
+ inputs: Record<string, boolean>;
58
+ };
@@ -0,0 +1,52 @@
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
+ * A build proposed from the Assist chat (AGL-3616): one request that names
18
+ * several things to make on a site — pages, a header and footer, a form, a
19
+ * booking service — turned into ONE `build` job. The chat proposes; the panel
20
+ * starts the job, whose plan step plans it against the site's real inventory
21
+ * and stops with one plan card and one price; nothing is built until the
22
+ * person confirms that card.
23
+ *
24
+ * Shared by the chat door that resolves it and the panel that starts it.
25
+ * Pure: no request, no React.
26
+ */ /** The strict tool the chat model proposes a build through. */ export const ASSIST_BUILD_TOOL_NAME = 'propose_build';
27
+ /** The proposal's fixed id, as `assistant_proposal_shown` records it. */ export const ASSIST_BUILD_ACTION_ID = 'build';
28
+ /** The longest one-line summary the panel shows above the plan card. */ export const ASSIST_BUILD_SUMMARY_MAX_CHARS = 200;
29
+ function isRecord(value) {
30
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
31
+ }
32
+ /** Shape-checks a build proposal before it can start a job. */ export function isAssistBuildProposal(value) {
33
+ if (!isRecord(value) || value['id'] !== ASSIST_BUILD_ACTION_ID) return false;
34
+ return typeof value['hostId'] === 'string' && /^[A-Za-z0-9_-]{1,100}$/.test(value['hostId']) && typeof value['brief'] === 'string' && value['brief'].trim().length > 0 && typeof value['publish'] === 'boolean' && typeof value['summary'] === 'string';
35
+ }
36
+ /**
37
+ * The body of `POST /api/ai/jobs` a proposal starts: a `build` on its site,
38
+ * asking to publish only when the request did. The door climbs its own
39
+ * ladder and takes its own reservation, as every job does.
40
+ */ export function assistBuildJobRequest(orgId, proposal) {
41
+ return {
42
+ orgId,
43
+ hostId: proposal.hostId,
44
+ kind: 'build',
45
+ brief: proposal.brief,
46
+ inputs: proposal.publish ? {
47
+ publish: true
48
+ } : {}
49
+ };
50
+ }
51
+
52
+ //# sourceMappingURL=assist-build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/model/assist-build.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * A build proposed from the Assist chat (AGL-3616): one request that names\n * several things to make on a site — pages, a header and footer, a form, a\n * booking service — turned into ONE `build` job. The chat proposes; the panel\n * starts the job, whose plan step plans it against the site's real inventory\n * and stops with one plan card and one price; nothing is built until the\n * person confirms that card.\n *\n * Shared by the chat door that resolves it and the panel that starts it.\n * Pure: no request, no React.\n */\n\n/** The strict tool the chat model proposes a build through. */\nexport const ASSIST_BUILD_TOOL_NAME = 'propose_build'\n\n/** The proposal's fixed id, as `assistant_proposal_shown` records it. */\nexport const ASSIST_BUILD_ACTION_ID = 'build'\n\n/** The longest one-line summary the panel shows above the plan card. */\nexport const ASSIST_BUILD_SUMMARY_MAX_CHARS = 200\n\nexport interface AssistBuildProposal {\n id: typeof ASSIST_BUILD_ACTION_ID\n /** The site the request was asked on; the job is created for it. */\n hostId: string\n /** What to build, in the person's words plus what the console page implies. */\n brief: string\n /** Whether the request asked for the new pages to go live; the plan card still asks. */\n publish: boolean\n /** One line saying what will be planned. */\n summary: string\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Shape-checks a build proposal before it can start a job. */\nexport function isAssistBuildProposal(value: unknown): value is AssistBuildProposal {\n if (!isRecord(value) || value['id'] !== ASSIST_BUILD_ACTION_ID) return false\n return (\n typeof value['hostId'] === 'string' &&\n /^[A-Za-z0-9_-]{1,100}$/.test(value['hostId']) &&\n typeof value['brief'] === 'string' &&\n value['brief'].trim().length > 0 &&\n typeof value['publish'] === 'boolean' &&\n typeof value['summary'] === 'string'\n )\n}\n\n/**\n * The body of `POST /api/ai/jobs` a proposal starts: a `build` on its site,\n * asking to publish only when the request did. The door climbs its own\n * ladder and takes its own reservation, as every job does.\n */\nexport function assistBuildJobRequest(\n orgId: string,\n proposal: AssistBuildProposal,\n): { orgId: string; hostId: string; kind: 'build'; brief: string; inputs: Record<string, boolean> } {\n return {\n orgId,\n hostId: proposal.hostId,\n kind: 'build',\n brief: proposal.brief,\n inputs: proposal.publish ? { publish: true } : {},\n }\n}\n"],"names":["ASSIST_BUILD_TOOL_NAME","ASSIST_BUILD_ACTION_ID","ASSIST_BUILD_SUMMARY_MAX_CHARS","isRecord","value","Array","isArray","isAssistBuildProposal","test","trim","length","assistBuildJobRequest","orgId","proposal","hostId","kind","brief","inputs","publish"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;CAUC,GAED,6DAA6D,GAC7D,OAAO,MAAMA,yBAAyB,gBAAe;AAErD,uEAAuE,GACvE,OAAO,MAAMC,yBAAyB,QAAO;AAE7C,sEAAsE,GACtE,OAAO,MAAMC,iCAAiC,IAAG;AAcjD,SAASC,SAASC,KAAc;IAC9B,OAAO,OAAOA,UAAU,YAAYA,UAAU,QAAQ,CAACC,MAAMC,OAAO,CAACF;AACvE;AAEA,6DAA6D,GAC7D,OAAO,SAASG,sBAAsBH,KAAc;IAClD,IAAI,CAACD,SAASC,UAAUA,KAAK,CAAC,KAAK,KAAKH,wBAAwB,OAAO;IACvE,OACE,OAAOG,KAAK,CAAC,SAAS,KAAK,YAC3B,yBAAyBI,IAAI,CAACJ,KAAK,CAAC,SAAS,KAC7C,OAAOA,KAAK,CAAC,QAAQ,KAAK,YAC1BA,KAAK,CAAC,QAAQ,CAACK,IAAI,GAAGC,MAAM,GAAG,KAC/B,OAAON,KAAK,CAAC,UAAU,KAAK,aAC5B,OAAOA,KAAK,CAAC,UAAU,KAAK;AAEhC;AAEA;;;;CAIC,GACD,OAAO,SAASO,sBACdC,KAAa,EACbC,QAA6B;IAE7B,OAAO;QACLD;QACAE,QAAQD,SAASC,MAAM;QACvBC,MAAM;QACNC,OAAOH,SAASG,KAAK;QACrBC,QAAQJ,SAASK,OAAO,GAAG;YAAEA,SAAS;QAAK,IAAI,CAAC;IAClD;AACF"}
@@ -16,6 +16,7 @@
16
16
  */
17
17
  import type { ScreenSeoTextField } from '@aglyn/aglyn/app-utils/screen-seo-fields';
18
18
  import type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/platform.types';
19
+ import type { NodeInteraction } from '@aglyn/aglyn/app-utils/node-interactions';
19
20
  /**
20
21
  * Assist level 3, the edit contract (AGL-2906): what the chat door proposes
21
22
  * for a besigner canvas, and what the panel applies once the author says so.
@@ -33,8 +34,8 @@ import type { ReusableComponentProp } from '@aglyn/aglyn/foundation/definitions/
33
34
  * an unsaved edit on the version the editor has open — a draft — and on a
34
35
  * version the live site serves, the editor's own new-version flow runs first.
35
36
  */
36
- /** The eight operations a proposal may carry. */
37
- export declare const ASSIST_EDIT_OP_KINDS: readonly ["insertSubtree", "updateProps", "updateSx", "move", "remove", "setSeo", "rename", "saveAsComponent"];
37
+ /** The nine operations a proposal may carry. */
38
+ export declare const ASSIST_EDIT_OP_KINDS: readonly ["insertSubtree", "updateProps", "updateSx", "move", "remove", "setSeo", "rename", "saveAsComponent", "addInteraction"];
38
39
  export type AssistEditOpKind = (typeof ASSIST_EDIT_OP_KINDS)[number];
39
40
  /** The id of the besigner view's edit action — the one edit a view offers. */
40
41
  export declare const ASSIST_EDIT_ACTION_ID = "edit.canvas";
@@ -91,7 +92,35 @@ export interface AssistEditCanvasNode {
91
92
  * the way the cards of a row are. Its own contents are left out.
92
93
  */
93
94
  like?: string;
95
+ /**
96
+ * The interactions the element carries (AGL-3603), each by its name, the
97
+ * event it listens for and its steps' kinds — carried for the selected
98
+ * element only, so Assist can say what it does. Never a selector, a script
99
+ * or HTML.
100
+ */
101
+ interactions?: AssistEditInteractionSummary[];
102
+ }
103
+ /** One interaction on an element, as an outline describes it. */
104
+ export interface AssistEditInteractionSummary {
105
+ name: string;
106
+ event: string;
107
+ steps: string[];
108
+ enabled: boolean;
94
109
  }
110
+ /**
111
+ * The events an interaction Assist adds may listen for: those that watch the
112
+ * element it is added to, the way the Besigner's Interactions section offers
113
+ * them on a selected element.
114
+ */
115
+ export declare const ASSIST_EDIT_INTERACTION_EVENTS: readonly ["elementClick", "elementHoverEnter", "elementHoverLeave", "elementVisible", "scrollToElement"];
116
+ export type AssistEditInteractionEvent = (typeof ASSIST_EDIT_INTERACTION_EVENTS)[number];
117
+ /**
118
+ * The steps it may take: the platform's basic presentational client steps,
119
+ * which run on every plan and touch no data. No script, no HTML, no
120
+ * attribute, no overlay, no analytics event — and no server step.
121
+ */
122
+ export declare const ASSIST_EDIT_INTERACTION_STEPS: readonly ["showElement", "hideElement", "toggleElement", "addClass", "removeClass", "toggleClass", "scrollTo", "playVideo", "openDrawer", "closeDrawer", "toggleDrawer", "openMenu", "closeMenu", "toggleMenu", "redirect", "siteAlert"];
123
+ export type AssistEditInteractionStep = (typeof ASSIST_EDIT_INTERACTION_STEPS)[number];
95
124
  export interface AssistEditCanvasContext {
96
125
  /** The element the author has selected, or null. */
97
126
  selectedId: string | null;
@@ -198,7 +227,21 @@ export interface AssistEditSaveAsComponentOp extends AssistEditNodeOpBase {
198
227
  props: AssistEditComponentProp[];
199
228
  bindings: AssistEditPropBinding[];
200
229
  }
201
- export type AssistEditOp = AssistEditInsertOp | AssistEditUpdatePropsOp | AssistEditUpdateSxOp | AssistEditMoveOp | AssistEditRemoveOp | AssistEditRenameOp | AssistEditSetSeoOp | AssistEditSaveAsComponentOp;
230
+ /**
231
+ * Add one interaction to an element (AGL-3603), in the shape the element
232
+ * stores it — its trigger with no selector, which the element's id derives.
233
+ * It never replaces or removes one the element already carries.
234
+ */
235
+ export interface AssistEditAddInteractionOp extends AssistEditNodeOpBase {
236
+ op: 'addInteraction';
237
+ interaction: NodeInteraction;
238
+ /** Every other element a step acts on, each with the component it was validated as. */
239
+ targets: Array<{
240
+ nodeId: string;
241
+ componentId: string;
242
+ }>;
243
+ }
244
+ export type AssistEditOp = AssistEditAddInteractionOp | AssistEditInsertOp | AssistEditUpdatePropsOp | AssistEditUpdateSxOp | AssistEditMoveOp | AssistEditRemoveOp | AssistEditRenameOp | AssistEditSetSeoOp | AssistEditSaveAsComponentOp;
202
245
  /** Where a proposal applies, as the server read it off the request. */
203
246
  export interface AssistEditTarget extends AssistEditDocument {
204
247
  hostId: string;
@@ -219,6 +262,8 @@ export interface AssistEditDiff {
219
262
  componentProps: number;
220
263
  /** Selections saved as a reusable component — at most one per proposal. */
221
264
  componentsSaved: number;
265
+ /** Interactions added to elements. */
266
+ interactionsAdded: number;
222
267
  }
223
268
  /** The most reasons a proposal carries for the changes validation left out. */
224
269
  export declare const ASSIST_EDIT_MAX_DROPPED_REASONS = 10;
@@ -29,7 +29,7 @@
29
29
  * guarded mutators, on confirm, as one undoable step. Every change lands as
30
30
  * an unsaved edit on the version the editor has open — a draft — and on a
31
31
  * version the live site serves, the editor's own new-version flow runs first.
32
- */ /** The eight operations a proposal may carry. */ export const ASSIST_EDIT_OP_KINDS = [
32
+ */ /** The nine operations a proposal may carry. */ export const ASSIST_EDIT_OP_KINDS = [
33
33
  'insertSubtree',
34
34
  'updateProps',
35
35
  'updateSx',
@@ -37,7 +37,8 @@
37
37
  'remove',
38
38
  'setSeo',
39
39
  'rename',
40
- 'saveAsComponent'
40
+ 'saveAsComponent',
41
+ 'addInteraction'
41
42
  ];
42
43
  /** The id of the besigner view's edit action — the one edit a view offers. */ export const ASSIST_EDIT_ACTION_ID = 'edit.canvas';
43
44
  /** The name of the structured-output tool a model proposes an edit through. */ export const ASSIST_EDIT_TOOL_NAME = 'propose_canvas_edit';
@@ -71,6 +72,41 @@ const BESIGNER_DOCUMENT_ROUTE = /^\/[^/]+\/hosts\/[^/]+\/(screens|components|lay
71
72
  /** Props described per element at most. */ export const ASSIST_EDIT_CONTEXT_MAX_PROPS = 16;
72
73
  /** Characters of a string prop the selected element carries into a request. */ export const ASSIST_EDIT_SELECTED_TEXT_CHARS = 400;
73
74
  /** Characters of a string prop every other element carries. */ export const ASSIST_EDIT_OUTLINE_TEXT_CHARS = 80;
75
+ /* ------------------------------------------------------------------ *
76
+ * The interactions Assist may add (AGL-3603)
77
+ * ------------------------------------------------------------------ */ /**
78
+ * The events an interaction Assist adds may listen for: those that watch the
79
+ * element it is added to, the way the Besigner's Interactions section offers
80
+ * them on a selected element.
81
+ */ export const ASSIST_EDIT_INTERACTION_EVENTS = [
82
+ 'elementClick',
83
+ 'elementHoverEnter',
84
+ 'elementHoverLeave',
85
+ 'elementVisible',
86
+ 'scrollToElement'
87
+ ];
88
+ /**
89
+ * The steps it may take: the platform's basic presentational client steps,
90
+ * which run on every plan and touch no data. No script, no HTML, no
91
+ * attribute, no overlay, no analytics event — and no server step.
92
+ */ export const ASSIST_EDIT_INTERACTION_STEPS = [
93
+ 'showElement',
94
+ 'hideElement',
95
+ 'toggleElement',
96
+ 'addClass',
97
+ 'removeClass',
98
+ 'toggleClass',
99
+ 'scrollTo',
100
+ 'playVideo',
101
+ 'openDrawer',
102
+ 'closeDrawer',
103
+ 'toggleDrawer',
104
+ 'openMenu',
105
+ 'closeMenu',
106
+ 'toggleMenu',
107
+ 'redirect',
108
+ 'siteAlert'
109
+ ];
74
110
  /** The most reasons a proposal carries for the changes validation left out. */ export const ASSIST_EDIT_MAX_DROPPED_REASONS = 10;
75
111
  /** The counts for a list of ops. */ export function summarizeAssistEditOps(ops) {
76
112
  const diff = {
@@ -82,7 +118,8 @@ const BESIGNER_DOCUMENT_ROUTE = /^\/[^/]+\/hosts\/[^/]+\/(screens|components|lay
82
118
  renamed: 0,
83
119
  seoFields: 0,
84
120
  componentProps: 0,
85
- componentsSaved: 0
121
+ componentsSaved: 0,
122
+ interactionsAdded: 0
86
123
  };
87
124
  for (const op of ops){
88
125
  switch(op.op){
@@ -111,6 +148,9 @@ const BESIGNER_DOCUMENT_ROUTE = /^\/[^/]+\/hosts\/[^/]+\/(screens|components|lay
111
148
  diff.componentsSaved += 1;
112
149
  diff.componentProps += op.props.length;
113
150
  break;
151
+ case 'addInteraction':
152
+ diff.interactionsAdded += 1;
153
+ break;
114
154
  }
115
155
  }
116
156
  return diff;
@@ -131,6 +171,9 @@ const plural = (count, one, many)=>`${count} ${count === 1 ? one : many}`;
131
171
  if (diff.seoFields) {
132
172
  lines.push(`${plural(diff.seoFields, 'search field', 'search fields')} filled in`);
133
173
  }
174
+ if (diff.interactionsAdded) {
175
+ lines.push(`${plural(diff.interactionsAdded, 'interaction', 'interactions')} added`);
176
+ }
134
177
  if (diff.componentsSaved) {
135
178
  lines.push(`saved as a reusable component with ${plural(diff.componentProps, 'property', 'properties')}`);
136
179
  }
@@ -144,7 +187,8 @@ const plural = (count, one, many)=>`${count} ${count === 1 ? one : many}`;
144
187
  remove: 'remove',
145
188
  rename: 'rename',
146
189
  setSeo: 'seo',
147
- saveAsComponent: 'component'
190
+ saveAsComponent: 'component',
191
+ addInteraction: 'interaction'
148
192
  };
149
193
  /** The applied ops counted by kind, as the activity row names them (`{ set: 3, insert: 1 }`). */ export function assistEditOpCounts(ops) {
150
194
  const counts = {};