@aglyn/plugins-workflows 1.0.0-beta.221 → 1.0.0-beta.223

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 (101) hide show
  1. package/README.md +22 -14
  2. package/package.json +12 -12
  3. package/src/lib/components/automation-step-fields.component.d.ts +16 -16
  4. package/src/lib/components/automation-step-fields.component.js +146 -12
  5. package/src/lib/components/automation-step-fields.component.js.map +1 -1
  6. package/src/lib/components/automation-trigger-conditions.component.d.ts +25 -3
  7. package/src/lib/components/automation-trigger-conditions.component.js +89 -10
  8. package/src/lib/components/automation-trigger-conditions.component.js.map +1 -1
  9. package/src/lib/components/host-actions-card.component.js +126 -84
  10. package/src/lib/components/host-actions-card.component.js.map +1 -1
  11. package/src/lib/components/host-webhooks-card.component.js +2 -1
  12. package/src/lib/components/host-webhooks-card.component.js.map +1 -1
  13. package/src/lib/components/host-workflows-card.component.js +31 -10
  14. package/src/lib/components/host-workflows-card.component.js.map +1 -1
  15. package/src/lib/components/org-automation-editor.component.d.ts +2 -1
  16. package/src/lib/components/org-automation-editor.component.js +20 -11
  17. package/src/lib/components/org-automation-editor.component.js.map +1 -1
  18. package/src/lib/components/org-automations-card.component.js +2 -1
  19. package/src/lib/components/org-automations-card.component.js.map +1 -1
  20. package/src/lib/components/run-quota-line.component.d.ts +17 -4
  21. package/src/lib/components/run-quota-line.component.js +16 -7
  22. package/src/lib/components/run-quota-line.component.js.map +1 -1
  23. package/src/lib/components/site-org-automations-panel.component.js +2 -1
  24. package/src/lib/components/site-org-automations-panel.component.js.map +1 -1
  25. package/src/lib/components/workflows-console-page.js +18 -7
  26. package/src/lib/components/workflows-console-page.js.map +1 -1
  27. package/src/lib/declarations.d.ts +28 -0
  28. package/src/lib/declarations.js +37 -0
  29. package/src/lib/declarations.js.map +1 -0
  30. package/src/lib/declarations.server.js +17 -0
  31. package/src/lib/declarations.server.js.map +1 -1
  32. package/src/lib/engine/email-merge.d.ts +69 -0
  33. package/src/lib/engine/email-merge.js +98 -0
  34. package/src/lib/engine/email-merge.js.map +1 -0
  35. package/src/lib/engine/run-event-actions.d.ts +35 -10
  36. package/src/lib/engine/run-event-actions.js +256 -369
  37. package/src/lib/engine/run-event-actions.js.map +1 -1
  38. package/src/lib/engine/run-event-automations.d.ts +1 -1
  39. package/src/lib/engine/run-event-automations.js.map +1 -1
  40. package/src/lib/engine/run-event-workflows.d.ts +2 -1
  41. package/src/lib/engine/run-event-workflows.js +71 -30
  42. package/src/lib/engine/run-event-workflows.js.map +1 -1
  43. package/src/lib/engine/run-meter.d.ts +98 -0
  44. package/src/lib/engine/run-meter.js +121 -0
  45. package/src/lib/engine/run-meter.js.map +1 -0
  46. package/src/lib/engine/run-org-automations.d.ts +3 -2
  47. package/src/lib/engine/run-org-automations.js +17 -12
  48. package/src/lib/engine/run-org-automations.js.map +1 -1
  49. package/src/lib/engine/triggered-docs.d.ts +39 -0
  50. package/src/lib/engine/triggered-docs.js +47 -0
  51. package/src/lib/engine/triggered-docs.js.map +1 -0
  52. package/src/lib/engine/workflow-steps.d.ts +1 -1
  53. package/src/lib/engine/workflow-steps.js +3 -2
  54. package/src/lib/engine/workflow-steps.js.map +1 -1
  55. package/src/lib/model/host-actions.d.ts +284 -0
  56. package/src/lib/model/host-actions.js +316 -0
  57. package/src/lib/model/host-actions.js.map +1 -0
  58. package/src/lib/model/org-automations.d.ts +8 -5
  59. package/src/lib/model/org-automations.js +14 -8
  60. package/src/lib/model/org-automations.js.map +1 -1
  61. package/src/lib/model/step-outcomes.d.ts +2 -2
  62. package/src/lib/model/step-outcomes.js +1 -1
  63. package/src/lib/model/step-outcomes.js.map +1 -1
  64. package/src/lib/plugin.js +10 -0
  65. package/src/lib/plugin.js.map +1 -1
  66. package/src/lib/server/action-test-run-route.d.ts +3 -2
  67. package/src/lib/server/action-test-run-route.js +3 -2
  68. package/src/lib/server/action-test-run-route.js.map +1 -1
  69. package/src/lib/server/org-automations-routes.d.ts +42 -1
  70. package/src/lib/server/org-automations-routes.js +120 -72
  71. package/src/lib/server/org-automations-routes.js.map +1 -1
  72. package/src/lib/server-automation-drafts.d.ts +2 -2
  73. package/src/lib/server-automation-drafts.js +11 -7
  74. package/src/lib/server-automation-drafts.js.map +1 -1
  75. package/src/lib/server.js +19 -13
  76. package/src/lib/server.js.map +1 -1
  77. package/src/lib/testing/stand-in-crm-steps.d.ts +45 -0
  78. package/src/lib/testing/stand-in-crm-steps.js +97 -0
  79. package/src/lib/testing/stand-in-crm-steps.js.map +1 -0
  80. package/src/lib/testing/stand-in-dataset-list.js +11 -3
  81. package/src/lib/testing/stand-in-dataset-list.js.map +1 -1
  82. package/src/lib/testing/stand-in-dataset-steps.d.ts +43 -0
  83. package/src/lib/testing/stand-in-dataset-steps.js +65 -0
  84. package/src/lib/testing/stand-in-dataset-steps.js.map +1 -0
  85. package/src/lib/testing/stand-in-person-records.d.ts +35 -0
  86. package/src/lib/testing/stand-in-person-records.js +54 -0
  87. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  88. package/src/lib/testing/stand-in-recipes.d.ts +36 -0
  89. package/src/lib/testing/stand-in-recipes.js +206 -0
  90. package/src/lib/testing/stand-in-recipes.js.map +1 -0
  91. package/src/lib/transfer/org-automations-package.d.ts +54 -0
  92. package/src/lib/transfer/org-automations-package.js +167 -0
  93. package/src/lib/transfer/org-automations-package.js.map +1 -0
  94. package/src/lib/transfer/org-automations-package.server.d.ts +38 -0
  95. package/src/lib/transfer/org-automations-package.server.js +296 -0
  96. package/src/lib/transfer/org-automations-package.server.js.map +1 -0
  97. package/src/lib/usage-axes.js +5 -1
  98. package/src/lib/usage-axes.js.map +1 -1
  99. package/src/lib/engine/crm-action-steps.d.ts +0 -142
  100. package/src/lib/engine/crm-action-steps.js +0 -452
  101. package/src/lib/engine/crm-action-steps.js.map +0 -1
@@ -0,0 +1,37 @@
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
+ */ // The seam from its own module, not the plugin-manager barrel: both apps load
17
+ // this at boot, and the barrel reaches the client contexts.
18
+ import { registerInteractionStepChecks } from "@aglyn/aglyn/plugin-manager/interaction-step-checks";
19
+ import { BUNDLE_ID } from "./constants/bundle-common.js";
20
+ import { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from "./model/host-actions.js";
21
+ /**
22
+ * The automation editor's DECLARATIONS: what another surface reads of this
23
+ * plugin before any of its own surfaces load, in both apps and in the
24
+ * console's browser.
25
+ *
26
+ * The checks of the server steps an automation holds (AGL-3080), so a plugin
27
+ * that writes an automation it does not edit — a recipe installed into a
28
+ * site, a drafted automation graded before it is handed over — refuses what
29
+ * this editor would (`validateStoredInteraction`). Light by construction: the
30
+ * checks are pure, and nothing here reaches Firestore or React.
31
+ */ export function registerWorkflowsDeclarations() {
32
+ registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, {
33
+ pluginId: BUNDLE_ID
34
+ });
35
+ }
36
+
37
+ //# sourceMappingURL=declarations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.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// The seam from its own module, not the plugin-manager barrel: both apps load\n// this at boot, and the barrel reaches the client contexts.\nimport { registerInteractionStepChecks } from '@aglyn/aglyn/plugin-manager/interaction-step-checks'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { hostActionStepProblem, SERVER_ACTION_STEP_TYPES } from './model/host-actions'\n\n/**\n * The automation editor's DECLARATIONS: what another surface reads of this\n * plugin before any of its own surfaces load, in both apps and in the\n * console's browser.\n *\n * The checks of the server steps an automation holds (AGL-3080), so a plugin\n * that writes an automation it does not edit — a recipe installed into a\n * site, a drafted automation graded before it is handed over — refuses what\n * this editor would (`validateStoredInteraction`). Light by construction: the\n * checks are pure, and nothing here reaches Firestore or React.\n */\nexport function registerWorkflowsDeclarations(): void {\n registerInteractionStepChecks(SERVER_ACTION_STEP_TYPES, hostActionStepProblem, { pluginId: BUNDLE_ID })\n}\n"],"names":["registerInteractionStepChecks","BUNDLE_ID","hostActionStepProblem","SERVER_ACTION_STEP_TYPES","registerWorkflowsDeclarations","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,8EAA8E;AAC9E,4DAA4D;AAC5D,SAASA,6BAA6B,QAAQ,sDAAqD;AACnG,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SAASC,qBAAqB,EAAEC,wBAAwB,QAAQ,0BAAsB;AAEtF;;;;;;;;;;CAUC,GACD,OAAO,SAASC;IACdJ,8BAA8BG,0BAA0BD,uBAAuB;QAAEG,UAAUJ;IAAU;AACvG"}
@@ -19,7 +19,10 @@ import { registerHostEventListener } from "@aglyn/tenant-runtime/host-event-list
19
19
  import { registerVariableComputer } from "@aglyn/aglyn/plugin-manager/computed-variables";
20
20
  import { registerPluginDependentsSource } from "@aglyn/aglyn/plugin-manager/plugin-dependents";
21
21
  import { registerPluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
22
+ import { registerPluginTransferResource } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
22
23
  import { BUNDLE_ID } from "./constants/bundle-common.js";
24
+ import { ORG_AUTOMATION_PACKAGE_RULES, ORG_AUTOMATIONS_TRANSFER_KEY, orgAutomationDependencies, rawOrgAutomationContent, remapOrgAutomationIds } from "./transfer/org-automations-package.js";
25
+ /** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */ const automationsPackage = async ()=>(await import("./transfer/org-automations-package.server.js")).createOrgAutomationsPackage();
23
26
  /**
24
27
  * The automation engine, as the runtime hears it.
25
28
  *
@@ -68,6 +71,20 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
68
71
  * whose boot did not run it still registers the listener the first time a
69
72
  * plugin door loads. Registering twice replaces in place.
70
73
  */ export function registerWorkflowsServerDeclarations() {
74
+ // Org automations in a workspace package (AGL-3535).
75
+ registerPluginTransferResource(ORG_AUTOMATIONS_TRANSFER_KEY, {
76
+ items: async (ctx)=>(await automationsPackage()).items(ctx),
77
+ dependencies: (item)=>orgAutomationDependencies(rawOrgAutomationContent(item)),
78
+ remapIds: (item, idMap)=>remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),
79
+ readItems: async (ctx, ids)=>(await automationsPackage()).readItems(ctx, ids),
80
+ writeItems: async (ctx, items, writer)=>(await automationsPackage()).writeItems(ctx, items, writer),
81
+ revertItems: async (ctx, steps)=>(await automationsPackage()).revertItems(ctx, steps),
82
+ problems: async (ctx, write)=>(await automationsPackage()).problems(ctx, write),
83
+ referenceTargets: async (ctx, kinds)=>(await automationsPackage()).referenceTargets(ctx, kinds),
84
+ rules: ORG_AUTOMATION_PACKAGE_RULES
85
+ }, {
86
+ pluginId: BUNDLE_ID
87
+ });
71
88
  registerHostEventListener(BUNDLE_ID, workflowsHostEventListener);
72
89
  // The readers and the Admin SDK arrive with the first read, not with the boot.
73
90
  registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","BUNDLE_ID","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","lazyIndex","pluginId","kinds","find","request","findFunctionDependents","prepare","prepareWorkflowVariables","name","load","list","get"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,2EAA2E;AAC3E,oEAAoE;AACpE,SAEEA,yBAAyB,QACpB,6CAA4C;AACnD,SAASC,wBAAwB,QAAQ,iDAAgD;AACzF,SAASC,8BAA8B,QAAQ,gDAA+C;AAC9F,SACEC,yBAAyB,QAEpB,kDAAiD;AACxD,SAASC,SAAS,QAAQ,+BAA2B;AAErD;;;;;;CAMC,GACD,OAAO,MAAMC,6BAAgD;IAC3D,MAAMC,SAAQC,MAAM,EAAEC,KAAK,EAAEC,OAAO,EAAEC,OAAO;QAC3C,MAAM,CAAC,EAAEC,mBAAmB,EAAE,EAAE,EAAEC,mBAAmB,EAAE,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAC3E,MAAM,CAAC;YACP,MAAM,CAAC;SACR;QACD,gEAAgE;QAChE,OAAO,MAAMF,oBAAoBF,2BAAAA,QAASK,KAAK,EAAE,IAC/CJ,oBAAoBJ,QAAQC,OAAOC;IAEvC;IACA,MAAMO,YAAWT,MAAM,EAAEU,YAAY,EAAET,KAAK,EAAEC,OAAO,EAAEC,OAAO;;QAC5D,MAAM,CAAC,EAAEQ,eAAe,EAAE,EAAE,EAAEN,mBAAmB,EAAE,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YACvE,MAAM,CAAC;YACP,MAAM,CAAC;SACR;QACD,qEAAqE;QACrE,OAAO,MAAMF,4BAAoBF,2BAAAA,QAASK,KAAK,mBAAI;YAAEI,MAAM;QAAU,GAAG,IACtED,gBAAgBX,QAAQU,cAAcT,OAAOC;IAEjD;AACF,EAAC;AAED;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASW;IACdpB,0BAA0BI,WAAWC;IACrC,+EAA+E;IAC/EF,0BAA0B,YAAYkB,UAAU,wBAAwB;QAAEC,UAAUlB;IAAU;IAC9FD,0BAA0B,WAAWkB,UAAU,uBAAuB;QAAEC,UAAUlB;IAAU;IAC5FD,0BAA0B,UAAUkB,UAAU,sBAAsB;QAAEC,UAAUlB;IAAU;IAC1FF,+BACE;QACEqB,OAAO;YAAC;SAAW;QACnBC,MAAM,OAAOC,UACX,AAAC,CAAA,MAAM,MAAM,CAAC,kCAA8B,EAAGC,sBAAsB,CAACD;IAC1E,GACA;QAAEH,UAAUlB;IAAU;IAExBH,yBACE;QACE0B,SAAS,OAAOpB,SACd,AAAC,CAAA,MAAM,MAAM,CAAC,iCAA6B,EAAGqB,wBAAwB,CAACrB;IAC3E,GACA;QAAEe,UAAUlB;IAAU;AAE1B;AAIA,SAASiB,UAAUQ,IAAe;IAChC,MAAMC,OAAO,UAAY,AAAC,CAAA,MAAM,MAAM,CAAC,sCAAkC,CAAE,CAACD,KAAK;IACjF,OAAO;QACLE,MAAM,OAAON,UAAY,AAAC,CAAA,MAAMK,MAAK,EAAGC,IAAI,CAACN;QAC7CO,KAAK,OAAOP,UAAY,AAAC,CAAA,MAAMK,MAAK,EAAGE,GAAG,CAACP;IAC7C;AACF"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/declarations.server.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// The registry from its own module, not the runtime barrel: boot needs the\n// registry and nothing else, and the barrel reaches the data layer.\nimport {\n type HostEventListener,\n registerHostEventListener,\n} from '@aglyn/tenant-runtime/host-event-listeners'\nimport { registerVariableComputer } from '@aglyn/aglyn/plugin-manager/computed-variables'\nimport { registerPluginDependentsSource } from '@aglyn/aglyn/plugin-manager/plugin-dependents'\nimport {\n registerPluginRecordIndex,\n type PluginRecordIndex,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-index'\nimport { registerPluginTransferResource } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport {\n ORG_AUTOMATION_PACKAGE_RULES,\n ORG_AUTOMATIONS_TRANSFER_KEY,\n orgAutomationDependencies,\n rawOrgAutomationContent,\n remapOrgAutomationIds,\n} from './transfer/org-automations-package'\n\n/** The org automations package's server half (AGL-3535), loaded when an import or export first asks. */\nconst automationsPackage = async () =>\n (await import('./transfer/org-automations-package.server')).createOrgAutomationsPackage()\n\n/**\n * The automation engine, as the runtime hears it.\n *\n * Light by construction: the engine is imported when the first event\n * arrives, not when the process starts, so a process that never sees an\n * event pays for this object and nothing else.\n */\nexport const workflowsHostEventListener: HostEventListener = {\n async onEvent(hostId, event, payload, context) {\n const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-automations'),\n import('./engine/run-trigger-actor'),\n ])\n // Every run this event causes records who caused it (AGL-3376).\n return await withRunTriggerActor(context?.actor, () =>\n runEventAutomations(hostId, event, payload),\n )\n },\n async onDispatch(hostId, automationId, event, payload, context) {\n const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([\n import('./engine/run-event-actions'),\n import('./engine/run-trigger-actor'),\n ])\n // A page-fired automation is always a visitor on the published page.\n return await withRunTriggerActor(context?.actor ?? { kind: 'visitor' }, () =>\n runSingleAction(hostId, automationId, event, payload),\n )\n },\n}\n\n/**\n * The plugin's SERVER declarations: what the server must know at boot,\n * before any door of the plugin has been called.\n *\n * The engine's host-event listener, because the doors that raise the events\n * mostly never load this plugin. A form submission and a page view are core\n * routes; a contact captured by a Stripe webhook is a core capture door. Each\n * calls `emitHostEvent`, and the listener has to be registered before the\n * first of them arrives — which is what running this from both apps'\n * `instrumentation.ts` gives.\n *\n * The indexes of the site's workflows, webhooks and actions, so another\n * surface — an AI job — reads them without reaching for this plugin's\n * collections; the workflows that call a function, which the \"Used by\"\n * scan asks this plugin for; and the site variables a workflow computes,\n * which every published page's compose asks for.\n *\n * Also called from the plugin's own API register functions, so a process\n * whose boot did not run it still registers the listener the first time a\n * plugin door loads. Registering twice replaces in place.\n */\nexport function registerWorkflowsServerDeclarations(): void {\n // Org automations in a workspace package (AGL-3535).\n registerPluginTransferResource(\n ORG_AUTOMATIONS_TRANSFER_KEY,\n {\n items: async (ctx) => (await automationsPackage()).items(ctx),\n dependencies: (item) => orgAutomationDependencies(rawOrgAutomationContent(item)),\n remapIds: (item, idMap) => remapOrgAutomationIds(rawOrgAutomationContent(item), idMap),\n readItems: async (ctx, ids) => (await automationsPackage()).readItems(ctx, ids),\n writeItems: async (ctx, items, writer) => (await automationsPackage()).writeItems(ctx, items as never, writer),\n revertItems: async (ctx, steps) => (await automationsPackage()).revertItems(ctx, steps as never),\n problems: async (ctx, write) => (await automationsPackage()).problems(ctx, write as never),\n referenceTargets: async (ctx, kinds) => (await automationsPackage()).referenceTargets(ctx, kinds),\n rules: ORG_AUTOMATION_PACKAGE_RULES,\n },\n { pluginId: BUNDLE_ID },\n )\n registerHostEventListener(BUNDLE_ID, workflowsHostEventListener)\n // The readers and the Admin SDK arrive with the first read, not with the boot.\n registerPluginRecordIndex('workflow', lazyIndex('workflowRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('webhook', lazyIndex('webhookRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginRecordIndex('action', lazyIndex('actionRecordIndex'), { pluginId: BUNDLE_ID })\n registerPluginDependentsSource(\n {\n kinds: ['function'],\n find: async (request) =>\n (await import('./server/workflow-dependents')).findFunctionDependents(request),\n },\n { pluginId: BUNDLE_ID },\n )\n registerVariableComputer(\n {\n prepare: async (hostId) =>\n (await import('./server/workflow-variables')).prepareWorkflowVariables(hostId),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\ntype IndexName = 'workflowRecordIndex' | 'webhookRecordIndex' | 'actionRecordIndex'\n\nfunction lazyIndex(name: IndexName): PluginRecordIndex {\n const load = async () => (await import('./server/automation-record-index'))[name]\n return {\n list: async (request) => (await load()).list(request),\n get: async (request) => (await load()).get(request),\n }\n}\n"],"names":["registerHostEventListener","registerVariableComputer","registerPluginDependentsSource","registerPluginRecordIndex","registerPluginTransferResource","BUNDLE_ID","ORG_AUTOMATION_PACKAGE_RULES","ORG_AUTOMATIONS_TRANSFER_KEY","orgAutomationDependencies","rawOrgAutomationContent","remapOrgAutomationIds","automationsPackage","createOrgAutomationsPackage","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations","items","ctx","dependencies","item","remapIds","idMap","readItems","ids","writeItems","writer","revertItems","steps","problems","write","referenceTargets","kinds","rules","pluginId","lazyIndex","find","request","findFunctionDependents","prepare","prepareWorkflowVariables","name","load","list","get"],"mappings":"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"}
@@ -0,0 +1,69 @@
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 CrmMergeContext, type CrmMergeResult } from '@aglyn/aglyn/app-utils/crm-email-templates';
18
+ /**
19
+ * The person a step's email is about, as the payload names them when no
20
+ * record does: the `name` and `email` the event carried.
21
+ */
22
+ export interface EmailMergePerson {
23
+ name?: string | null;
24
+ email?: string | null;
25
+ }
26
+ /**
27
+ * A `sendEmail` step's subject or body with its merge tags filled from the
28
+ * contact or the lead the run is about (AGL-3458), so a reply greets the
29
+ * person by name rather than as `{{firstName|there}}`.
30
+ *
31
+ * Both spellings a merchant has been shown resolve, because a workflow email
32
+ * is written by the same people who write the other two:
33
+ *
34
+ * - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,
35
+ * `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's
36
+ * resolver, run over the record the run resolved;
37
+ * - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,
38
+ * `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run
39
+ * over the same person: the contact's name as this site knows it, else
40
+ * the lead's, else the name the event carried.
41
+ *
42
+ * Both honor `|fallback`, and an unknown tag prints its fallback or nothing,
43
+ * never its braces — a typo must not reach a customer as `{{frstName}}`.
44
+ * Pure; the caller hands over what it read.
45
+ */
46
+ export declare function resolveStepEmailMerge(text: string, context: CrmMergeContext, person?: EmailMergePerson | null): CrmMergeResult;
47
+ /**
48
+ * The records a step's email is about, for its merge tags: the person the
49
+ * event names, as the plugin that keeps people holds them — a contact, else
50
+ * the lead a lead-routed form filed — and the site's name, in the shape the
51
+ * one-to-one email's resolver takes (`resolveStepEmailMerge`).
52
+ *
53
+ * Asked through the person-records seam (`findPluginPerson`), narrowed to
54
+ * what this site may see, so the engine opens no record system's storage. A
55
+ * workspace with no record system, or a person nobody holds yet, leaves both
56
+ * records out, and the tags fall back to the name and address the event
57
+ * carried.
58
+ *
59
+ * **Never throws.** A lookup that fails is an email with its fallbacks, never
60
+ * an email that did not leave.
61
+ */
62
+ export declare function stepEmailMergeContext(input: {
63
+ hostId: string;
64
+ /** The address of the person the event is about. */
65
+ email: unknown;
66
+ /** The holder group whose facet names a contact: the sending site's. */
67
+ contactGroupId: string;
68
+ siteName: string;
69
+ }): Promise<CrmMergeContext>;
@@ -0,0 +1,98 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { normalizeContactEmail } from "@aglyn/aglyn/app-utils/contacts";
18
+ import { resolveCrmMergeFields } from "@aglyn/aglyn/app-utils/crm-email-templates";
19
+ import { findPluginPerson } from "@aglyn/aglyn/plugin-manager/plugin-person-records";
20
+ import { resolveMergeTags } from "@aglyn/shared-util-email/email-merge";
21
+ /** One CRM field's value, through the one-to-one email's own resolver. */ const field = (key, context)=>resolveCrmMergeFields(`{{${key}}}`, context).text.trim();
22
+ /**
23
+ * A `sendEmail` step's subject or body with its merge tags filled from the
24
+ * contact or the lead the run is about (AGL-3458), so a reply greets the
25
+ * person by name rather than as `{{firstName|there}}`.
26
+ *
27
+ * Both spellings a merchant has been shown resolve, because a workflow email
28
+ * is written by the same people who write the other two:
29
+ *
30
+ * - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,
31
+ * `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's
32
+ * resolver, run over the record the run resolved;
33
+ * - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,
34
+ * `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run
35
+ * over the same person: the contact's name as this site knows it, else
36
+ * the lead's, else the name the event carried.
37
+ *
38
+ * Both honor `|fallback`, and an unknown tag prints its fallback or nothing,
39
+ * never its braces — a typo must not reach a customer as `{{frstName}}`.
40
+ * Pure; the caller hands over what it read.
41
+ */ export function resolveStepEmailMerge(text, context, person = null) {
42
+ var _normalizeContactEmail;
43
+ const crm = resolveCrmMergeFields(text, context);
44
+ const name = field('contact.name', context) || field('lead.name', context) || (typeof (person == null ? void 0 : person.name) === 'string' ? person.name.trim() : '');
45
+ const email = field('contact.email', context) || field('lead.email', context) || ((_normalizeContactEmail = normalizeContactEmail(person == null ? void 0 : person.email)) != null ? _normalizeContactEmail : '');
46
+ return {
47
+ text: resolveMergeTags(crm.text, _extends({
48
+ email
49
+ }, name ? {
50
+ name
51
+ } : {})),
52
+ unresolved: crm.unresolved
53
+ };
54
+ }
55
+ /**
56
+ * The records a step's email is about, for its merge tags: the person the
57
+ * event names, as the plugin that keeps people holds them — a contact, else
58
+ * the lead a lead-routed form filed — and the site's name, in the shape the
59
+ * one-to-one email's resolver takes (`resolveStepEmailMerge`).
60
+ *
61
+ * Asked through the person-records seam (`findPluginPerson`), narrowed to
62
+ * what this site may see, so the engine opens no record system's storage. A
63
+ * workspace with no record system, or a person nobody holds yet, leaves both
64
+ * records out, and the tags fall back to the name and address the event
65
+ * carried.
66
+ *
67
+ * **Never throws.** A lookup that fails is an email with its fallbacks, never
68
+ * an email that did not leave.
69
+ */ export async function stepEmailMergeContext(input) {
70
+ const context = {
71
+ site: {
72
+ name: input.siteName
73
+ }
74
+ };
75
+ try {
76
+ const person = await findPluginPerson({
77
+ hostId: input.hostId,
78
+ email: input.email,
79
+ onlyVisibleToSite: true,
80
+ anyKind: true
81
+ });
82
+ const data = person ? _extends({}, person.data) : null;
83
+ if ((person == null ? void 0 : person.kind) === 'contact') {
84
+ return _extends({}, context, {
85
+ contact: data,
86
+ contactGroupId: input.contactGroupId
87
+ });
88
+ }
89
+ if ((person == null ? void 0 : person.kind) === 'lead') return _extends({}, context, {
90
+ lead: data
91
+ });
92
+ } catch (error) {
93
+ console.error('[workflow] email merge context could not be read', input.hostId, error);
94
+ }
95
+ return context;
96
+ }
97
+
98
+ //# sourceMappingURL=email-merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/engine/email-merge.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 { normalizeContactEmail } from '@aglyn/aglyn/app-utils/contacts'\nimport {\n type CrmMergeContext,\n type CrmMergeResult,\n resolveCrmMergeFields,\n} from '@aglyn/aglyn/app-utils/crm-email-templates'\nimport { findPluginPerson } from '@aglyn/aglyn/plugin-manager/plugin-person-records'\nimport { resolveMergeTags } from '@aglyn/shared-util-email/email-merge'\n\n/**\n * The person a step's email is about, as the payload names them when no\n * record does: the `name` and `email` the event carried.\n */\nexport interface EmailMergePerson {\n name?: string | null\n email?: string | null\n}\n\n/** One CRM field's value, through the one-to-one email's own resolver. */\nconst field = (key: string, context: CrmMergeContext): string =>\n resolveCrmMergeFields(`{{${key}}}`, context).text.trim()\n\n/**\n * A `sendEmail` step's subject or body with its merge tags filled from the\n * contact or the lead the run is about (AGL-3458), so a reply greets the\n * person by name rather than as `{{firstName|there}}`.\n *\n * Both spellings a merchant has been shown resolve, because a workflow email\n * is written by the same people who write the other two:\n *\n * - the CRM's fields, `{{contact.firstName}}`, `{{lead.company}}`,\n * `{{site.name}}` — `resolveCrmMergeFields`, the one-to-one email's\n * resolver, run over the record the run resolved;\n * - the campaign's short tags, `{{firstName|there}}`, `{{name}}`,\n * `{{email}}` — `resolveMergeTags`, the campaign sender's own resolver, run\n * over the same person: the contact's name as this site knows it, else\n * the lead's, else the name the event carried.\n *\n * Both honor `|fallback`, and an unknown tag prints its fallback or nothing,\n * never its braces — a typo must not reach a customer as `{{frstName}}`.\n * Pure; the caller hands over what it read.\n */\nexport function resolveStepEmailMerge(\n text: string,\n context: CrmMergeContext,\n person: EmailMergePerson | null = null,\n): CrmMergeResult {\n const crm = resolveCrmMergeFields(text, context)\n const name =\n field('contact.name', context) ||\n field('lead.name', context) ||\n (typeof person?.name === 'string' ? person.name.trim() : '')\n const email =\n field('contact.email', context) ||\n field('lead.email', context) ||\n (normalizeContactEmail(person?.email) ?? '')\n return {\n text: resolveMergeTags(crm.text, { email, ...(name ? { name } : {}) }),\n unresolved: crm.unresolved,\n }\n}\n\n/**\n * The records a step's email is about, for its merge tags: the person the\n * event names, as the plugin that keeps people holds them — a contact, else\n * the lead a lead-routed form filed — and the site's name, in the shape the\n * one-to-one email's resolver takes (`resolveStepEmailMerge`).\n *\n * Asked through the person-records seam (`findPluginPerson`), narrowed to\n * what this site may see, so the engine opens no record system's storage. A\n * workspace with no record system, or a person nobody holds yet, leaves both\n * records out, and the tags fall back to the name and address the event\n * carried.\n *\n * **Never throws.** A lookup that fails is an email with its fallbacks, never\n * an email that did not leave.\n */\nexport async function stepEmailMergeContext(input: {\n hostId: string\n /** The address of the person the event is about. */\n email: unknown\n /** The holder group whose facet names a contact: the sending site's. */\n contactGroupId: string\n siteName: string\n}): Promise<CrmMergeContext> {\n const context: CrmMergeContext = { site: { name: input.siteName } }\n try {\n const person = await findPluginPerson({\n hostId: input.hostId,\n email: input.email,\n onlyVisibleToSite: true,\n anyKind: true,\n })\n const data = person ? { ...person.data } : null\n if (person?.kind === 'contact') {\n return { ...context, contact: data, contactGroupId: input.contactGroupId }\n }\n if (person?.kind === 'lead') return { ...context, lead: data }\n } catch (error) {\n console.error('[workflow] email merge context could not be read', input.hostId, error)\n }\n return context\n}\n"],"names":["normalizeContactEmail","resolveCrmMergeFields","findPluginPerson","resolveMergeTags","field","key","context","text","trim","resolveStepEmailMerge","person","crm","name","email","unresolved","stepEmailMergeContext","input","site","siteName","hostId","onlyVisibleToSite","anyKind","data","kind","contact","contactGroupId","lead","error","console"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,qBAAqB,QAAQ,kCAAiC;AACvE,SAGEC,qBAAqB,QAChB,6CAA4C;AACnD,SAASC,gBAAgB,QAAQ,oDAAmD;AACpF,SAASC,gBAAgB,QAAQ,uCAAsC;AAWvE,wEAAwE,GACxE,MAAMC,QAAQ,CAACC,KAAaC,UAC1BL,sBAAsB,CAAC,EAAE,EAAEI,IAAI,EAAE,CAAC,EAAEC,SAASC,IAAI,CAACC,IAAI;AAExD;;;;;;;;;;;;;;;;;;;CAmBC,GACD,OAAO,SAASC,sBACdF,IAAY,EACZD,OAAwB,EACxBI,SAAkC,IAAI;QAUnCV;IARH,MAAMW,MAAMV,sBAAsBM,MAAMD;IACxC,MAAMM,OACJR,MAAM,gBAAgBE,YACtBF,MAAM,aAAaE,YAClB,CAAA,QAAOI,0BAAAA,OAAQE,IAAI,MAAK,WAAWF,OAAOE,IAAI,CAACJ,IAAI,KAAK,EAAC;IAC5D,MAAMK,QACJT,MAAM,iBAAiBE,YACvBF,MAAM,cAAcE,cACnBN,yBAAAA,sBAAsBU,0BAAAA,OAAQG,KAAK,aAAnCb,yBAAwC;IAC3C,OAAO;QACLO,MAAMJ,iBAAiBQ,IAAIJ,IAAI,EAAE;YAAEM;WAAWD,OAAO;YAAEA;QAAK,IAAI,CAAC;QACjEE,YAAYH,IAAIG,UAAU;IAC5B;AACF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,eAAeC,sBAAsBC,KAO3C;IACC,MAAMV,UAA2B;QAAEW,MAAM;YAAEL,MAAMI,MAAME,QAAQ;QAAC;IAAE;IAClE,IAAI;QACF,MAAMR,SAAS,MAAMR,iBAAiB;YACpCiB,QAAQH,MAAMG,MAAM;YACpBN,OAAOG,MAAMH,KAAK;YAClBO,mBAAmB;YACnBC,SAAS;QACX;QACA,MAAMC,OAAOZ,SAAS,aAAKA,OAAOY,IAAI,IAAK;QAC3C,IAAIZ,CAAAA,0BAAAA,OAAQa,IAAI,MAAK,WAAW;YAC9B,OAAO,aAAKjB;gBAASkB,SAASF;gBAAMG,gBAAgBT,MAAMS,cAAc;;QAC1E;QACA,IAAIf,CAAAA,0BAAAA,OAAQa,IAAI,MAAK,QAAQ,OAAO,aAAKjB;YAASoB,MAAMJ;;IAC1D,EAAE,OAAOK,OAAO;QACdC,QAAQD,KAAK,CAAC,oDAAoDX,MAAMG,MAAM,EAAEQ;IAClF;IACA,OAAOrB;AACT"}
@@ -14,11 +14,14 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { type HostAction, type HostActionAlert, type HostActionStep, type HostFunction, type HostVariable, type PluginJobHostGate } from '@aglyn/aglyn/server';
17
+ import { type HostFunction, type HostVariable, type PluginJobHostGate } from '@aglyn/aglyn/server';
18
18
  import { type HostWorkflow } from '../model/workflows';
19
19
  import { type FlowEnrollment, type FlowSweepCursor, type FlowSweepResult } from './flow-enrollments';
20
20
  import type { HostEventPayload } from '@aglyn/tenant-runtime/host-event-listeners';
21
21
  import { type AutomationWorkflow, type WorkflowStep } from './workflow-steps';
22
+ import { type HostAction, type HostActionAlert, type HostActionStep } from '../model/host-actions';
23
+ /** The live, switched-on actions a site holds for `event`, in document-id order. */
24
+ export declare function liveActionsForEvent(hostRef: FirebaseFirestore.DocumentReference, event: string): Promise<FirebaseFirestore.QueryDocumentSnapshot[]>;
22
25
  export interface ActionRunEnv {
23
26
  hostId: string;
24
27
  hostRef: FirebaseFirestore.DocumentReference;
@@ -32,20 +35,14 @@ export interface ActionRunEnv {
32
35
  */
33
36
  actionsAllowed: boolean;
34
37
  webhooksAllowed: boolean;
35
- /**
36
- * Whether the owning org holds the CRM suite (AGL-2611) — the gate the
37
- * five CRM steps take, resolved once beside `webhooksAllowed` and for the
38
- * same reason: a step gated on the org doc it already read costs no
39
- * second read per step.
40
- */
41
- crmAllowed: boolean;
42
38
  depth: number;
43
39
  /**
44
40
  * The owning org's billing doc, already read by the entitlement gate that
45
41
  * admitted this run, and the org's id beside it.
46
42
  *
47
43
  * Carried rather than re-read: both entry points resolve `getOrgForHost`
48
- * before they build this, so the dataset caps below cost no extra org read.
44
+ * before they build this, so a step that gates on the plan — another
45
+ * plugin's step, handed both — costs no extra org read.
49
46
  * Null only when the host has no resolvable org, which the gate treats as
50
47
  * the free plan.
51
48
  */
@@ -226,6 +223,34 @@ interface ExecuteWorkflowOptions {
226
223
  * both, once.
227
224
  */
228
225
  export declare function executeWorkflow(env: ActionRunEnv, run: AutomationRun, workflow: AutomationWorkflow, event: string, payload: HostEventPayload, options?: ExecuteWorkflowOptions): Promise<WorkflowExecution>;
226
+ /**
227
+ * Events too frequent to log a skip for.
228
+ *
229
+ * `runEventActions` fires on EVERY page view of every published site. A
230
+ * Firestore write per visitor per non-matching action is not a run
231
+ * history, it is an outage — and a page-view condition is one an author
232
+ * tunes by watching the site, not by reading a log. The events people
233
+ * actually debug are the server-emitted ones (a form submission, a
234
+ * booking, a sign-up), and those are low-volume by construction.
235
+ */
236
+ export declare const SKIP_LOG_EXCLUDED_EVENTS: ReadonlySet<string>;
237
+ /**
238
+ * Whether an action's unmet conditions only say it is ANOTHER FORM'S
239
+ * (AGL-3458) — the case that is noise rather than an answer.
240
+ *
241
+ * A site with one auto-reply per form keyed on `formId equals …` would write
242
+ * a `Skipped` row for every other form's reply on every submission: eight
243
+ * rows a fill on a nine-form site, burying the one row that explains a real
244
+ * miss. A form picked by id cannot be mistyped, so its mismatch against a
245
+ * submission that names a DIFFERENT form says only "not this form", and it
246
+ * is not recorded. Every other unmet condition still is: a hand-typed
247
+ * `formName`, a field condition, a submission that names no form at all.
248
+ *
249
+ * With `and`, one such clause is the whole answer; with `or`, only when every
250
+ * clause is about which form it was — `formId` or `formName` equals — since
251
+ * any other clause could have been the one that was meant to match.
252
+ */
253
+ export declare function conditionsNameAnotherForm(trigger: HostAction['trigger'] | null | undefined, payload: HostEventPayload): boolean;
229
254
  /**
230
255
  * Event-triggered action runner (AGL-148): loads enabled actions whose
231
256
  * `trigger.event` matches (built-in, site event, or custom), evaluates
@@ -249,7 +274,7 @@ export declare function runEventActions(hostId: string, event: string, payload?:
249
274
  export type SingleActionSkip = 'missing' | 'disabled' | 'event' | 'conditions' | 'plan' | 'allowance' | 'failed';
250
275
  /** What one dispatched action did, for a caller that has to say so. */
251
276
  export interface SingleActionOutcome {
252
- /** Its steps ran, and the run counted on the site's meter. */
277
+ /** Its steps ran, and the run counted on the site's and workspace's meters. */
253
278
  ran: boolean;
254
279
  /** Why nothing ran; `null` when it ran. */
255
280
  skipped: SingleActionSkip | null;