@aglyn/plugins-workflows 1.0.0-beta.204 → 1.0.0-beta.206

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aglyn/plugins-workflows",
3
- "version": "1.0.0-beta.204",
3
+ "version": "1.0.0-beta.206",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://aglyn.com",
6
6
  "repository": {
@@ -25,17 +25,17 @@
25
25
  "./package.json": "./package.json"
26
26
  },
27
27
  "dependencies": {
28
- "@aglyn/aglyn": "1.0.0-beta.204",
29
- "@aglyn/shared-data-mdi": "1.0.0-beta.204",
30
- "@aglyn/shared-ui-jsx": "1.0.0-beta.204",
31
- "@aglyn/shared-ui-next": "1.0.0-beta.204",
32
- "@aglyn/shared-ui-snackstack": "1.0.0-beta.204",
33
- "@aglyn/shared-util-email": "1.0.0-beta.204",
34
- "@aglyn/shared-util-http": "1.0.0-beta.204",
35
- "@aglyn/shared-util-timestamp": "1.0.0-beta.204",
36
- "@aglyn/tenant-data-admin": "1.0.0-beta.204",
37
- "@aglyn/tenant-feature-instance": "1.0.0-beta.204",
38
- "@aglyn/tenant-runtime": "1.0.0-beta.204",
28
+ "@aglyn/aglyn": "1.0.0-beta.206",
29
+ "@aglyn/shared-data-mdi": "1.0.0-beta.206",
30
+ "@aglyn/shared-ui-jsx": "1.0.0-beta.206",
31
+ "@aglyn/shared-ui-next": "1.0.0-beta.206",
32
+ "@aglyn/shared-ui-snackstack": "1.0.0-beta.206",
33
+ "@aglyn/shared-util-email": "1.0.0-beta.206",
34
+ "@aglyn/shared-util-http": "1.0.0-beta.206",
35
+ "@aglyn/shared-util-timestamp": "1.0.0-beta.206",
36
+ "@aglyn/tenant-data-admin": "1.0.0-beta.206",
37
+ "@aglyn/tenant-feature-instance": "1.0.0-beta.206",
38
+ "@aglyn/tenant-runtime": "1.0.0-beta.206",
39
39
  "@swc/helpers": "0.5.23"
40
40
  },
41
41
  "peerDependencies": {
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */ 'use client';
17
17
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
- import { actionRunResult, actionRunSummary, actionTriggerLabel } from "@aglyn/aglyn/app-utils/activity-presenter";
18
+ import { actionRunResult, actionRunSummary, actionTriggerLabel, runTriggeredByLabel } from "@aglyn/aglyn/app-utils/activity-presenter";
19
19
  import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
20
20
  import { HOST_EVENT_TYPES } from "@aglyn/aglyn/app-utils/workflows";
21
21
  import { CardDisplay } from "@aglyn/shared-ui-jsx";
@@ -132,6 +132,7 @@ const RUN_SELECT_FIELDS = Object.keys(RUN_FILTER_OPTIONS);
132
132
  trigger: String((_entry_trigger = entry.trigger) != null ? _entry_trigger : ''),
133
133
  // `formSubmission` → `Form submitted`.
134
134
  triggerLabel: actionTriggerLabel(entry.trigger),
135
+ who: runTriggeredByLabel(entry),
135
136
  summary: actionRunSummary(entry),
136
137
  createdAtMs: typeof ((_entry_createdAt = entry.createdAt) == null ? void 0 : _entry_createdAt.seconds) === 'number' ? entry.createdAt.seconds * 1000 : null
137
138
  };
@@ -167,6 +168,13 @@ const RUN_SELECT_FIELDS = Object.keys(RUN_FILTER_OPTIONS);
167
168
  width: 180,
168
169
  renderCell: ({ row })=>row.triggerLabel
169
170
  },
171
+ {
172
+ field: 'who',
173
+ headerName: 'Who',
174
+ description: 'Who set this run off: the person, visitor, key or system whose act fired the trigger.',
175
+ width: 200,
176
+ renderCell: ({ row })=>row.who
177
+ },
170
178
  {
171
179
  field: 'result',
172
180
  headerName: 'Result',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/host-run-history-card.component.tsx"],"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'use client'\n\nimport {\n actionRunResult,\n actionRunSummary,\n actionTriggerLabel,\n} from '@aglyn/aglyn/app-utils/activity-presenter'\nimport { useConsoleWidgetSlot } from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport { HOST_EVENT_TYPES } from '@aglyn/aglyn/app-utils/workflows'\nimport type { ConsoleAutomationTarget } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { CardDisplay, type HelpTipContent } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport ListQueryNotices, {\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { Chip, Stack, Tooltip, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport {\n RUN_HISTORY_HEADERS,\n RUN_HISTORY_QUERY,\n runHistoryBase,\n} from '../model/run-history'\n\nexport interface HostRunHistoryCardProps {\n hostId: string\n /** The org the page names, for what other plugins add to a failed run. */\n orgId?: string\n /** Show only runs of this action/workflow. */\n targetId?: string\n /**\n * What `targetId` names, and its name. A run row records its automation's id\n * but not whether it is an action or a workflow, so the card that opened the\n * history says; without it, a failed run offers nothing beside its summary.\n */\n targetType?: ConsoleAutomationTarget['type']\n targetName?: string\n header?: string\n /** Overrides the default help affordance on the card header. */\n help?: HelpTipContent\n}\n\nconst RESULT_COLOR = {\n succeeded: 'success',\n failed: 'error',\n skipped: 'warning',\n} as const\n\nconst RESULT_LABEL = {\n succeeded: 'Succeeded',\n failed: 'Failed',\n skipped: 'Skipped',\n} as const\n\ntype RunResult = keyof typeof RESULT_LABEL\n\n/** One run as the grid holds it: the entry, and what it is shown by. */\ninterface RunRow {\n $id: string\n entry: any\n result: RunResult\n trigger: string\n triggerLabel: string\n summary: string\n createdAtMs: number | null\n}\n\n/** Trigger and Result are picked: the event list and the three verdicts. */\nconst RUN_FILTER_OPTIONS = {\n trigger: HOST_EVENT_TYPES.map((event) => ({\n value: event,\n label: actionTriggerLabel(event),\n })),\n result: (Object.keys(RESULT_LABEL) as RunResult[]).map((result) => ({\n value: result,\n label: RESULT_LABEL[result],\n })),\n}\nconst RUN_SELECT_FIELDS = Object.keys(RUN_FILTER_OPTIONS)\n\n/**\n * The run-history table `/product/workflows` advertises (AGL-2171):\n * `Time | Trigger | Result | What happened`.\n *\n * A separate component from `HostActivityCard` on purpose. That card is\n * the site's general feed — publishes, media saves, member changes — and\n * a run is a different record with different columns. Pointing the Runs\n * dialog at the general feed is what produced a list of\n * `Action ran on formSubmission — My automation` where the mockup shows\n * four columns.\n *\n * EVERY FILTER IS ON THE QUERY (AGL-3321). The history is one Firestore\n * query over `hosts/{hostId}/activity` — this automation's entries, runs\n * only, newest first — with each Filters-panel clause and the search word\n * as another predicate on it (`RUN_HISTORY_QUERY`), and it pages by that\n * query. A filtered page is a page of matches from the whole history, not\n * the matches among entries already read; what one query cannot hold is\n * said above the grid and not applied.\n */\nexport function HostRunHistoryCard(props: HostRunHistoryCardProps) {\n const {\n hostId,\n orgId,\n targetId,\n targetType,\n targetName = '',\n header = 'Run history',\n help = pluginDocsHelp('buildAWorkflow', {\n anchor: '#4-save-and-test',\n excerpt:\n 'Every run of this automation, including the ones a trigger ' +\n 'condition skipped — which is the answer to \"why did it not fire?\".',\n }),\n } = props\n const firestore = useFirestore()\n /**\n * The shell's zone renderer (AGL-2919), for what other plugins add to a\n * failed run; `null` outside the console shell.\n */\n const RunZone = useConsoleWidgetSlot()\n /** The automation a failed run belongs to, as the `automationRun` zone names it. */\n const zoneTarget = useMemo<ConsoleAutomationTarget | null>(\n () =>\n targetId && targetType\n ? { type: targetType, id: targetId, name: targetName }\n : null,\n [targetId, targetType, targetName],\n )\n const gridFilter = useListGridFilter({ selectFields: RUN_SELECT_FIELDS })\n const activity = useMemo(\n () => (hostId ? collection(firestore, 'hosts', hostId, 'activity') : null),\n [firestore, hostId],\n )\n /*\n * `target.id` equality keeps the read proportional to the card — this\n * automation's rows, not the site's — and `result` keeps it to runs.\n * Both, and every clause, are served by the `activity` composites the\n * index file carries for `RUN_HISTORY_QUERY` beneath `createdAt` DESC.\n * A different workflow is a different history, and a new plan a new\n * query: the pager starts over on either.\n */\n const {\n rows: entries,\n status,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n } = useListQuery<any>({\n collection: activity,\n declaration: RUN_HISTORY_QUERY,\n request: {\n clauses: gridFilter.clauses,\n search: gridFilter.searchWords,\n base: runHistoryBase(targetId, gridFilter.clauses),\n },\n deps: [firestore, hostId, targetId],\n idField: '$id',\n })\n const filtering =\n gridFilter.clauses.length > 0 || gridFilter.searchWords.length > 0\n\n const runs = useMemo(\n () =>\n entries.map((entry): RunRow => ({\n $id: String(entry.$id),\n entry,\n // Every entry the query returns carries a verdict; the presenter\n // still reads it, so a row renders the way the feed names it.\n result: actionRunResult(entry) ?? 'succeeded',\n trigger: String(entry.trigger ?? ''),\n // `formSubmission` → `Form submitted`.\n triggerLabel: actionTriggerLabel(entry.trigger),\n summary: actionRunSummary(entry),\n createdAtMs:\n typeof entry.createdAt?.seconds === 'number'\n ? entry.createdAt.seconds * 1000\n : null,\n })),\n [entries],\n )\n const refused = useMemo(\n () =>\n listQueryRefusals(plan.refused, {\n fields: RUN_HISTORY_QUERY.fields,\n headers: RUN_HISTORY_HEADERS,\n options: RUN_FILTER_OPTIONS,\n }),\n [plan],\n )\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'createdAtMs',\n headerName: 'Time',\n width: 120,\n renderCell: ({ row }: { row: RunRow }) => {\n const at = row.entry.createdAt?.toDate?.()\n return (\n <Tooltip title={at ? at.toLocaleString() : ''}>\n <span>{at ? at.toLocaleTimeString() : '--'}</span>\n </Tooltip>\n )\n },\n },\n {\n field: 'trigger',\n headerName: 'Trigger',\n width: 180,\n renderCell: ({ row }: { row: RunRow }) => row.triggerLabel,\n },\n {\n field: 'result',\n headerName: 'Result',\n width: 120,\n renderCell: ({ row }: { row: RunRow }) => (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={RESULT_COLOR[row.result]}\n label={RESULT_LABEL[row.result]}\n />\n ),\n },\n {\n field: 'summary',\n headerName: 'What happened',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }: { row: RunRow }) => (\n <Stack sx={{ minWidth: 0, py: 0.75 }}>\n <Typography variant=\"body2\">{row.summary}</Typography>\n {row.entry.durationMs != null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${row.entry.durationMs}ms`}\n </Typography>\n ) : null}\n {/*\n What other plugins add to a failed run (AGL-2919): the\n `automationRun` zone, through the shell's gated slot.\n */}\n {RunZone && zoneTarget && row.result === 'failed' ? (\n <RunZone\n slot=\"automationRun\"\n hostId={hostId}\n orgId={orgId}\n target={zoneTarget}\n runId={row.$id}\n />\n ) : null}\n </Stack>\n ),\n },\n ] as GridColDef[],\n RUN_HISTORY_QUERY.fields,\n RUN_FILTER_OPTIONS,\n RUN_HISTORY_HEADERS,\n ),\n [RunZone, zoneTarget, hostId, orgId],\n )\n\n return (\n <CardDisplay\n header={header}\n help={help}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!filtering && page === 0 && runs.length === 0 && status === 'success' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No runs yet — every run of this automation is logged here, ' +\n 'including the ones a condition skipped.'}\n </Typography>\n ) : (\n <Stack spacing={1.5}>\n <ListFilterChips\n fields={RUN_HISTORY_QUERY.fields}\n headers={RUN_HISTORY_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={RUN_FILTER_OPTIONS}\n />\n <ListQueryNotices refused={refused} notices={plan.notices} />\n <ListTable\n aria-label=\"Run history\"\n rows={runs}\n columns={columns}\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n // Newest first is the query's one order.\n sortingMode=\"server\"\n disableColumnSorting\n // A failed run carries what other plugins add under its summary.\n getRowHeight={() => 'auto'}\n // `ListPagination` below pages the query.\n hideFooter\n noRowsLabel=\"No runs match these filters\"\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={runs.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nHostRunHistoryCard.displayName = 'HostRunHistoryCard'\n\nexport default HostRunHistoryCard\n"],"names":["actionRunResult","actionRunSummary","actionTriggerLabel","useConsoleWidgetSlot","HOST_EVENT_TYPES","CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","listFilterGridColumns","useListGridFilter","Chip","Stack","Tooltip","Typography","collection","useMemo","useFirestore","useListQuery","pluginDocsHelp","RUN_HISTORY_HEADERS","RUN_HISTORY_QUERY","runHistoryBase","RESULT_COLOR","succeeded","failed","skipped","RESULT_LABEL","RUN_FILTER_OPTIONS","trigger","map","event","value","label","result","Object","keys","RUN_SELECT_FIELDS","HostRunHistoryCard","props","hostId","orgId","targetId","targetType","targetName","header","help","anchor","excerpt","firestore","RunZone","zoneTarget","type","id","name","gridFilter","selectFields","activity","rows","entries","status","hasMore","page","setPage","pageSize","setPageSize","plan","declaration","request","clauses","search","searchWords","base","deps","idField","filtering","length","runs","entry","$id","String","triggerLabel","summary","createdAtMs","createdAt","seconds","refused","fields","headers","options","columns","field","headerName","width","renderCell","row","at","toDate","title","toLocaleString","span","toLocaleTimeString","size","variant","color","flex","minWidth","sx","py","durationMs","slot","target","runId","contentGutterX","contentGutterY","contentBordered","spacing","onChange","setClauses","notices","aria-label","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","getRowHeight","hideFooter","noRowsLabel","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/host-run-history-card.component.tsx"],"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'use client'\n\nimport {\n actionRunResult,\n actionRunSummary,\n actionTriggerLabel,\n runTriggeredByLabel,\n} from '@aglyn/aglyn/app-utils/activity-presenter'\nimport { useConsoleWidgetSlot } from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport { HOST_EVENT_TYPES } from '@aglyn/aglyn/app-utils/workflows'\nimport type { ConsoleAutomationTarget } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { CardDisplay, type HelpTipContent } from '@aglyn/shared-ui-jsx'\nimport ListFilterChips from '@aglyn/shared-ui-jsx/components/list-filter-chips.component'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport ListQueryNotices, {\n listQueryRefusals,\n} from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport { ListTable } from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { listFilterGridColumns } from '@aglyn/shared-ui-jsx/const/list-grid-filter'\nimport { useListGridFilter } from '@aglyn/shared-ui-jsx/hooks/use-list-grid-filter'\nimport { Chip, Stack, Tooltip, Typography } from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { collection } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport { useListQuery } from '@aglyn/tenant-feature-instance/hooks/use-list-query'\nimport { pluginDocsHelp } from '@aglyn/aglyn'\nimport {\n RUN_HISTORY_HEADERS,\n RUN_HISTORY_QUERY,\n runHistoryBase,\n} from '../model/run-history'\n\nexport interface HostRunHistoryCardProps {\n hostId: string\n /** The org the page names, for what other plugins add to a failed run. */\n orgId?: string\n /** Show only runs of this action/workflow. */\n targetId?: string\n /**\n * What `targetId` names, and its name. A run row records its automation's id\n * but not whether it is an action or a workflow, so the card that opened the\n * history says; without it, a failed run offers nothing beside its summary.\n */\n targetType?: ConsoleAutomationTarget['type']\n targetName?: string\n header?: string\n /** Overrides the default help affordance on the card header. */\n help?: HelpTipContent\n}\n\nconst RESULT_COLOR = {\n succeeded: 'success',\n failed: 'error',\n skipped: 'warning',\n} as const\n\nconst RESULT_LABEL = {\n succeeded: 'Succeeded',\n failed: 'Failed',\n skipped: 'Skipped',\n} as const\n\ntype RunResult = keyof typeof RESULT_LABEL\n\n/** One run as the grid holds it: the entry, and what it is shown by. */\ninterface RunRow {\n $id: string\n entry: any\n result: RunResult\n trigger: string\n triggerLabel: string\n /** Who set the run off (AGL-3376). */\n who: string\n summary: string\n createdAtMs: number | null\n}\n\n/** Trigger and Result are picked: the event list and the three verdicts. */\nconst RUN_FILTER_OPTIONS = {\n trigger: HOST_EVENT_TYPES.map((event) => ({\n value: event,\n label: actionTriggerLabel(event),\n })),\n result: (Object.keys(RESULT_LABEL) as RunResult[]).map((result) => ({\n value: result,\n label: RESULT_LABEL[result],\n })),\n}\nconst RUN_SELECT_FIELDS = Object.keys(RUN_FILTER_OPTIONS)\n\n/**\n * The run-history table `/product/workflows` advertises (AGL-2171):\n * `Time | Trigger | Result | What happened`.\n *\n * A separate component from `HostActivityCard` on purpose. That card is\n * the site's general feed — publishes, media saves, member changes — and\n * a run is a different record with different columns. Pointing the Runs\n * dialog at the general feed is what produced a list of\n * `Action ran on formSubmission — My automation` where the mockup shows\n * four columns.\n *\n * EVERY FILTER IS ON THE QUERY (AGL-3321). The history is one Firestore\n * query over `hosts/{hostId}/activity` — this automation's entries, runs\n * only, newest first — with each Filters-panel clause and the search word\n * as another predicate on it (`RUN_HISTORY_QUERY`), and it pages by that\n * query. A filtered page is a page of matches from the whole history, not\n * the matches among entries already read; what one query cannot hold is\n * said above the grid and not applied.\n */\nexport function HostRunHistoryCard(props: HostRunHistoryCardProps) {\n const {\n hostId,\n orgId,\n targetId,\n targetType,\n targetName = '',\n header = 'Run history',\n help = pluginDocsHelp('buildAWorkflow', {\n anchor: '#4-save-and-test',\n excerpt:\n 'Every run of this automation, including the ones a trigger ' +\n 'condition skipped — which is the answer to \"why did it not fire?\".',\n }),\n } = props\n const firestore = useFirestore()\n /**\n * The shell's zone renderer (AGL-2919), for what other plugins add to a\n * failed run; `null` outside the console shell.\n */\n const RunZone = useConsoleWidgetSlot()\n /** The automation a failed run belongs to, as the `automationRun` zone names it. */\n const zoneTarget = useMemo<ConsoleAutomationTarget | null>(\n () =>\n targetId && targetType\n ? { type: targetType, id: targetId, name: targetName }\n : null,\n [targetId, targetType, targetName],\n )\n const gridFilter = useListGridFilter({ selectFields: RUN_SELECT_FIELDS })\n const activity = useMemo(\n () => (hostId ? collection(firestore, 'hosts', hostId, 'activity') : null),\n [firestore, hostId],\n )\n /*\n * `target.id` equality keeps the read proportional to the card — this\n * automation's rows, not the site's — and `result` keeps it to runs.\n * Both, and every clause, are served by the `activity` composites the\n * index file carries for `RUN_HISTORY_QUERY` beneath `createdAt` DESC.\n * A different workflow is a different history, and a new plan a new\n * query: the pager starts over on either.\n */\n const {\n rows: entries,\n status,\n hasMore,\n page,\n setPage,\n pageSize,\n setPageSize,\n plan,\n } = useListQuery<any>({\n collection: activity,\n declaration: RUN_HISTORY_QUERY,\n request: {\n clauses: gridFilter.clauses,\n search: gridFilter.searchWords,\n base: runHistoryBase(targetId, gridFilter.clauses),\n },\n deps: [firestore, hostId, targetId],\n idField: '$id',\n })\n const filtering =\n gridFilter.clauses.length > 0 || gridFilter.searchWords.length > 0\n\n const runs = useMemo(\n () =>\n entries.map((entry): RunRow => ({\n $id: String(entry.$id),\n entry,\n // Every entry the query returns carries a verdict; the presenter\n // still reads it, so a row renders the way the feed names it.\n result: actionRunResult(entry) ?? 'succeeded',\n trigger: String(entry.trigger ?? ''),\n // `formSubmission` → `Form submitted`.\n triggerLabel: actionTriggerLabel(entry.trigger),\n who: runTriggeredByLabel(entry),\n summary: actionRunSummary(entry),\n createdAtMs:\n typeof entry.createdAt?.seconds === 'number'\n ? entry.createdAt.seconds * 1000\n : null,\n })),\n [entries],\n )\n const refused = useMemo(\n () =>\n listQueryRefusals(plan.refused, {\n fields: RUN_HISTORY_QUERY.fields,\n headers: RUN_HISTORY_HEADERS,\n options: RUN_FILTER_OPTIONS,\n }),\n [plan],\n )\n\n const columns = useMemo(\n () =>\n listFilterGridColumns(\n [\n {\n field: 'createdAtMs',\n headerName: 'Time',\n width: 120,\n renderCell: ({ row }: { row: RunRow }) => {\n const at = row.entry.createdAt?.toDate?.()\n return (\n <Tooltip title={at ? at.toLocaleString() : ''}>\n <span>{at ? at.toLocaleTimeString() : '--'}</span>\n </Tooltip>\n )\n },\n },\n {\n field: 'trigger',\n headerName: 'Trigger',\n width: 180,\n renderCell: ({ row }: { row: RunRow }) => row.triggerLabel,\n },\n {\n field: 'who',\n headerName: 'Who',\n description: 'Who set this run off: the person, visitor, key or system whose act fired the trigger.',\n width: 200,\n renderCell: ({ row }: { row: RunRow }) => row.who,\n },\n {\n field: 'result',\n headerName: 'Result',\n width: 120,\n renderCell: ({ row }: { row: RunRow }) => (\n <Chip\n size=\"small\"\n variant=\"outlined\"\n color={RESULT_COLOR[row.result]}\n label={RESULT_LABEL[row.result]}\n />\n ),\n },\n {\n field: 'summary',\n headerName: 'What happened',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }: { row: RunRow }) => (\n <Stack sx={{ minWidth: 0, py: 0.75 }}>\n <Typography variant=\"body2\">{row.summary}</Typography>\n {row.entry.durationMs != null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${row.entry.durationMs}ms`}\n </Typography>\n ) : null}\n {/*\n What other plugins add to a failed run (AGL-2919): the\n `automationRun` zone, through the shell's gated slot.\n */}\n {RunZone && zoneTarget && row.result === 'failed' ? (\n <RunZone\n slot=\"automationRun\"\n hostId={hostId}\n orgId={orgId}\n target={zoneTarget}\n runId={row.$id}\n />\n ) : null}\n </Stack>\n ),\n },\n ] as GridColDef[],\n RUN_HISTORY_QUERY.fields,\n RUN_FILTER_OPTIONS,\n RUN_HISTORY_HEADERS,\n ),\n [RunZone, zoneTarget, hostId, orgId],\n )\n\n return (\n <CardDisplay\n header={header}\n help={help}\n contentGutterX\n contentGutterY\n contentBordered=\"all\"\n >\n {!filtering && page === 0 && runs.length === 0 && status === 'success' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No runs yet — every run of this automation is logged here, ' +\n 'including the ones a condition skipped.'}\n </Typography>\n ) : (\n <Stack spacing={1.5}>\n <ListFilterChips\n fields={RUN_HISTORY_QUERY.fields}\n headers={RUN_HISTORY_HEADERS}\n clauses={gridFilter.clauses}\n onChange={gridFilter.setClauses}\n options={RUN_FILTER_OPTIONS}\n />\n <ListQueryNotices refused={refused} notices={plan.notices} />\n <ListTable\n aria-label=\"Run history\"\n rows={runs}\n columns={columns}\n filterMode=\"server\"\n filterModel={gridFilter.filterModel}\n onFilterModelChange={gridFilter.onFilterModelChange}\n quickFilter\n // Newest first is the query's one order.\n sortingMode=\"server\"\n disableColumnSorting\n // A failed run carries what other plugins add under its summary.\n getRowHeight={() => 'auto'}\n // `ListPagination` below pages the query.\n hideFooter\n noRowsLabel=\"No runs match these filters\"\n />\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={runs.length}\n hasMore={hasMore}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n </Stack>\n )}\n </CardDisplay>\n )\n}\nHostRunHistoryCard.displayName = 'HostRunHistoryCard'\n\nexport default HostRunHistoryCard\n"],"names":["actionRunResult","actionRunSummary","actionTriggerLabel","runTriggeredByLabel","useConsoleWidgetSlot","HOST_EVENT_TYPES","CardDisplay","ListFilterChips","ListPagination","ListQueryNotices","listQueryRefusals","ListTable","listFilterGridColumns","useListGridFilter","Chip","Stack","Tooltip","Typography","collection","useMemo","useFirestore","useListQuery","pluginDocsHelp","RUN_HISTORY_HEADERS","RUN_HISTORY_QUERY","runHistoryBase","RESULT_COLOR","succeeded","failed","skipped","RESULT_LABEL","RUN_FILTER_OPTIONS","trigger","map","event","value","label","result","Object","keys","RUN_SELECT_FIELDS","HostRunHistoryCard","props","hostId","orgId","targetId","targetType","targetName","header","help","anchor","excerpt","firestore","RunZone","zoneTarget","type","id","name","gridFilter","selectFields","activity","rows","entries","status","hasMore","page","setPage","pageSize","setPageSize","plan","declaration","request","clauses","search","searchWords","base","deps","idField","filtering","length","runs","entry","$id","String","triggerLabel","who","summary","createdAtMs","createdAt","seconds","refused","fields","headers","options","columns","field","headerName","width","renderCell","row","at","toDate","title","toLocaleString","span","toLocaleTimeString","description","size","variant","color","flex","minWidth","sx","py","durationMs","slot","target","runId","contentGutterX","contentGutterY","contentBordered","spacing","onChange","setClauses","notices","aria-label","filterMode","filterModel","onFilterModelChange","quickFilter","sortingMode","disableColumnSorting","getRowHeight","hideFooter","noRowsLabel","rowCount","onPageChange","onPageSizeChange","displayName"],"mappings":"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"}
@@ -24,13 +24,24 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
24
24
  * arrives, not when the process starts, so a process that never sees an
25
25
  * event pays for this object and nothing else.
26
26
  */ export const workflowsHostEventListener = {
27
- async onEvent (hostId, event, payload) {
28
- const { runEventAutomations } = await import("./engine/run-event-automations.js");
29
- return await runEventAutomations(hostId, event, payload);
27
+ async onEvent (hostId, event, payload, context) {
28
+ const [{ runEventAutomations }, { withRunTriggerActor }] = await Promise.all([
29
+ import("./engine/run-event-automations.js"),
30
+ import("./engine/run-trigger-actor.js")
31
+ ]);
32
+ // Every run this event causes records who caused it (AGL-3376).
33
+ return await withRunTriggerActor(context == null ? void 0 : context.actor, ()=>runEventAutomations(hostId, event, payload));
30
34
  },
31
- async onDispatch (hostId, automationId, event, payload) {
32
- const { runSingleAction } = await import("./engine/run-event-actions.js");
33
- return await runSingleAction(hostId, automationId, event, payload);
35
+ async onDispatch (hostId, automationId, event, payload, context) {
36
+ var _ref;
37
+ const [{ runSingleAction }, { withRunTriggerActor }] = await Promise.all([
38
+ import("./engine/run-event-actions.js"),
39
+ import("./engine/run-trigger-actor.js")
40
+ ]);
41
+ // A page-fired automation is always a visitor on the published page.
42
+ return await withRunTriggerActor((_ref = context == null ? void 0 : context.actor) != null ? _ref : {
43
+ kind: 'visitor'
44
+ }, ()=>runSingleAction(hostId, automationId, event, payload));
34
45
  }
35
46
  };
36
47
  /**
@@ -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 { 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) {\n const { runEventAutomations } = await import('./engine/run-event-automations')\n return await runEventAutomations(hostId, event, payload)\n },\n async onDispatch(hostId, automationId, event, payload) {\n const { runSingleAction } = await import('./engine/run-event-actions')\n return await runSingleAction(hostId, automationId, event, payload)\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 * 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}\n"],"names":["registerHostEventListener","BUNDLE_ID","workflowsHostEventListener","onEvent","hostId","event","payload","runEventAutomations","onDispatch","automationId","runSingleAction","registerWorkflowsServerDeclarations"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,2EAA2E;AAC3E,oEAAoE;AACpE,SAEEA,yBAAyB,QACpB,6CAA4C;AACnD,SAASC,SAAS,QAAQ,+BAA2B;AAErD;;;;;;CAMC,GACD,OAAO,MAAMC,6BAAgD;IAC3D,MAAMC,SAAQC,MAAM,EAAEC,KAAK,EAAEC,OAAO;QAClC,MAAM,EAAEC,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC;QAC7C,OAAO,MAAMA,oBAAoBH,QAAQC,OAAOC;IAClD;IACA,MAAME,YAAWJ,MAAM,EAAEK,YAAY,EAAEJ,KAAK,EAAEC,OAAO;QACnD,MAAM,EAAEI,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC;QACzC,OAAO,MAAMA,gBAAgBN,QAAQK,cAAcJ,OAAOC;IAC5D;AACF,EAAC;AAED;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASK;IACdX,0BAA0BC,WAAWC;AACvC"}
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 { 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 * 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}\n"],"names":["registerHostEventListener","BUNDLE_ID","workflowsHostEventListener","onEvent","hostId","event","payload","context","runEventAutomations","withRunTriggerActor","Promise","all","actor","onDispatch","automationId","runSingleAction","kind","registerWorkflowsServerDeclarations"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,2EAA2E;AAC3E,oEAAoE;AACpE,SAEEA,yBAAyB,QACpB,6CAA4C;AACnD,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;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASW;IACdjB,0BAA0BC,WAAWC;AACvC"}
@@ -38,6 +38,7 @@ import { resumeWorkflowEnrollment, runEventWorkflows } from "./run-event-workflo
38
38
  import { ORG_AUTOMATION_RUN_TARGET, orgAutomationStepRefusal } from "../model/org-automations.js";
39
39
  import { findOrgAutomationsForEvent, resumeOrgAutomationEnrollment } from "./run-org-automations.js";
40
40
  import { isWorkflowActionStep, workflowActionStepRefusal, workflowHasActionSteps, workflowStepTypeLabel } from "./workflow-steps.js";
41
+ import { runTriggeredByFields } from "./run-trigger-actor.js";
41
42
  /** Bounded fan-out per event, mirroring the workflow runner. */ const MAX_TRIGGERED_ACTIONS = 10;
42
43
  /**
43
44
  * The run environment for one automation run: the org's plan gates resolved
@@ -898,7 +899,8 @@ async function datasetAppendRefusal(env, datasetRef) {
898
899
  const summary = stepErrors.length ? `${noun} ran on ${event} with errors: ${stepErrors.join('; ')}`.slice(0, 300) : ending === 'waiting' ? `${noun} is waiting, on ${event}` : `${noun} ran on ${event}`;
899
900
  await hostRef.collection('activity').add(_extends({
900
901
  actorId: null,
901
- actorEmail: null,
902
+ actorEmail: null
903
+ }, runTriggeredByFields(), {
902
904
  // The prose line stays exactly as it was: `activityPrimaryText` and
903
905
  // three other renderers read it, and the run table is not the only
904
906
  // thing this collection feeds.
@@ -1141,7 +1143,8 @@ async function datasetAppendRefusal(env, datasetRef) {
1141
1143
  const reason = named.length ? `Condition on ${named.slice(0, 3).join(', ')} not met` : 'Trigger condition not met';
1142
1144
  await hostRef.collection('activity').add(_extends({
1143
1145
  actorId: null,
1144
- actorEmail: null,
1146
+ actorEmail: null
1147
+ }, runTriggeredByFields(), {
1145
1148
  action: `${kind === 'orgAutomation' ? 'Org automation' : 'Action'} skipped on ${event}`,
1146
1149
  result: 'skipped',
1147
1150
  trigger: event
@@ -1429,7 +1432,8 @@ async function datasetAppendRefusal(env, datasetRef) {
1429
1432
  await endFlowEnrollment(ref);
1430
1433
  await hostRef.collection('activity').add(_extends({
1431
1434
  actorId: null,
1432
- actorEmail: null,
1435
+ actorEmail: null
1436
+ }, runTriggeredByFields(), {
1433
1437
  action: `Flow stopped mid-wait: ${reason}`.slice(0, 300),
1434
1438
  result: 'skipped',
1435
1439
  trigger: enrollment.event